File size: 12,164 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
----- 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 = "<color 75 105 198>[Module]</color> ",
}

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