Skip to main content
Version: bleeding-edge 🩸

Inheriting Classes

How to inherit nanos world Classes. nanos world provides a built-in way of inheriting the built-in Classes

caution

This feature is still experimental, you can try it out and provide feedback before it's full release!

Inheriting a Class​

Inheriting a nanos world Class is really easy, for that you just need to use the Inherit static method on the Class you want to inherit:

-- Creates a new Class called "MyNewClass" inheriting from Prop
-- and stores it in the variable MyNewClass
MyNewClass = Prop.Inherit("MyNewClass")

-- Spawn it using the default constructor
local my_new_class_instance = MyNewClass(Vector(), Rotator(), "nanos-world::SM_Cube")

Multiple Inheritance​

You can also inherit from other inherited classes:

-- Creates a new Class called "MyNewSubClass" inheriting from MyNewClass
MyNewSubClass = MyNewClass.Inherit("MyNewSubClass")

-- Spawn it using the default constructor
local instance = MyNewSubClass(Vector(), Rotator(), "nanos-world::SM_Cube")

Overriding the Constructor​

You can create your own Constructor for your entities, for that you need to define the Constructor method:

-- Defines my constructor with any parameters you desire
function MyNewClass:Constructor(location, rotation)
-- Do any kind of logic here
location = location + Vector(0, 0, 100)

-- Calls Super Constructor to finalize the construction
-- This is the original constructor (in this case from Prop)
-- This is mandatory, if you don't call it, it will throw an error
-- You will only be able to access original and your own class
-- methods after calling it, when the class is completely spawned
self.Super:Constructor(location, rotation, "nanos-world::SM_Cube")

-- Now it's allowed to class methods
self:SetMaterialColorParameter("Tint", Color.RED)
end

-- Spawn it using your custom constructor
local my_new_class_instance = MyNewClass(Vector(123, 456, 100), Rotator())
tip

Inside the constructor, the entity is not fully spawned yet, so you cannot call other entity methods besides self.Super:Constructor. Here you should just use to validate constructor parameters, and use Spawn event to fully setup the entity.

Global Registry​

Through the parent class, we can get a list of all children classes of that class, having a global registry of all existing classes!

local children_classes = ToolGun.GetInheritedClasses()
for _, class in pairs(children_classes) do
-- 'class' is a custom inherited class! we can spawn it
local p = class()
end

Adding new Methods​

Adding new methods for new classes is very straightforward, let's say we want to add a new method for MyNewClass, we just do that:

function MyNewClass:Explode()
-- Spawns a particle
Particle(self:GetLocation(), Rotator(), "nanos-world::P_Explosion")

-- Destroys myself
self:Destroy()
end
tip

Within your methods, you can access the called entity instance with self.

And then you are able to call it as usual:

my_new_class_instance:Explode()

Overriding Existing Methods​

Besides creating new methods, it's possible to override existing ones, for that just redefine them:

function MyNewClass:SetLocation(new_location)
-- Do any kind of logic here
new_location = new_location + Vector(0, 0, 100)

-- Call Super to set the location to the parent Prop
self.Super:SetLocation(new_location)
end

Calling Native Methods​

To call native Class methods, you can use the special variable self.Super, which will allow you accessing the native and original methods directly:

function MyNewClass:GetRotation()
-- Calls original GetRotation and adds 90 to yaw
return self.Super:GetRotation() + Rotator(0, 90, 0)
end

Calling Parent Methods​

Besides calling the original/native method with self.Super, we can also call parent methods if you have nested inheritance.

For that, you must use a special Lua syntax with PARENT_CLASS.METHOD_NAME(self, ...), for example:

tip

In Lua, passing a value as the first parameter to a method while calling it with . will make that value appear as self inside the called method (if the method was defined with :).

-- Inherits Prop
MyNewClass = Prop.Inherit("MyNewClass")

function MyNewClass:SetScale(scale)
-- Does some logic
scale = scale * 2

-- Calls Super (original Prop method)
self.Super:SetScale(scale) * 2
end

-- Inherits MyNewClass
MyNewSubClass = MyNewClass.Inherit("MyNewSubClass")

function MyNewSubClass:SetScale(scale)
-- Does some logic
scale = scale + Vector(2, 2, 2)

-- Calls Parent MyNewClass method with special syntax
MyNewClass.SetScale(self, scale)
end
tip

This same rule applies for calling inherited Constructors!

Overriding __newindex​

It is also possible to add a custom __newindex metamethod on Inherited Classes.

tip

__newindex metamethod is a function which is triggered when you attempt to set a value in an entity. E.g.: my_entity.something = 123.

For that, we just add a custom method called newindex:

note

The method name must be newindex and not __newindex as __newindex is the native method used internally to make the inheritance to work.

function MyNewClass:newindex(key, value)
Console.Log("Setting a %s value: %s = %s", tostring(self), key, tostring(value))
end

An useful way of using __newindex is overriding it to SetValue automatically:

function MyNewClass:newindex(key, value)
self:SetValue(key, value)
end

-- Example usage
local my_entity = MyNewClass()
my_entity.amazing_value = 123

Overriding __index​

tip

__index metamethod is a function which is triggered when you attempt to get a value from an entity. E.g.: local value = my_entity.something.

For that, we just add a custom method called index:

note

The method name must be index and not __index as __index is the native method used internally to make the inheritance to work.

function MyNewClass:index(key)
Console.Log("Getting %s value: %s", tostring(self), key)
-- ... do something
return some_value
end

You can also use __index to return a method:

function MyNewClass:index(key)
Console.Log("%s key not found: %s", tostring(self), key)

-- inside the redirected method you will have all the parameters passed originally
return function(self, param1, param2...)
-- ... do something
return "triggered!"
end

-- or you can even redirect to other member functions
return MyClass.SetLocation
end

local my_entity = MyNewClass()
my_entity:NonExistentMethod(123, "456")

An useful way of using __index is overriding it to GetValue automatically:

function MyNewClass:index(key)
return self:GetValue(key)
end

local my_entity = MyNewClass()
local amazing_value = my_entity.amazing_value

Overriding __tostring​

You can override __tostring as well as usual:

function MyNewClass:__tostring()
return "My Incredible Class!"
end

Native Events​

All events which are triggered on an inherited Class will only trigger in that Class and it's parents, also the parameter passed is the custom entity itself, example:

Prop.Subscribe("Spawn", function(self)
Console.Log("Spawned Prop: %s", tostring(self))
end)

MyNewClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewClass: %s", tostring(self))
end)

local my_entity = MyNewClass()
local my_prop = Prop()
local my_other_entity_inherited_from_prop = MyOtherClass()

-- Will output:
-- Spawned Prop: MyNewClass
-- Spawned MyNewClass: MyNewClass
-- Spawned Prop: Prop
-- Spawned Prop: MyOtherClass

Another way of subscribing is separating the definition and the subscription, this way you don't need the first self parameter anymore:

function MyNewClass:OnSpawn()
-- self is present is this context automatically
Console.Log("Spawned MyNewClass: %s", tostring(self))
end

MyNewClass.Subscribe("Spawn", MyNewClass.OnSpawn)

Multiple Parents Example:​

MyNewClass = Prop.Inherit("MyNewClass")
MyNewSubClass = MyNewClass.Inherit("MyNewSubClass")
MyNewOtherSubClass = MyNewClass.Inherit("MyNewOtherSubClass")

Prop.Subscribe("Spawn", function(self)
Console.Log("Spawned Prop: %s", tostring(self))
end)

MyNewClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewClass: %s", tostring(self))
end)

MyNewSubClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewSubClass: %s", tostring(self))
end)

MyNewOtherSubClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewOtherSubClass: %s", tostring(self))
end)

local my_entity = MyNewSubClass()

-- Will output:
-- Spawned Prop: MyNewClass
-- Spawned MyNewClass: MyNewClass
-- Spawned MyNewSubClass: MyNewClass
tip

Note that Prop and all parent Classes will still trigger events for all child Classes!

Client/Server Synchronization​

If you define your entities on both Client and Server side, they will behave properly and in a synchronized way! Example:

MyNewClass = Prop.Inherit("MyNewClass")

MyNewClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewClass: %s", tostring(self))
end)

local my_entity = MyNewClass()

-- Will output:
-- Spawned MyNewClass: MyNewClass
MyNewClass = Prop.Inherit("MyNewClass")

MyNewClass.Subscribe("Spawn", function(self)
-- It was spawned on server and will spawn on Client as a MyNewClass properly
Console.Log("Spawned MyNewClass: %s", tostring(self))
end)

-- Will output:
-- Spawned MyNewClass: MyNewClass

Custom Remote Events​

It is also possible to trigger custom events on remote instances of your Class, using the methods CallRemoteEvent or BroadcastRemoteEvent, it works like the Events class:

-- inherits the Class
MyNewClass = Prop.Inherit("MyNewClass")

-- defines a custom method
function MyNewClass:OnMyCustomRemoteEvent(a, b)
Console.Log("OnMyCustomRemoteEvent!", tostring(self), a, b)
self:CallRemoteEvent("AnotherRemoteEvent", 456, "def")
end

-- subscribes for a custom remote event
MyNewClass.SubscribeRemote("MyCustomRemoteEvent", MyNewClass.OnMyCustomRemoteEvent)
-- inherits the Class
MyNewClass = Prop.Inherit("MyNewClass")

-- Note that server-side received remote events have the 'player as first parameter
function MyNewClass:OnAnotherRemoteEvent(player, a, b)
Console.Log("OnAnotherRemoteEvent!", tostring(self), tostring(player), a, b)
end

-- subscribes for a custom remote event
MyNewClass.SubscribeRemote("AnotherRemoteEvent", MyNewClass.OnAnotherRemoteEvent)

-- spawns an entity and calls the custom remote event on that entity
local p = MyNewClass(...)
p:BroadcastRemoteEvent("MyCustomRemoteEvent", 123, "abc")

Class Custom Default Values​

It is possible to set a list of default values to the Inherited Class when creating it, just pass it as the 2nd parameter to Inherit:

-- inherits the Class
MyNewClass = Prop.Inherit("MyNewClass", {
name = "My Name",
category = "breakable",
my_custom_param = 123
})

Console.Log(MyNewClass.category)
-- outputs "breakable"

Class Register Event​

When you inherit a new class, the event ClassRegister will be triggered on the parents classes, allowing Packages to know when a new Class is registered.

Prop.Subscribe("ClassRegister", function(class)
-- here we see an useful case for the default values
-- as we can access it here
Console.Log(MyNewClass.name) -- outputs "My Name
-- now we can do something (add to spawn menu?)
end)

-- inherits the Class
MyNewClass = Prop.Inherit("MyNewClass", {
name = "My Name",
category = "breakable",
my_custom_param = 123
})