Arena is a model, CardEnvironment is a ModuleScript with some a list of cards in what section (i.e: the player’s hand, their deck, the discard pile) aand some functions to access them.
Card would be a custom pseudo class thing, containing each card’s properties, references to it’s model and associated gui object, and functions to run when custom events are triggered by metatables.
Now just forgive me because I still don’t get how to make code readable, i added comments though to see if that helps:
local Card = {} -- Metamethods are found at the end before defaultcards; line 184
function Card.new(Properties , player)
local self = setmetatable({} , Card) -- Giving acces to module functions
local newcard = self
self.Name = "Boyparis"
self.Color = 6865280869
self.Image = 6873565000
self.Power = 500
self.Health = 500
self.Counters = 0
self.Slot = "Pending"
do --Read only properties
self.WhiteCost = 10
self.RedCost = 10
self.BlueCost = 10
self.GreenCost = 10
self.YellowCost = 10
self.CardId = math.random(0x000000 , 0xffffff)
self.ArenaEnvironment = "Pending"
end
self.Owner = player or "Pending"
self.ClickConnection = "Pending"
self.Events = {}
do -- Events
self.Events.DamageTaken = {}
self.Events.Destroyed = {}
self.Events.Targeted = {}
self.Events.PowerChanged = {}
self.Events.CountersChanged = {}
self.Events.TurnEnd = {}
self.Events.TurnStart = {}
self.Events.CardDrawn = {}
self.Events.CardDestroyed = {}
self.Events.OnSelfCast = {}
self.Events.OnAllyCast = {}
self.Events.OnEnemyCast = {}
self.Events.OnAnyCast = {}
self.Events.EnemyDestroyed = {}
self.Events.AllyDestroyed = {}
self.Events.Transformed = {}
end
if Properties then
for i , v in pairs(Properties) do
if self[i] then
self[i] = v
end
end
end
self.Model = game.ReplicatedStorage.MysteryOrb:Clone()
self.CardObject = self:RenewGui()
local proxy = setmetatable({realcard = self} , {
__index = function(Tab , key)
if self[key] then
if self[key] == "Pending" then print(key .. " is nil") return nil end --Values aren't set to nil so they remain in table, getting them still returns nil though
return self[key]
end
end,
__newindex = function(Tab , key , val) -- This entire function just calls any functions within the card's events
if val == nil then --Don't want to remove properties from table
val = "Pending"
end
if self[key] then
print("Setting key " .. tostring(key) .. " to " .. tostring(val))
if table.find({"Power", "Health", "WhiteCost", "RedCost", "GreenCost", "BlueCost", "YellowCost", "Counters"} , key) then
local EventTable = nil
local Args = nil
if key == "Power" then
EventTable = self.Events.PowerChanged
Args = {self , val - self[key]}
end
if key == "Health" then
if self[key] < val then
EventTable = self.Events.DamageTaken
Args = {self , val - self[key]}
elseif val <= 0 then
EventTable = self.Events.Destroyed
Args = {self}
end
end
if key == "Counters" then
EventTable = self.Events.CountersChanged
Args = {self , val - self[key]}
end
self[key] = math.clamp(val , 0 , math.huge)
if EventTable then
for i , Effect in pairs(EventTable) do
Effect(Args)
end
end
elseif key == "Slot" then --This only triggers effects and changes the part of the environment the card is in
local EnvModule = require(self.ArenaEnvironment)
local PlayerEnv = EnvModule[self.Owner.UserId].Environment
if PlayerEnv[val] then
PlayerEnv[key][#PlayerEnv[val] + 1] = self
if val == "Field" then
for i , card in pairs(PlayerEnv.Field) do
for i , effect in pairs(card.Events.OnAnyCast) do
effect()
end
for i , effect in pairs(card.Events.OnAllyCast) do
effect()
end
end
for i , card in pairs(EnvModule[EnvModule:GetOppositePlayer(self.Owner).UserId].Environment.Field) do
for i , effect in pairs(card.Events.OnAnyCast) do
effect()
end
for i , effect in pairs(card.Events.OnAllyCast) do
effect()
end
end
end
if val == "Deck" then
self.Cardobject.Parent = nil
end
if val == "Baseplate" then
self.Cardobject.Parent = nil
end
end
elseif key == "ClickConnection" then
if self[key] then self[key]:Disconnect() end
elseif table.find({"Name", "Color", "Image" , "Owner"} , key) then
self[key] = val
end
else
error("Attempt to set invalid key: " .. key)
end
end
})
return proxy
end
function Card:LoadEnvironment(Module)
self.ArenaEnvironment = require(Module)
return self.ArenaEnvironment
end
function Card:SpawnModel(slotGui) --Just some visual effect thing
local effect = game.ReplicatedStorage.CastEffect:Clone() --Spawn effect
effect:SetPrimaryPartCFrame(slotGui.Parent.CFrame - Vector3.new(0,6,0)) --Make it appear in the correct slot
effect.Parent = workspace
local tween = game:GetService("TweenService"):Create(effect.PrimaryPart,TweenInfo.new(0.35),{CFrame = CFrame.new((effect.PrimaryPart.Position + Vector3.new(0,8,0))) * CFrame.Angles(0,math.rad(120),0)}) --Tween
tween:Play()
tween.Completed:Wait()
local model = self.Model
model:SetPrimaryPartCFrame(self.Parent.Parent.CFrame)
model:SetPrimaryPartCFrame(CFrame.new(model.PrimaryPart.Position + Vector3.new(0,6,0)))
model.Parent = self.CardObject
model.Humanoid.Animator:LoadAnimation(model.IdleAnimation):Play()
local tween = game:GetService("TweenService"):Create(effect.PrimaryPart,TweenInfo.new(0.2),{CFrame =(effect.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(240),0))}) --Tween
tween:Play()
tween.Completed:Wait()
local tween = game:GetService("TweenService"):Create(effect.PrimaryPart,TweenInfo.new(0.2),{CFrame = (effect.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(360),0))}) --Tween
tween:Play()
tween.Completed:Wait()
effect:Destroy()
end
function Card:RenewGui() --Copies a pre-made gui from replicatedstorage and adjusts it
local InsertService = game:GetService("InsertService")
local BorderDec = InsertService:LoadAsset(tonumber(self.Color))
local DisplayDec = InsertService:LoadAsset(self.Image)
local card = game.ReplicatedStorage.EmptyCard:Clone()
card.Name = self.Name
card.NameLabel.Text = self.Name
card.Border.Image = BorderDec.Decal.Texture
card.Display.Image = DisplayDec.Decal.Texture
BorderDec:Destroy()
DisplayDec:Destroy()
card.Power.Text = self.Power
card.Health.Text = self.Health
card.ModelVal.Value = self.Model
local distance = 0.13
local colors = {"White","Yellow","Green","Blue","Red",}
for i , color in pairs(colors) do
card.Costs[color].CostLabel.Text = self[color .. "Cost"]
if tonumber(card.Costs[color].CostLabel.Text) <= 0 then
card.Costs[color].Visible = false
else
card.Costs[color].Position = UDim2.new(distance,0,0.91, 0)
distance += 0.075
end
end
return card
end
local function deepCopy(original) -- function to copy tables
local copy = {}
for k, v in pairs(original) do
if type(v) == "table" then
v = deepCopy(v)
end
copy[k] = v
end
return copy
end
local mt = deepCopy(Card) -- create metatable with functions from module
mt.new = nil -- remove .new() function to prevent cyclical table reference, also does not have __index metamethod for the same reason.
Card.__index = mt
Card.DefaultCards = {
Card.new({
["Name"] = "Boyparis",
["Color"] = 6865280869,
["Image"] = 6873565000,
["Power"] = 400,
["Health"] = 5000,
["WhiteCost"] = 10,
["RedCost"] = 3,
["BlueCost"] = 3,
["GreenCost"] = 3,
["YellowCost"] = 3,
["Model"] = 6971743255
}),
Card.new({
["Name"] = "Nil",
["Color"] = 6865283271,
["Image"] = 6865303020,
["Power"] = 50,
["Health"] = 50,
["WhiteCost"] = 1,
["RedCost"] = 1,
["BlueCost"] = 1,
["GreenCost"] = 0,
["YellowCost"] = 0,
["Model"] = 6892715376
}),
Card.new({
["Name"] = "Nil",
["Color"] = 6865283271,
["Image"] = 6865303020,
["Power"] = 50,
["Health"] = 50,
["WhiteCost"] = 1,
["RedCost"] = 1,
["BlueCost"] = 1,
["GreenCost"] = 0,
["YellowCost"] = 0,
["Model"] = 6892715376
}),
Card.new({
["Name"] = "Nil",
["Color"] = 6865281448,
["Image"] = 6865303020,
["Power"] = 50,
["Health"] = 50,
["WhiteCost"] = 1,
["RedCost"] = 1,
["BlueCost"] = 1,
["GreenCost"] = 0,
["YellowCost"] = 0,
["Model"] = 6892715376
}),
Card.new({
["Name"] = "Nil",
["Color"] = 6865282008,
["Image"] = 6865303020,
["Power"] = 50,
["Health"] = 50,
["WhiteCost"] = 1,
["RedCost"] = 1,
["BlueCost"] = 1,
["GreenCost"] = 0,
["YellowCost"] = 0,
["Model"] = 6892715376
}),
Card.new({
["Name"] = "Nil",
["Color"] = 6865280869,
["Image"] = 6865303020,
["Power"] = 50,
["Health"] = 50,
["WhiteCost"] = 1,
["RedCost"] = 1,
["BlueCost"] = 1,
["GreenCost"] = 0,
["YellowCost"] = 0,
["Model"] = 6892715376
}),
Card.new({
["Name"] = "Nil",
["Color"] = 6865282709,
["Image"] = 6865303020,
["Power"] = 50,
["Health"] = 50,
["WhiteCost"] = 1,
["RedCost"] = 1,
["BlueCost"] = 1,
["GreenCost"] = 0,
["YellowCost"] = 0,
["Model"] = 6892715376
}),
}
return Card
That said, i believe my error was that i did not add anything in the newindex metamethod to set the value in the table, because i though that happened automatically.
nope still doesn’t work.