local itemDefaults = {
Name = "Unknown Item",
Quantity = 1,
Rarity = "Common",
}
local categoryDefaults = {
EOD = { BlockAmmo = 1900 },
Weapon = { Damage = 10, Durability = 100 },
}
local ItemMeta = {}
function ItemMeta.__index(self, key)
if ItemMeta[key] then return ItemMeta[key] end
if self.Category and categoryDefaults[self.Category] and
categoryDefaults[self.Category][key] ~= nil then
return categoryDefaults[self.Category][key]
end
if itemDefaults[key] ~= nil then
return itemDefaults[key]
end
end
function ItemMeta.__newindex(self, key, value)
if key == "Quantity" then
assert(type(value) == "number", "Quantity must be numeric") -- explain this, just
like a if statement, if true it goes to next line if false it give the error message and stops the
script , good for finding bugs and stop crashes
if value < 0 then
value = 0
end
end
rawset(self, key, value)
end
--- rawget(t, k) reads the value directly from the table, skipping __index
-- rawset(t, k, v) writes the value directly, skipping __newindex
-- No recursion. No crash. Just clean, predictable behavior
function ItemMeta:__tostring()
return string.format(
"[Item: %s | Qty: %d | Rarity: %s]",
self.Name,
self.Quantity,
self.Rarity
)
end
local function wrapItem(tbl)
return setmetatable(tbl or {}, ItemMeta)
end
local playerDefaults = {
Name = "UnknownPlayer",
Health = 100,
MaxHealth = 100,
Level = 1,
Experience = 0,
Inventory = {},
}
local PlayerData = {}
function PlayerData.__index(self, key)
if PlayerData[key] then return PlayerData[key] end
if playerDefaults[key] ~= nil then return playerDefaults[key] end
end
function PlayerData.__newindex(self, key, value)
if key == "Health" then
assert(type(value) == "number", "Health must be numeric")
if value > self.MaxHealth then value = self.MaxHealth end
if value < 0 then value = 0 end
end
rawset(self, key, value)
end
function PlayerData:__tostring()
return string.format(
"[Player: %s | HP: %d/%d | Lv: %d | Inv: %d items]",
self.Name,
self.Health,
self.MaxHealth,
self.Level,
#self.Inventory
)
end
function PlayerData:AddExperience(amount)
self.Experience += amount
if self.Experience >= 0 then
local TempLevel = math.modf(self.Experience / 100)
self.Level += TempLevel
self.Experience = 0
print(self.Name .. " leveled up to " .. self.Level)
end
end
function PlayerData:TakeDamage(damage)
self.Health -= damage
if self.Health <= 0 then
self.Health = 0
print(self.Name .. " has been defeated!")
end
end
function PlayerData:AddItem(itemData)
table.insert(self.Inventory, wrapItem(itemData))
end
function PlayerData:Snapshot()
local snapshot = {}
for k, v in pairs(self) do
local default = playerDefaults[k]
if type(v) == "table" and k == "Inventory" then
local invSnapshot = {}
for _, item in ipairs(v) do
local itemSnap = {}
for ik, iv in pairs(item) do
local catDef = item.Category and
categoryDefaults[item.Category]
local baseDef = (catDef and catDef[ik]) or itemDefaults[ik]
if iv ~= baseDef then
itemSnap[ik] = iv
end
end
table.insert(invSnapshot, itemSnap)
end
if #invSnapshot > 0 then
snapshot[k] = invSnapshot
end
elseif v ~= default then
snapshot[k] = v
end
end
return snapshot
end
function PlayerData.new(initValues)
local self = initValues or {}
if self.Inventory then
local wrapped = {}
for _, item in ipairs(self.Inventory) do
table.insert(wrapped, wrapItem(item))
end
self.Inventory = wrapped
else
self.Inventory = {}
end
return setmetatable(self, PlayerData)
end
return PlayerData