----- ModulePreset DefineClass.ModulePreset = { __parents = { "ModifiersPreset", }, properties = { { category = "Stack Limit", id = "StackLimit", name = "Stack limit", help = "When the Stack limit count is reached, OnStackLimitReached() is called.", editor = "number", default = 0, no_edit = function (self) return not self.HasStackLimit end, min = 0, }, { category = "Stack Limit", id = "StackLimitCounter", editor = "expression", default = function (self) return self.id end, no_edit = function(self) return not self.OnStackLimitCounterProp or self.StackLimit == 0 end, dont_save = function(self) return not self.OnStackLimitCounterProp or self.StackLimit == 0 end, }, { category = "Stack Limit", id = "OnStackLimitReached", editor = "func", params = "self, owner, ...", no_edit = function(self) return not self.OnStackLimitReachedProp or self.StackLimit == 0 end, dont_save = function(self) return not self.OnStackLimitReachedProp or self.StackLimit == 0 end, }, { category = "Expiration", id = "Expiration", name = "Auto expire", editor = "bool", default = false, no_edit = function(self) return not self.HasExpiration end, dont_save = function(self) return not self.HasExpiration end, }, { category = "Expiration", id = "ExpirationTime", name = "Expiration time", editor = "number", default = 480000, scale = "h", min = 0, no_edit = function(self) return not self.Expiration end, dont_save = function(self) return not self.Expiration end, }, { category = "Expiration", id = "ExpirationRandom", name = "Expiration random", editor = "number", default = 0, scale = "h", min = 0, no_edit = function(self) return not self.Expiration end, dont_save = function(self) return not self.Expiration end, help = "Expiration time + random(Expiration random)" }, { category = "Expiration", id = "OnExpire", editor = "func", params = "self, owner", no_edit = function(self) return not self.Expiration or not self.OnExpireProp end, dont_save = function(self) return not self.Expiration or not self.OnExpireProp end, }, }, StoreAsTable = true, PersistAsReference = true, HasStackLimit = false, OnStackLimitCounterProp = false, OnStackLimitReachedProp = false, HasExpiration = false, OnExpireProp = false, expiration_time = false, } function ModulePreset:PostLoad() self.__index = self ModifiersPreset.PostLoad(self) end function ModulePreset:CanAdd(owner, ...) return self end function ModulePreset:OnAdd(owner, ...) self:ApplyModifiers(owner) end function ModulePreset:OnRemove(owner, ...) self:UnapplyModifiers(owner) end function ModulePreset:OnStackLimitReached(owner, ...) end function ModulePreset:OnExpire(owner) end ----- ModuleDef ModuleDefStackLimits = { { value = -1, text = "Not supported" }, { value = 0, text = "Editable per instance" }, { value = 1, text = "Fixed to 1" }, } DefineClass.ModuleDef = { __parents = { "PresetDef" }, properties = { { category = "Module", id = "DefOwnerMember", name = "Member array in owner", editor = "text", default = "", help = "Where the modules attached to an owner are held."}, { category = "Module", id = "DefAlwaysAddInstances", name = "Always add instances", editor = "bool", default = false, }, { category = "Module", id = "DefStackLimit", name = "Stack limit", editor = "choice", default = -1, items = ModuleDefStackLimits, help = "Is there a limit on the number of modules that can be added to the owner?" }, { category = "Module", id = "DefOnStackLimitCounterProp", name = "OnStackLimitCounter() property", editor = "bool", default = false, no_edit = function(self) return self.DefStackLimit == -1 end, }, { category = "Module", id = "DefOnStackLimitReachedProp", name = "OnStackLimitReached() property", editor = "bool", default = false, no_edit = function(self) return self.DefStackLimit == -1 end, }, { category = "Module", id = "DefHasExpiration", name = "Has expiration", editor = "bool", default = false }, { category = "Module", id = "DefOnExpireProp", name = "OnExpire() property", editor = "bool", default = false, no_edit = function(self) return not self.DefHasExpiration end, }, }, group = "ModuleDefs", DefParentClassList = { "ModulePreset" }, GlobalMap = "ModuleDefs", EditorViewPresetPrefix = "[Module] ", } function ModuleDef:GenerateConsts(code) PresetDef.GenerateConsts(self, code) if self.DefStackLimit == 0 then code:append("\tHasStackLimit = true,\n") end if self.DefStackLimit == 1 then code:append("\tStackLimit = 1,\n") end if self.DefStackLimit ~= -1 and self.DefOnStackLimitCounterProp then code:append("\tOnStackLimitCounterProp = true,\n") end if self.DefStackLimit ~= -1 and self.DefOnStackLimitReachedProp then code:append("\tOnStackLimitReachedProp = true,\n") end if self.DefHasExpiration then code:append("\tHasExpiration = true,\n") if self.DefOnExpireProp then code:append("\tOnExpireProp = true,\n") end end end local function append(pstr, subs, code) code = code:gsub("%$%((.-)%)", subs) pstr:append(code) end function ModuleDef:GetError() local id = self.id if id == id:lower() then return "Id should have at least one capital letter" end if self.DefOwnerMember == "" then return "Member array in owner is required" end if self.DefOwnerMember == id or self.DefOwnerMember == id:lower() then return "Member array should be different than Id and lowercase Id" end end function ModuleDef:GenerateGlobalCode(code) local subs = { class = self.id, var = self.id:lower(), member_array = self.DefOwnerMember, global_map = self.DefGlobalMap, } assert(subs.class ~= subs.var) local reactions for _, parent in ipairs(self.DefParentClassList) do if parent:ends_with("ReactionsPreset") then local class = g_Classes[parent] if class and (class.ReactionTarget or "") ~= "" and (class.ReactionsMember or "") ~= "" then reactions = reactions or {} reactions[#reactions + 1] = class.ReactionsMember end end end subs.reactions_parent = reactions and ', "ReactionObject"' or "" -- owner class definition append(code, subs, [[ ----- $(class)Owner DefineClass.$(class)Owner = { __parents = { "Modifiable"$(reactions_parent) }, $(member_array) = false, can_remove_$(member_array) = true, } local find = table.find local find_value = table.find_value local remove_value = table.remove_value local type = type ]]) append(code, subs, [[ function $(class)Owner:Add$(class)($(var), ...) ]]) if self.DefGlobalMap ~= "" then append(code, subs, [[ if type($(var)) == "string" then $(var) = $(global_map)[$(var)] end ]]) end if self.DefAlwaysAddInstances then append(code, subs, [[ if $(var) and $(var).__index == $(var) then -- $(var) is not an instance $(var) = setmetatable({}, $(var)) -- make an instance end ]]) end append(code, subs, [[ $(var) = $(var):CanAdd(self, ...) if type($(var)) ~= "table" then return end local $(member_array) = self.$(member_array) if not $(member_array) then $(member_array) = {} self.$(member_array) = $(member_array) end ]]) if self.DefStackLimit == 0 then append(code, subs, [[ local limit = $(var).StackLimit if limit > 0 then local counter = $(var):StackLimitCounter() or false assert(type(counter) ~= "number") local count = $(member_array)[counter] or 0 if limit == 1 then -- for a modal $(class) (StackLimit == 1) keep a reference to the $(class) itself if count ~= 0 then return $(var):OnStackLimitReached(self, ...) end $(member_array)[counter] = $(var) else if count >= limit then return $(var):OnStackLimitReached(self, ...) end $(member_array)[counter] = count + 1 end end ]]) elseif self.DefStackLimit == 1 then append(code, subs, [[ local counter = $(var):StackLimitCounter() or false if $(member_array)[counter] then return $(var):OnStackLimitReached(self, ...) end $(member_array)[counter] = $(var) ]]) end if self.DefHasExpiration then append(code, subs, [[ if $(var).Expiration then assert(not rawget($(var), "__index")) -- a $(class) with expiration has to be instanced $(var).expiration_time = GameTime() + $(var).ExpirationTime + self:Random($(var).ExpirationRandom, "Add$(class)") end ]]) end for _, member in ipairs(reactions) do subs.reactions_member = member append(code, subs, [[ self:AddReactions($(var), $(var).$(reactions_member)) ]]) end append(code, subs, [[ $(member_array)[#$(member_array) + 1] = $(var) PostMsg("$(class)Added", self, $(var)) return $(var):OnAdd(self, ...) end ]]) append(code, subs, [[ function $(class)Owner:Remove$(class)($(var), ...) assert(self.can_remove_$(member_array)) ]]) if self.DefGlobalMap ~= "" then append(code, subs, [[ if type($(var)) == "string" then $(var) = $(global_map)[$(var)] end ]]) end append(code, subs, [[ local $(member_array) = self.$(member_array) local n = remove_value($(member_array), $(var)) assert(n) -- removing a $(class) that was not added if not n then return end ]]) if self.DefStackLimit == 0 then append(code, subs, [[ local limit = $(var).StackLimit if limit > 0 then local counter = $(var):StackLimitCounter() or false local count = $(member_array)[counter] or 1 if limit == 1 or count == 1 then $(member_array)[counter] = nil else $(member_array)[counter] = count - 1 end end ]]) elseif self.DefStackLimit == 1 then append(code, subs, [[ local counter = $(var):StackLimitCounter() or false $(member_array)[counter] = nil ]]) end if reactions then append(code, subs, [[ self:RemoveReactions($(var)) ]]) end append(code, subs, [[ PostMsg("$(class)Removed", self, $(var)) return $(var):OnRemove(self, ...) end ]]) if self.DefStackLimit == 0 then append(code, subs, [[ -- A modal $(class) has StackLimit == 1 function $(class)Owner:GetModal$(class)(counter) ]]) elseif self.DefStackLimit == 1 then append(code, subs, [[ function $(class)Owner:Get$(class)(counter) ]]) end if self.DefStackLimit == 0 or self.DefStackLimit == 1 then append(code, subs, [[ local $(member_array) = self.$(member_array) assert(type(counter) ~= "number") local $(var) = $(member_array) and $(member_array)[counter or false] assert(not $(var) or type($(var)) == "table" and IsKindOf($(var), "$(class)")) return $(var) end ]]) end append(code, subs, [[ function $(class)Owner:ForEach$(class)(func, ...) local can_remove = self.can_remove_$(member_array) self.can_remove_$(member_array) = false local res for _, $(var) in ipairs(self.$(member_array)) do res = func($(var), ...) if res then break end end if can_remove then self.can_remove_$(member_array) = nil end return res end ]]) if self.DefStackLimit ~= -1 then append(code, subs, [[ function $(class)Owner:First$(class)ByCounter(counter) local $(member_array) = self.$(member_array) local $(var) = $(member_array) and $(member_array)[counter or false] if type($(var)) == "table" then assert($(var):StackLimitCounter() == counter) return $(var) end ]]) if self.DefStackLimit == 0 then append(code, subs, [[ for _, $(var) in ipairs($(member_array)) do if $(var).StackLimit ~= 1 and $(var):StackLimitCounter() == counter then return $(var) end end ]]) end append(code, subs, [[ end ]]) end if self.DefHasExpiration then append(code, subs, [[ function $(class)Owner:Expire$(class)(time) time = time or GameTime() local $(member_array) = self.$(member_array) local expired for _, $(var) in ipairs($(member_array)) do if ($(var).expiration_time or time) - time < 0 then expired = expired or {} expired[#expired + 1] = $(var) end end for i, $(var) in ipairs(expired) do if i == 1 or find($(member_array), $(var)) then if not $(var):OnExpire(self) then self:Remove$(class)($(var)) end $(var).expiration_time = nil end end end ]]) end append(code, subs, [[ function $(class)Owner:First$(class)ById(id) return find_value(self.$(member_array), "id", id or false) end function $(class)Owner:First$(class)ByGroup(group) return find_value(self.$(member_array), "group", group or false) end ]]) PresetDef.GenerateGlobalCode(self, code) end