- Encapsulation: Keeping an object's details private and only showing what's needed.
- Inheritance: Letting one object share traits or actions from another.
- Polymorphism: Allowing objects to behave differently based on their type.
In this page, we will create a script that allows players to own, upgrade, and use swords to attack others. This system is designed using Object-oriented programming (OOP) 1) principles in Lua, but before starting. we advise you study object-oriented programming if you haven't, as it will be the main focus of the script.
first of all, we must create a table that acts as a blueprint for the swords object, the code should look like this:
local sword = {}
after, we create the constructor2), so we must create a function called sword:new() function creates a new sword with properties like damage and maximum damage. the code would look something like this:
code:
function sword:new(o) o = o or {} setmetatable(o, self) self.__index = self -- Sword attributes self.playerid = o.playerid -- sword owner self.itemid = o.itemid self.dmg = o.dmg -- Current damage self.maxdmg = o.maxdmg -- max damage self.incrementdmg = o.incrementdmg -- dmg increase per upgrade return o end
after that, we must create a function to upgrade the sword and its damage, for this script, we will use this operation:
math.min(self.dmg + (quantity or self.incrementdmg), self.maxdmg) 3)
after, we create a function to update the damage, we will call this function sword:addDmg(), where we will print the old and new damage for debugging purposes
function sword:addDmg(quantity) print("Old damage: " .. self.dmg) self.dmg = math.min(self.dmg + (quantity or self.incrementdmg), self.maxdmg) print("New damage: " .. self.dmg) end
next thing in our list is to create a function to add the damage, we can do this getting the current health of our target and substracting it by the sword's damage, we also must check that the result of this operation is greater than 0, so we use math.max(hp - self.dmg, 0) 4), now, as we want our swords to work for both creatures and players, we will need to make slight adjustments for each case. for creatures, we will use Creature:getAttr() and Creature:setAttr(). For players, we will use Player:getAttr() and Player:setAttr() to get and set their healths respectively, before that, we must check if the hurtactor is a player, if so, we will use the player methods, and if not, the creature ones.
our code should look similar to this:
function sword:hurt(hurtplayerid) if Actor:isPlayer(hurtplayerid) == ErrorCode.OK then local _, hp = Player:getAttr(hurtplayerid, PLAYERATTR.CUR_HP) Player:setAttr(hurtplayerid, PLAYERATTR.CUR_HP, math.max(hp - self.dmg, 0)) else local _, hp = Creature:getAttr(hurtplayerid, CREATUREATTR.CUR_HP) Creature:setAttr(hurtplayerid, CREATUREATTR.CUR_HP, math.max(hp - self.dmg, 0)) end end
With that, we have created a class that can create swords, upgrade them, hurt mobs, etc, but right now, we have no way of managing players and their sword inventories, our goal is that each player should be able to own multiple swords, upgrade them individually, and use them to attack enemies. To achieve our goal, we will create a PlayerObj class that will manage player-specific data, including their sword inventory and attributes, this will make developing and adding new features a whole lot easier.
First, we will Define a playerobj table that acts as the base for all player-related functions:
local playerobj = {}
then we create a function called playerobj:new() that sets up their inventory:
function playerobj:new(o) o = o or {} setmetatable(o, self) self.__index = self self.playerid = o.playerid -- Player ID self.swords = {} -- Sword inventory print("Player object created for " .. self.playerid) return o end
after, we create a function to add a sword to a player's inventory:
function playerobj:addsword(data) print("Adding sword " .. data.id .. " to player " .. self.playerid) self.swords[data.id] = sword:new(data) end
we then create a function to update a player's sword:
function playerobj:swordupgrade(id, customincrement) if self.swords[id] then print("Upgrading sword " .. id .. " for player " .. self.playerid) self.swords[id]:addDmg(customincrement) end end
we then create a function to attack a target:
function playerobj:swordattack(id, hurtplayerid) if self.swords[id] then print("Player " .. self.playerid .. " attacks target " .. hurtplayerid) self.swords[id]:hurt(hurtplayerid) end end
First, we must create a new playerobj for that player, so we can add swords to that player, but first we are going to create a table to store all the players:
local players = {}
then, when any player joins the game, we create an entry in the players table with the same index as their uid. This ensures that each player's data can be easily accessed and managed using their UID as the key and we pass their uid as the playerid.
code should look like this:
ScriptSupportEvent:registerEvent([[Game.AnyPlayer.EnterGame]], function(e) players[e.eventobjid] = playerobj:new({playerid = e.eventobjid}) end)
also, to remove any lag and optimize, when any player leaves, we remove their playerobj this way:
ScriptSupportEvent:registerEvent([[Game.AnyPlayer.LeaveGame]], function(e) players[e.eventobjid] = nil end)
next, we must check when the player picks up any item, to see if that item is a sword, for that, we must create a sword list that contains all the data of a sword, we will create a sword that has the following data as an example sword:
{id = 4097, dmg = 30, maxdmg = 100, incrementdmg = 10}
and we iterate through the swordlist and if the id matches, we add that sword to the player's inventory:
local swordlist = { {id = 4097, dmg = 30, maxdmg = 100, incrementdmg = 10} } ScriptSupportEvent:registerEvent([[Player.PickUpItem]], function(e) local playerid = e.eventobjid local itemid = e.itemid for _, v in pairs(swordlist) do if v.id == itemid then v.playerid = playerid players[playerid]:addsword(v) end end end)
after that, when the player attacks, we must check their current tool and feed it to the function
ScriptSupportEvent:registerEvent([[Player.AttackHit]], function(e) local playerid = e.eventobjid local hurtplayerid = e.toobjid local _, curtoolid = Player:getCurToolID(playerid) players[playerid]:swordattack(curtoolid, hurtplayerid) end)
Now, the system we’ve built works as intended. However, while this is functional, the system currently doesn't handle updating swords (e.g., modifying damage or managing duplicates)
Here's where you step in! As a challenge for you, try modifying the script to add these features:
This system is extremelly flexible thanks to the structure we have created, this makes it so adding new features or extending functionality is straightforward. The sword and playerobj classes make it easy to adapt and expand the system without rewriting existing code.
local swordlist = { {id = 4097, dmg = 30, maxdmg = 100, incrementdmg = 10} --example sword } local sword = {} local playerobj = {} function sword:new(o) o = o or {} setmetatable(o, self) self.__index = self self.playerid = o.playerid -- owner self.itemid = o.itemid self.dmg = o.dmg self.maxdmg = o.maxdmg self.incrementdmg = o.incrementdmg return o end function sword:addDmg(quantity) print("old dmg: " .. self.dmg) self.dmg = math.min(self.dmg + (quantity or self.incrementdmg), self.maxdmg) print(" -> " .. self.dmg) end function sword:hurt(hurtplayerid) if Actor:isPlayer(hurtplayerid) == ErrorCode.OK then local _, hurtplayerhp = Player:getAttr(hurtplayerid, PLAYERATTR.CUR_HP) local newhp = math.max(hurtplayerhp - self.dmg, 0) Player:setAttr(hurtplayerid, PLAYERATTR.CUR_HP, newhp) else local _, hurtplayerhp = Creature:getAttr(hurtplayerid, CREATUREATTR.CUR_HP) local newhp = math.max(hurtplayerhp - self.dmg, 0) Creature:setAttr(hurtplayerid, CREATUREATTR.CUR_HP, newhp) end end function playerobj:new(o) o = o or {} setmetatable(o, self) self.__index = self self.playerid = o.playerid -- owner self.swords = {} print("Sword created for " .. self.playerid) return o end function playerobj:addsword(data) print("adding sword " .. data.id .. " to " .. self.playerid) self.swords[data.id] = sword:new(data) end function playerobj:swordupgrade(id, customincrement) print("increasing damage to " .. self.playerid " sword") self.swords[id]:addDmg(customincrement) end function playerobj:swordattack(id, hurtplayerid) print(self.playerid .. " hurting " .. hurtplayerid) self.swords[id]:hurt(hurtplayerid) end local players = {} ScriptSupportEvent:registerEvent([[Game.AnyPlayer.EnterGame]], function(e) players[e.eventobjid] = playerobj:new({playerid = e.eventobjid}) -- add a playerobject for any new players end) ScriptSupportEvent:registerEvent([[Game.AnyPlayer.LeaveGame]], function(e) players[e.eventobjid] = nil -- delete the playerobject of any player that leaves end) ScriptSupportEvent:registerEvent([[Player.AttackHit]], function(e) local playerid = e['eventobjid'] local hurtplayerid = e['toobjid'] --e['targetactorid'] local _, curtoolid = Player:getCurToolID(playerid) players[playerid]:swordattack(curtoolid, hurtplayerid) end) ScriptSupportEvent:registerEvent([[Player.PickUpItem]], function(e) local playerid = e['eventobjid'] local itemid = e['itemid'] for k,v in pairs(swordlist) do if v.id == itemid then v.playerid = playerid players[playerid]:addsword(v) end end end)