View difference between Paste ID: nXBc4xy5 and FMg9NeDb
SHOW: | | - or go back to the newest paste.
1
-----------------------------------------
2
-- spawn_manager.script
3
--
4
-- Alundaio
5
-----------------------------------------
6
7
8
-- storage for spawned objects
9
local Objects = {}
10
11
-- Spawner Table and Index
12
local SpawnerIndex = {}
13
local Spawner = {}
14
15
local ini = ini_file("spawn_manager\\spawn_manager.ltx")
16
local initialized = false
17
function init()
18
	local SpawnerSections = collect_sections(ini,{"sections"})
19
20
	for k,v in pairs(SpawnerSections) do
21
		table.insert(SpawnerIndex,k)
22
		-- Create a spawn manager for each section
23
		Spawner[k] = SpawnManager(k)
24
	end
25
26
	initialized = true
27
end
28
29
---------------------------------------------------
30
-- class SpawnManager
31
---------------------------------------------------
32
class "SpawnManager"
33
function SpawnManager:__init(section)
34
	self.section = section
35
	self.loaded = false
36
end
37
38
function SpawnManager:initialize()
39
	local section = self.section
40
	-- Inherit data from ini
41
	self.sections = 	str_explode(read_from_ini(	ini, 	section, "sections", 		"string", 	"stalker"), ","		)
42
	self.respawn = 					read_from_ini(	ini, 	section, "respawn", 		"string", 	"true"				)
43
	self.respawn_delta = str_explode(read_from_ini(	ini, 	section, "respawn_delta",	"string", 	"60,120"), "," 		)
44
	self.respawn_radius	=			read_from_ini(  ini,	section, "respawn_radius",	"float",    0					)
45
	self.count = 					read_from_ini(	ini, 	section, "count",			"float",	0					)
46
	self.position =		str_explode(read_from_ini(	ini, 	section, "position",		"string",	"0,0,0"), "," 		)
47
	self.level_vertex_id =			read_from_ini(	ini, 	section, "level_vertex_id",	"float",	0					)
48
	self.game_vertex_id	=			read_from_ini(	ini, 	section, "game_vertex_id",	"float",	0					)
49
50
	-- set respawn timer
51
	self.respawn_delta[1] = tonumber(self.respawn_delta[1])
52
	self.respawn_delta[2] = tonumber(self.respawn_delta[2])
53
54
	self.delta = math.random(self.respawn_delta[1],self.respawn_delta[2])
55
56
	-- create position vector
57
	self.position = vector():set( tonumber(self.position[1]) , tonumber(self.position[2]), tonumber(self.position[3]))
58
59
	-- create table to hold ids of spawned sections
60
	self.ids = {}
61
62
	self.loaded = true
63
end
64
65
function SpawnManager:finalize()
66
	self:clear_dead()
67
end
68
69
function SpawnManager:update(game_time)
70
	-- Check if time to call a respawn
71
72
	if (self.last_respawn_update == nil or game_time:diffSec(self.last_respawn_update) > self.delta) then
73
74
		-- Clear Dead
75
		self:clear_dead()
76
77
		-- Set last update
78
		self.last_respawn_update = game_time
79
80
		-- check if max population reached
81
		if (#self.ids == self.count) then
82
			return
83
		end
84
85
		-- Parse conditions to allow a respawn
86
		local actor = alife():actor()
87
		--if (xr_logic.pick_section_from_condlist(actor, actor, xr_logic.parse_condlist(actor,"","",self.respawn) ) ~= "true") then
88
			--return
89
		--end
90
91
		-- If actor is on same level, then respawn only if actor outside of respawn raidus
92
		if (self.respawn_radius > 0 and game_graph():vertex(self.game_vertex_id):level_id() == game_graph():vertex(actor.m_game_vertex_id):level_id()) then
93
			if (actor.position:distance_to_sqr(self.position) < self.respawn_radius) then
94
				return
95
			end
96
		end
97
98
		-- randomize delta
99
		self.delta = math.random(self.respawn_delta[1],self.respawn_delta[2])
100
101
		-- Spawn section for each available slot
102
		self:spawn()
103
	end
104
end
105
106
function SpawnManager:spawn()
107
	local se_obj
108
	local amt = self.count-#self.ids
109
	for i=1,amt,1 do
110
		-- Create sections
111
		se_obj = alife():create(self.sections[math.random(1, #self.sections)],self.position,self.level_vertex_id,self.game_vertex_id)
112
113
		-- Remember spawned objects by id
114
		table.insert(self.ids, se_obj.id)
115
116
		register_object(se_obj.id)
117
118
		set_object_info(se_obj,"alive",true)
119
	end
120
end
121
122
function SpawnManager:clear_dead()
123
	if (#self.ids < 1) then
124
		return
125
	end
126
127
	local se_obj
128
129
	local _temp = {}
130
	for i=1,#self.ids,1 do
131
		se_obj = alife():object(self.ids[i])
132
133
		-- Check if alive or deleted
134
		if (se_obj == nil or get_object_info(se_obj,"alive") == false) then
135
136
			-- unregister object
137
			unregister_object(self.ids[i])
138
139
		else
140
			table.insert(_temp,self.ids[i])
141
		end
142
		self.ids[i] = nil
143
	end
144
145
	self.ids = nil
146
	self.ids = _temp
147
end
148
149
function SpawnManager:save(packet)
150
	set_save_marker(packet, "save", false, "SpwnManager")
151
	if (self.last_respawn_update) then
152
		utils.w_CTime(packet, self.last_respawn_update)
153
	else
154
		packet:w_u8(0)
155
	end
156
	packet:w_u32(#self.ids)
157
	for i=1,#self.ids,1 do
158
		packet:w_u32(self.ids[i])
159
	end
160
	set_save_marker(packet, "save", true, "SpwnManager")
161
end
162
163
function SpawnManager:load(packet)
164
	set_save_marker(packet, "load", false, "SpwnManager")
165
	self.last_respawn_update = utils.r_CTime(packet)
166
167
	local count = packet:r_u32()
168
	for i=1,count,1 do
169
		self.ids[i] = packet:r_u32()
170
	end
171
	set_save_marker(packet, "load", true, "SpwnManager")
172
end
173
174
function set_object_info(se_obj,info,val)
175
	if (Objects[se_obj.id]) then
176
		--printf("Set[%s] %s = %s",se_obj.id,info,val)
177
		Objects[se_obj.id][info] = val
178
	end
179
end
180
181
function get_object_info(se_obj,info)
182
	if (Objects[se_obj.id]) then
183
		--printf("Get[%s] %s = %s",se_obj.id,info,Objects[se_obj.id][info])
184
		return Objects[se_obj.id][info]
185
	end
186
end
187
188
function register_object(id)
189
	--printf("Object registered %s",se_obj.id)
190-
	if not (Objects[se_obj.id]) then
190+
	if not (Objects[id]) then
191
		Objects[id] = {}
192
	end
193
end
194
195
function unregister_object(id)
196
	--printf("Object unregistered %s",se_obj.id)
197
	Objects[id] = nil
198
end
199
---------------------------------------------------
200
-- Callbacks
201
---------------------------------------------------
202
203
-- Callback actor_binder:net_spawn()
204
function on_game_load()
205
	if not (initialized) then
206
		init()
207
	end
208
end
209
210
-- Callback actor_binder:update()
211
function on_actor_update()
212
	local gt = game.get_game_time()
213
	for i=1,#SpawnerIndex,1 do
214
		if not (Spawner[SpawnerIndex[i]].loaded) then
215
			Spawner[SpawnerIndex[i]]:initialize()
216
		end
217
		Spawner[SpawnerIndex[i]]:update(gt)
218
	end
219
end
220
221
-- Callback actor_binder:save()
222
function on_actor_save(actor,packet)
223
	for i=1,#SpawnerIndex,1 do
224
		Spawner[SpawnerIndex[i]]:finalize()
225
		Spawner[SpawnerIndex[i]]:save(packet)
226
	end
227
end
228
229
-- Callback actor_binder:load()
230
function on_actor_load(actor,reader)
231
	if not (initialized) then
232
		init()
233
	end
234
235
	for i=1,#SpawnerIndex,1 do
236
		Spawner[SpawnerIndex[i]]:initialize()
237
		Spawner[SpawnerIndex[i]]:load(reader)
238
	end
239
	initialized = true
240
end
241
242
-- Callback se_stalker:on_death()       Can be called also in se_monster
243
function on_npc_death(se_obj,who)
244
245
	-- flag object as dead
246
	if (get_object_info(se_obj,"alive")) then
247
		set_object_info(se_obj,"alive",false)
248
	end
249
end
250
251
--------------------------
252
-- Utils
253
--------------------------
254
function collect_sections(ini,sections)
255
	local r,p = {},{}
256
	for k,v in ipairs(sections) do
257
		if ini:section_exist(v) then
258
			local n = ini:line_count(v)
259
			if n > 0 then
260
				for i = 0,n-1 do
261
					local res,id,val = ini:r_line(v,i,"","")
262
					if r[id] == nil then
263
						r[id] = val
264
					end
265
				end
266
			end
267
			p[k] = n
268
		else
269
			p[k] = 0
270
		end
271
	end
272
	return r,p
273
end
274
275
function read_from_ini(ini,section,line,var_type,default)
276
	if not (ini) then
277
		ini = system_ini()
278
	end
279
280
	if (section and line and ini:section_exist(section) and ini:line_exist(section,line)) then
281
		if (var_type == "bool") then
282
			return ini:r_bool(section,line)
283
		elseif (var_type == "string") then
284
			return ini:r_string(section,line)
285
		elseif (var_type == "float") then
286
			return ini:r_float(section,line)
287
		else
288
			return ini:r_string_wq(section,line)
289
		end
290
	else
291
		return default
292
	end
293
end
294
295
function string:split(pat)
296
  local st, g = 1, self:gmatch("()("..pat..")")
297
  local function getter(self, segs, seps, sep, cap1, ...)
298
    st = sep and seps + #sep
299
    return self:sub(segs, (seps or 0) - 1), cap1 or sep, ...
300
  end
301
  local function splitter(self)
302
    if st then return getter(self, st, g()) end
303
  end
304
  return splitter, self
305
end
306
307
function str_explode(str,pattern)
308
	local t = {}
309
	if (type(str) ~= "string") then return end
310
	for word, pat, start in str:split(pattern) do
311
		t[#t+1] = word
312
		if (start and pat == pattern) then
313
		    t[#t+1] = str:sub(start)
314
			break
315
		end
316
	end
317
	return t
318
end
319
320
function printf(txt,...)
321
	if not (text) then return end
322
	local i = 0
323
	local p = {...}
324
	local function sr(a)
325
		i = i + 1
326
		return tostring(p[i])
327
	end
328
	local output = string.gsub(text,"%%s",sr)
329
	get_console():execute("load ~#I#:"..output)
330
	--get_console():execute("flush")
331
end