· 8 years ago · Dec 24, 2017, 07:36 PM
1--[[ General Scripting TIPS
2
3 1. NEVER keep userdata (ie. engine classes) in global scope, instead keep track of game object id, and grab object in the scope you need it in by using level.object_by_id or db.storage table.
4 Otherwise object can't deconstruct and you will end up with instances were script binder exists but object doesn't or level object exists but server object doesn't. This is because variables in lua are all references.
5 Ex. _G.npc = level.object_by_id(2342)
6 alife():release(alife_object(2342))
7 Above, 'npc' will not be nil but se_obj will be! scripts will blow up. Pure Virtual Function calls may occur! Or quite possibly silent errors like for example when player goes to talk to npc game crashes because self.object is nil
8 Such thing is 'okay' in db.storage, but that is because on net_destroy db.storage[id] is set nil. So if you are keeping track of objects directly in a table, instead of ID
9 then you absolutely must rid of every single reference to the userdata so that it can be destroy and garbage collected. Otherwise you end up with undetectable issues.
10--]]
11GAME_VERSION = "1.5r6"
12
13if string.find(command_line(), "-dbg") then
14 DEV_DEBUG = true
15 if string.find(command_line(), "-dbgdez") then -- because users all using dbg, need to hide OP features
16 DEV_DEBUG_DEV = true
17 end
18end
19
20-------------------------------------------------------------------------------------------------
21-- Use marshal library for saving persistent data (like xr_logic pstor)
22-- marshal library can encode tables, functions, strings and numbers to easily allow persistent data storage to file
23-- This is used for db.storage[id].pstor, surge_manager, mines, and coc_treasure_manager.script if enabled
24-- See alife_storage_manager.script for implementation
25require("lua_extensions")
26marshal = require "marshal"
27USE_MARSHAL = marshal ~= nil
28
29----------------------------------------------------------------------
30mus_vol = 0
31amb_vol = 0
32timer_transparent = 0 --маÑкхалат
33----------------------------------------------------------------------
34
35function start_game_callback()
36 printf("Call of Chernobyl version 1.5 r6 %s",GAME_VERSION)
37 if (USE_MARSHAL) then
38 printf("using marshal library")
39 end
40
41 -- Alundaio
42 if (axr_main) then axr_main.on_game_start() end
43 -- End Alundaio
44
45 sim_board.get_sim_board()
46 dialog_manager.fill_phrase_table()
47 pda.add_quick_slot_items_on_game_start()
48end
49
50--local alife_sim
51function alife_object(id)
52 if (id == nil or id >= 65535) then
53 callstack()
54 printf("ALIFE OBJECT ID IS %s!",id)
55 return
56 end
57 -- if not (alife_sim) then
58 -- alife_sim = alife()
59 -- end
60 return alife():object(id)
61end
62-------------------------------------------------------------------------------------------------------
63-- SCRIPTED CALLBACKS
64-------------------------------------------------------------------------------------------------------
65function RegisterScriptCallback(name,func_or_userdata)
66 axr_main.callback_set(name,func_or_userdata)
67end
68
69function UnregisterScriptCallback(name,func_or_userdata)
70 axr_main.callback_unset(name,func_or_userdata)
71end
72
73-- Call this from a script to create a new callback to functions that register for it with RegisterScriptCallback
74-- Every time this function is executed it will callback to all registered members
75-- If axr_main.script has a function by this name, it will automatically trigger it!
76function SendScriptCallback(name,...)
77 --alun_utils.debug_write(strformat("BEFORE SendScriptCallback %s",name))
78 -- callback to all registered functions
79 axr_main.make_callback(name,...)
80 --alun_utils.debug_write(strformat("AFTER SendScriptCallback %s",name))
81 -- check if axr_main has it's own function to execute
82 if (axr_main[name]) then
83 axr_main[name](...)
84 end
85end
86--------------------------------------------
87-- Displays message on middle-top of screen for n amount of milliseconds
88-- Overwritten with each use!
89-- param 1 - Message as string
90-- param 2 - Milliseconds as number
91--------------------------------------------
92function SetHudMsg(msg,n)
93 n = n or 5
94 msg = tostring(msg)
95 local hud = get_hud()
96 if (hud) then
97 hud:AddCustomStatic("not_enough_money_mine", true)
98 hud:GetCustomStatic("not_enough_money_mine"):wnd():TextControl():SetTextST(msg)
99 end
100 bind_stalker_ext.ShowMessageTime = time_global() + n*1000
101end
102
103--------------------------------------------------------------------------------------------
104-- Delayed Event Queue
105--
106-- Events must have a unique id. Such as object id or another identifier unique to the occasion.
107-- Action id must be unique to the specific Event. This allows a single event to have many queued
108-- actions waiting to happen.
109--
110-- Returning true will remove the queued action. Returning false will execute the action continuously.
111-- This allows for events to wait for a specific occurrence, such as triggering after a certain amount of
112-- time only when object is offline
113--
114-- param 1 - Event ID as type<any>
115-- param 2 - Action ID as type<any>
116-- param 3 - Timer in seconds as type<number>
117-- param 4 - Function to execute as type<function>
118-- extra params are passed to executing function as table as param 1
119
120-- see on_game_load or state_mgr_animation.script for example uses
121-- This does not persists through saves! So only use for non-important things.
122-- For example, do not try to destroy npcs unless you do not care that it can fail before player saved then loaded.
123----------------------------------------------------------------------------------------------
124local ev_queue = {}
125function CreateTimeEvent(ev_id,act_id,timer,f,...)
126 if not (ev_queue[ev_id]) then
127 ev_queue[ev_id] = {}
128 ev_queue[ev_id].__size = 0
129 end
130
131 if not (ev_queue[ev_id][act_id]) then
132 ev_queue[ev_id][act_id] = {}
133 ev_queue[ev_id][act_id].timer = time_global() + timer*1000
134 ev_queue[ev_id][act_id].f = f
135 ev_queue[ev_id][act_id].p = {...}
136 ev_queue[ev_id].__size = ev_queue[ev_id].__size + 1
137 end
138end
139
140function RemoveTimeEvent(ev_id,act_id)
141 if (ev_queue[ev_id] and ev_queue[ev_id][act_id]) then
142 ev_queue[ev_id][act_id] = nil
143 ev_queue[ev_id].__size = ev_queue[ev_id].__size - 1
144 end
145end
146
147function ResetTimeEvent(ev_id,act_id,timer)
148 if (ev_queue[ev_id] and ev_queue[ev_id][act_id]) then
149 ev_queue[ev_id][act_id].timer = time_global() + timer*1000
150 end
151end
152
153function ReturnEventTime(ev_id,act_id)
154 if (ev_queue[ev_id] and ev_queue[ev_id][act_id]) and ev_queue[ev_id][act_id].timer then
155 return (ev_queue[ev_id][act_id].timer - time_global())/1000
156 end
157end
158
159function ProcessEventQueue(force)
160 -- if (has_alife_info("sleep_active")) then
161 -- return false
162 -- end
163
164 for event_id,actions in pairs(ev_queue) do
165 for action_id,act in pairs(actions) do
166 --alun_utils.debug_write(strformat("event_queue: event_id=%s action_id=%s",event_id,action_id))
167 if (action_id ~= "__size") then
168 if (force) or (time_global() >= act.timer) then
169 if (act.f(unpack(act.p)) == true) then
170 ev_queue[event_id][action_id] = nil
171 ev_queue[event_id].__size = ev_queue[event_id].__size - 1
172 end
173 end
174 end
175 end
176
177 if (ev_queue[event_id].__size == 0) then
178 ev_queue[event_id] = nil
179 end
180 end
181
182 return false
183end
184function ProcessEventQueueState(m_data,save)
185 if (save) then
186 m_data.event_queue = ev_queue
187 else
188 ev_queue = m_data.event_queue or ev_queue
189 end
190end
191
192function SetSwitchDistance(dist)
193 if (alife()) then
194 local p = net_packet()
195 p:w_begin(18)
196 p:w_float(dist or 2.0)
197 level.send(p,true,true)
198 end
199end
200
201function ChangeLevel(pos,lvid,gvid,angle)
202-- IMPORTANT: You must realize that when you send this event it will happen immediately
203-- if done in lua code it will not execute the rest of the block, level changes immediately happen!
204--[[
205 NET_Packet p;
206 p.w_begin (M_CHANGE_LEVEL); -- M_CHANGE_LEVEL == 13
207 p.w (&m_game_vertex_id,sizeof(m_game_vertex_id));
208 p.w (&m_level_vertex_id,sizeof(m_level_vertex_id));
209 p.w_vec3 (m_position);
210 p.w_vec3 (m_angles);
211 Level().Send(p,net_flags(TRUE));
212--]]
213 -- requires OpenXRay
214 local p = net_packet()
215 p:w_begin(13)
216 p:w_u16(gvid)
217 p:w_u32(lvid)
218 p:w_vec3(pos)
219 p:w_vec3(angle)
220 level.send(p,true)
221end
222
223-- Wrapper for level.add_call
224-- For some reason in 1.6 engine level.remove_call does not work! If you know why, please contact me @alundaio
225local level_add_call_unique = {}
226function AddUniqueCall(functor_a)
227
228 if (level_add_call_unique[functor_a]) then
229 return
230 end
231
232 local function wrapper()
233 if not (level_add_call_unique[functor_a]) then
234 return true
235 end
236
237 if (functor_a()) then
238 level_add_call_unique[functor_a] = nil
239 return true
240 end
241
242 return false
243 end
244
245 level_add_call_unique[functor_a] = true
246
247 level.add_call(wrapper,function() end)
248end
249
250function RemoveUniqueCall(functor_a)
251 level_add_call_unique[functor_a] = nil
252end
253
254function JumpToLevel(new_level)
255 -- requires OpenXray
256 local level_name = level.name()
257 if (level_name == new_level) then
258 return false
259 end
260
261 local cvertex
262 local sim,gg = alife(),game_graph()
263 -- first try to find a smart_terrain on specified level
264 for id,smart in pairs(db.smart_terrain_by_id) do
265 cvertex = smart and gg:vertex(smart.m_game_vertex_id)
266 if (cvertex and sim:level_name(cvertex:level_id()) == new_level) then
267 ChangeLevel(cvertex:level_point(),cvertex:level_vertex_id(),smart.m_game_vertex_id,VEC_ZERO)
268 return true
269 end
270 end
271
272 -- in case level has no smarts then just teleport to first found gvid for level
273 for gvid=0, 5332 do
274 if gg:valid_vertex_id(gvid) then
275 cvertex = gg:vertex(gvid)
276 lvl = sim:level_name(cvertex:level_id())
277 if (lvl == new_level) then
278 ChangeLevel(cvertex:level_point(),cvertex:level_vertex_id(),gvid,VEC_ZERO)
279 return true
280 end
281 else
282 break
283 end
284 end
285 return false
286end
287
288function TeleportObject(id,pos,lvid,gvid)
289 -- Requires OpenXray
290 if (db.offline_objects[id]) then
291 db.offline_objects[id].level_vertex_id = nil
292 end
293 db.spawned_vertex_by_id[id] = nil
294 alife():teleport_object(id,gvid,lvid,pos)
295end
296
297function TeleportSquad(squad,pos,lvid,gvid)
298 -- Requires OpenXray
299 local sim = alife()
300 sim:teleport_object(squad.id,gvid,lvid,pos)
301 for k in squad:squad_members() do
302 if (db.offline_objects[k.id]) then
303 db.offline_objects[k.id].level_vertex_id = nil
304 end
305 db.spawned_vertex_by_id[k.id] = nil
306 sim:teleport_object(k.id,gvid,lvid,pos)
307 end
308end
309
310function IsAzazelMode()
311 return axr_main.config and axr_main.config:r_value("character_creation","new_game_azazel_mode",1) == true or alife_storage_manager.get_state().enable_azazel_mode == true
312end
313
314function IsHardcoreMode()
315 return axr_main.config and axr_main.config:r_value("character_creation","new_game_hardcore_mode",1) == true or alife_storage_manager.get_state().uuid ~= nil
316end
317
318function IsStoryMode()
319 return axr_main.config and axr_main.config:r_value("character_creation","new_game_story_mode",1) == true or not has_alife_info("story_mode_disabled")
320end
321
322function IsSurvivalMode()
323 return axr_main.config and axr_main.config:r_value("character_creation","new_game_survival_mode",1) == true or alife_storage_manager.get_state().enable_survival_mode == true
324end
325
326--------------------------------------------------------------------
327-- Serialization of userdata for Marshal Library
328--------------------------------------------------------------------
329if (marshal) then
330 function game_CTime___persist(self)
331 local Y, M, D, h, m, s, ms = 0,0,0,0,0,0,0
332 if (self and self.get) then
333 Y, M, D, h, m, s, ms = self:get(Y, M, D, h, m, s, ms)
334 end
335 return function ()
336 local t = game.CTime()
337 t:set(Y, M, D, h, m, s, ms)
338 return t
339 end
340 end
341 getmetatable(game.CTime()).__persist = game_CTime___persist
342end
343
344-- debug to find objects that shouldn't be calling game_object:alive()
345--[[
346game_object.alive = function(self)
347 callstack()
348 printf("alive %s",self:name())
349 local se_obj = alife_object(self:id())
350 return se_obj:alive()
351end
352--]]
353--------------------------------------------------------------------
354function is_empty(t)
355 if not (t) then
356 return true
357 end
358 for i,j in pairs(t) do
359 return false
360 end
361 return true
362end
363
364function strformat(text,...)
365 if not (text) then return end
366 local i = 0
367 local p = {...}
368 local function sr(a)
369 i = i + 1
370 if (type(p[1]) == "userdata") then
371 return "userdata"
372 end
373 return tostring(p[i])
374 end
375 -- so that it doesn't return gsub's multiple returns
376 local s = string.gsub(text,"%%s",sr)
377 return s
378end
379
380-- Used by modules.script for generic module management
381schemes = {}
382schemes_by_stype = {}
383function LoadScheme(filename, scheme, ...)
384 if not (_G[filename]) then
385 printf("ERROR: Trying to load scheme that does not exist! %s",filename)
386 return
387 end
388 schemes[scheme] = filename
389 local p = {...}
390 for i=1,#p do
391 if not (schemes_by_stype[p[i]]) then
392 schemes_by_stype[p[i]] = {}
393 end
394 schemes_by_stype[p[i]][scheme] = true
395 end
396end
397
398function printf(fmt,...)
399 if not (fmt) then return end
400 local fmt = tostring(fmt)
401
402 if (select('#',...) >= 1) then
403 local i = 0
404 local p = {...}
405 local function sr(a)
406 i = i + 1
407 if (type(p[i]) == 'userdata') then
408 if (p[i].x and p[i].y) then
409 return vec_to_str(p[i])
410 end
411 return 'userdata'
412 end
413 return tostring(p[i])
414 end
415 fmt = string.gsub(fmt,"%%s",sr)
416 end
417 if (log) then
418 log(fmt)
419 --get_console():execute("flush")
420 else
421 get_console():execute("load ~#debug msg:"..fmt)
422 end
423end
424
425function spairs(t, order)
426 -- collect the keys
427 local keys = {}
428 for k in pairs(t) do keys[#keys+1] = k end
429
430 -- if order function given, sort by it by passing the table and keys a, b,
431 -- otherwise just sort the keys
432 if order then
433 table.sort(keys, function(a,b) return order(t, a, b) end)
434 else
435 table.sort(keys)
436 end
437
438 -- return the iterator function
439 local i = 0
440 return function()
441 i = i + 1
442 if keys[i] then
443 return keys[i], t[keys[i]]
444 end
445 end
446end
447----------------------------------------------------------------------
448function time_global()
449 return device():time_global()
450end
451
452--[[ does not work?
453function wait_game(time_to_wait)
454 verify_if_thread_is_running()
455 if (time_to_wait == nil) then
456 coroutine.yield()
457 else
458 local time_to_stop = game.time() + time_to_wait
459 while game.time() <= time_to_stop do
460 coroutine.yield()
461 end
462 end
463end
464
465function wait(time_to_wait)
466 verify_if_thread_is_running()
467 if (time_to_wait == nil) then
468 coroutine.yield()
469 else
470 local time_to_stop = time_global() + time_to_wait
471 while time_global() <= time_to_stop do
472 coroutine.yield()
473 end
474 end
475end
476--]]
477
478function action(obj,...)
479 local arg = {...}
480 local e_act = entity_action()
481 for i=1,#arg do
482 e_act:set_action(arg[i])
483 end
484 if (obj ~= nil) then
485 obj:command(e_act,false)
486 end
487 return entity_action(e_act)
488end
489
490function action_first(obj,...)
491 local arg = {...}
492 local e_act = entity_action()
493 for i=1,#arg do
494 e_act:set_action(arg[i])
495 end
496 if (obj ~= nil) then
497 obj:command(e_act,true)
498 end
499 return entity_action(e_act)
500end
501
502function round (value)
503 local min = math.floor (value)
504 local max = min + 1
505 if value - min > max - value then return max end
506 return min
507end
508
509function distance_between(obj1, obj2)
510 return obj1:position():distance_to(obj2:position())
511end
512
513-- +Ñли один объект nil, например нет актера, то Ñчитаем, что он далеко
514function distance_between_safe(obj1, obj2)
515 if(obj1 == nil or obj2 == nil) then return 100000 end
516 return obj1:position():distance_to(obj2:position())
517end
518
519--' іроверка на инфопоршнv, даже еÑли игрока не Ñу еÑтвует
520function has_alife_info(info_id)
521 local sim = alife()
522 return sim:has_info(0, info_id)
523end
524
525function reset_action (npc, script_name)
526 if npc:get_script () then
527 npc:script (false, script_name)
528 end
529 npc:script (true, script_name)
530end
531
532--------------------------------------------------
533-- Functions and variables added by Zmey
534--------------------------------------------------
535
536-- іонÑтанта, которуі иÑпользовать в меÑтах, где нужно задать неограниченное Ð²Ñ€ÐµÐ¼Ñ Ð´ÐµÐ¹ÑтвиÑ
537time_infinite = 100000000
538
539-- +Ñли в даннvй момент вvполнÑетÑÑ ÐºÐ°ÐºÐ¾Ðµ-то дейÑтвие, прерvвает его и отклічает Ñкриптовvй режим
540function interrupt_action(who, script_name)
541 if who:get_script() then
542 who:script(false, script_name)
543 end
544end
545
546function random_choice(...)
547 local arg = {...}
548 if (#arg > 0) then
549 local r = math.random(1, #arg)
550 return arg[r]
551 end
552end
553
554function random_number (min_value, max_value)
555 if min_value == nil and max_value == nil then
556 return math.random ()
557 else
558 return math.random (min_value, max_value)
559 end
560end
561
562function parse_names( s )
563 local t = {}
564 --for name in string.gmatch( s, "([%w_\\]+)%p*" ) do
565 for name in string.gmatch( s, "([%w_%-.\\]+)%p*" ) do
566 t[#t+1] = name
567 end
568 return t
569end
570
571function parse_key_value( s )
572 local t = {}
573 if s == nil then
574 return nil
575 end
576 local key, nam = nil, nil
577 for name in string.gmatch( s, "([%w_\\]+)%p*" ) do
578 if key == nil then
579 key = name
580 else
581 t[key] = name
582 key = nil
583 end
584 end
585 return t
586end
587
588function parse_nums( s )
589 local t = {}
590 for entry in string.gmatch( s, "([%-%d%.]+)%,*" ) do
591 t[#t+1] = tonumber(entry)
592 end
593 return t
594end
595
596function get_clsid(obj)
597 if not (obj) then
598 callstack()
599 printf("ERROR: get_clsid - obj is nil!")
600 return
601 end
602 if not (obj.clsid) then
603 callstack()
604 printf("ERROR: no clsid method for %s",obj:name())
605 return
606 end
607 return obj:clsid()
608end
609
610--ВычиÑлÑет yaw в радианах
611function yaw( v1, v2 )
612 return math.acos( ( (v1.x*v2.x) + (v1.z*v2.z ) ) / ( math.sqrt(v1.x*v1.x + v1.z*v1.z ) * math.sqrt(v2.x*v2.x + v2.z*v2.z ) ) )
613end
614function yaw_degree( v1, v2 )
615 return (math.acos( ( (v1.x*v2.x) + (v1.z*v2.z ) ) / ( math.sqrt(v1.x*v1.x + v1.z*v1.z ) * math.sqrt(v2.x*v2.x + v2.z*v2.z ) ) ) * 57.2957)
616end
617function yaw_degree3d( v1, v2 )
618 return (math.acos((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/(math.sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z )*math.sqrt(v2.x*v2.x + v2.y*v2.y + v2.z*v2.z)))*57.2957)
619end
620function vector_cross(v1, v2)
621 return vector():set(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x)
622end
623
624--Поворачивает вектор вокруг оÑи y против чаÑовой Ñтрелки.
625function vector_rotate_y(v, angle)
626 angle = angle * 0.017453292519943295769236907684886
627 local c = math.cos (angle)
628 local s = math.sin (angle)
629 return vector ():set (v.x * c - v.z * s, v.y, v.x * s + v.z * c)
630end
631
632-- очиÑтка таблицы.
633function iempty_table (t)
634 if not (t) then
635 return {}
636 end
637 while #t > 0 do
638 table.remove(t)
639 end
640 return t
641end
642
643function empty_table(t)
644 if not (t) then
645 return {}
646 end
647 for k,v in pairs(t) do
648 t[k] = nil
649 end
650 return t
651end
652
653function stop_play_sound(obj)
654 if (IsStalker(obj) and not obj:alive()) then
655 return
656 end
657 obj:set_sound_mask(-1)
658 obj:set_sound_mask(0)
659end
660
661-- Печатает таблицу как дерево.
662function print_table(table, subs)
663 --[[
664 local sub
665 if subs ~= nil then
666 sub = subs
667 else
668 sub = ""
669 end
670 for k,v in pairs(table) do
671 if type(v) == "table" then
672 print_table(v, sub.."["..k.."]----->")
673 elseif type(v) == "function" then
674 printf(sub.."%s = function",k)
675 elseif type(v) == "userdata" then
676 if (v.x) then
677 printf(sub.."%s = %s",k,alun_utils.vector_to_string(v))
678 else
679 printf(sub.."%s = userdata", k)
680 end
681 elseif type(v) == "boolean" then
682 if v == true then
683 if(type(k)~="userdata") then
684 printf(sub.."%s = true",k)
685 else
686 printf(sub.."userdata = true")
687 end
688 else
689 if(type(k)~="userdata") then
690 printf(sub.."%s = false", k)
691 else
692 printf(sub.."userdata = false")
693 end
694 end
695 else
696 if v ~= nil then
697 printf(sub.."%s = %s", k,v)
698 else
699 printf(sub.."%s = nil", k,v)
700 end
701 end
702 end
703 --]]
704end
705function store_table(table, subs)
706 local sub
707 if subs ~= nil then
708 sub = subs
709 else
710 sub = ""
711 end
712 printf(sub.."{")
713 for k,v in pairs(table) do
714 if type(v) == "table" then
715 printf(sub.."%s = ", tostring(k))
716 store_table(v, sub.." ")
717 elseif type(v) == "function" then
718 printf(sub.."%s = \"func\",", tostring(k))
719 elseif type(v) == "userdata" then
720 printf(sub.."%s = \"userdata\",", tostring(k))
721 elseif type(v) == "string" then
722 printf(sub.."%s = \"%s\",", tostring(k), tostring(v))
723 else
724 printf(sub.."%s = %s,", tostring(k), tostring(v))
725 end
726 end
727 printf(sub.."},")
728end
729----------------------------------------
730function IsWounded(o)
731 if not (o:clsid() == clsid.script_stalker and o:alive()) then
732 return false
733 end
734
735 if (o:critically_wounded() or o:in_smart_cover()) then
736 return false
737 end
738
739 if o:best_enemy() and utils.load_var(o, "wounded_fight") == "true" then
740 return false
741 end
742
743 local state = tostring(utils.load_var(o, "wounded_state"))
744 if (state == "nil") then
745 return false
746 end
747
748 return true
749end
750-------------------------------------------------------------------------------------------
751-- CLASS TESTING
752-------------------------------------------------------------------------------------------
753local monster_classes
754local weapon_classes
755local artefact_classes
756local anomaly_classes
757
758function IsOutfit(o,c)
759 if not c then
760 c = o and o:clsid()
761 end
762 return c and (c == clsid.equ_stalker_s or c == clsid.equ_stalker)
763end
764
765function IsHeadgear(o,c)
766 if not c then
767 c = o and o:clsid()
768 end
769 return c and (c == clsid.equ_helmet_s or c == clsid.helmet)
770end
771
772function IsExplosive(o,c)
773 if not c then
774 c = o and o:clsid()
775 end
776 return c and (c == clsid.obj_explosive_s or c == clsid.obj_explosive)
777end
778
779function IsPistol(o,c)
780 if not (c) then
781 c = o and o:clsid()
782 end
783 local pistol = {
784 [clsid.wpn_pm_s] = true,
785 [clsid.wpn_walther_s] = true,
786 [clsid.wpn_usp45_s] = true,
787 [clsid.wpn_hpsa_s] = true,
788 [clsid.wpn_lr300_s] = true,
789 [clsid.wpn_pm] = true,
790 [clsid.wpn_walther] = true,
791 [clsid.wpn_usp45] = true,
792 [clsid.wpn_hpsa] = true,
793 [clsid.wpn_lr300] = true
794 }
795 return c and pistol[c] or false
796end
797
798function IsSniper(o,c)
799 if not (c) then
800 c = o and o:clsid()
801 end
802 local sniper = {
803 [clsid.wpn_svu_s] = true,
804 [clsid.wpn_svd_s] = true,
805 [clsid.wpn_vintorez_s] = true,
806 [clsid.wpn_svu] = true,
807 [clsid.wpn_svd] = true,
808 [clsid.wpn_vintorez] = true
809 }
810 return c and sniper[c] or false
811end
812
813function IsLauncher(o,c)
814 if not (c) then
815 c = o and o:clsid()
816 end
817 local launcher = {
818 [clsid.wpn_rg6_s] = true,
819 [clsid.wpn_rpg7_s] = true,
820 [clsid.wpn_rg6] = true,
821 [clsid.wpn_rpg7] = true
822 }
823 return c and launcher[c] or false
824end
825
826function IsShotgun(o,c)
827 if not (c) then
828 c = o and o:clsid()
829 end
830 local shotgun = {
831 [clsid.wpn_bm16_s] = true,
832 [clsid.wpn_shotgun_s] = true,
833 [clsid.wpn_auto_shotgun_s] = true,
834 [clsid.wpn_bm16] = true,
835 [clsid.wpn_shotgun] = true
836 --[clsid.wpn_auto_shotgun] = true
837 }
838 return c and shotgun[c] or false
839end
840
841function IsRifle(o,c)
842 if not (c) then
843 c = o and o:clsid()
844 end
845 local rifle = {
846 [clsid.wpn_ak74_s] = true,
847 [clsid.wpn_groza_s] = true,
848 [clsid.wpn_val_s] = true,
849 [clsid.wpn_ak74] = true,
850 [clsid.wpn_groza] = true,
851 [clsid.wpn_val] = true
852 }
853 return c and rifle[c] or false
854end
855
856function IsMonster(o,c)
857 if not (c) then
858 c = o and o:clsid()
859 end
860 if not (monster_classes) then
861 monster_classes = {
862 [clsid.bloodsucker_s] = true,
863 [clsid.boar_s] = true,
864 [clsid.burer_s] = true,
865 [clsid.cat_s] = true,
866 [clsid.chimera_s] = true,
867 [clsid.controller_s] = true,
868 [clsid.dog_s] = true,
869 [clsid.flesh_s] = true,
870 [clsid.fracture_s] = true,
871 [clsid.gigant_s] = true,
872 [clsid.karlik_s] = true,
873 [clsid.poltergeist_s] = true,
874 [clsid.pseudodog_s] = true,
875 [clsid.psy_dog_phantom_s] = true,
876 [clsid.psy_dog_s] = true,
877 [clsid.rat] = true,
878 [clsid.rat_s] = true,
879 [clsid.snork_s] = true,
880 [clsid.tushkano_s] = true,
881 [clsid.zombie_s] = true,
882 [clsid.medwed_s] = true,
883 [clsid.polterbuild_s] = true
884 }
885 end
886 return c and monster_classes[c] or false
887end
888
889function IsAnomaly(o,c)
890 if not (c) then
891 c = o and o:clsid()
892 end
893 if not (anomaly_classes) then
894 anomaly_classes = {
895 [clsid.zone] = true,
896 [clsid.zone_acid_fog] = true,
897 [clsid.zone_bfuzz] = true,
898 [clsid.zone_campfire] = true,
899 [clsid.zone_dead] = true,
900 [clsid.zone_galantine] = true,
901 [clsid.zone_mincer] = true,
902 [clsid.zone_mosquito_bald] = true,
903 [clsid.zone_radioactive] = true,
904 [clsid.zone_rusty_hair] = true,
905 [clsid.zone_bfuzz_s] = true,
906 [clsid.zone_mbald_s] = true,
907 [clsid.zone_galant_s] = true,
908 [clsid.zone_mincer_s] = true,
909 [clsid.zone_radio_s] = true,
910 [clsid.zone_torrid_s] = true,
911 [clsid.zone_nograv_s] = true,
912 }
913 end
914 return c and anomaly_classes[c] or false
915end
916
917function isLc(obj)
918 return (obj:clsid() == clsid.level_changer)
919end
920
921function IsStalker(o,c)
922 if not (c) then
923 c = o and o:clsid()
924 end
925 return c and (c == clsid.script_stalker or c == clsid.script_actor) or false
926end
927
928function IsTrader(o,c)
929 if not (c) then
930 c = o and o:clsid()
931 end
932 return c and (c == clsid.script_trader) or false
933end
934
935function IsHelicopter(o,c)
936 if not (c) then
937 c = o and o:clsid()
938 end
939 return c and (c == clsid.helicopter or c == clsid.car or c == clsid.script_heli) or false
940end
941
942function IsWeapon(o,c)
943 if not (c) then
944 c = o and o:clsid()
945 end
946 if not (weapon_classes) then
947 weapon_classes = {
948 [clsid.wpn_vintorez_s] = true,
949 [clsid.wpn_ak74_s] = true,
950 [clsid.wpn_lr300_s] = true,
951 [clsid.wpn_hpsa_s] = true,
952 [clsid.wpn_pm_s] = true,
953 [clsid.wpn_shotgun_s] = true,
954 [clsid.wpn_auto_shotgun_s] = true,
955 [clsid.wpn_bm16_s] = true,
956 [clsid.wpn_svd_s] = true,
957 [clsid.wpn_svu_s] = true,
958 [clsid.wpn_rg6_s] = true,
959 [clsid.wpn_rpg7_s] = true,
960 [clsid.wpn_val_s] = true,
961 [clsid.wpn_walther_s] = true,
962 [clsid.wpn_usp45_s] = true,
963 [clsid.wpn_groza_s] = true,
964 [clsid.wpn_knife_s] = true,
965 [clsid.wpn_vintorez] = true,
966 [clsid.wpn_ak74] = true,
967 [clsid.wpn_lr300] = true,
968 [clsid.wpn_hpsa] = true,
969 [clsid.wpn_pm] = true,
970 [clsid.wpn_shotgun] = true,
971 --[clsid.wpn_auto_shotgun] = true,
972 [clsid.wpn_bm16] = true,
973 [clsid.wpn_svd] = true,
974 [clsid.wpn_svu] = true,
975 [clsid.wpn_rg6] = true,
976 [clsid.wpn_rpg7] = true,
977 [clsid.wpn_val] = true,
978 [clsid.wpn_walther] = true,
979 [clsid.wpn_usp45] = true,
980 [clsid.wpn_groza] = true,
981 [clsid.wpn_knife] = true
982 }
983 end
984 return c and weapon_classes[c] or false
985end
986
987function IsAmmo(o,c)
988 if not (c) then
989 c = o and o:clsid()
990 end
991 return c and (c == clsid.wpn_ammo or c == clsid.wpn_ammo_s)
992end
993
994function IsGrenade(o,c)
995 if not (c) then
996 c = o and o:clsid()
997 end
998 if not (grenade_classes) then
999 grenade_classes = {
1000 [clsid.wpn_grenade_f1_s] = true,
1001 [clsid.wpn_grenade_rgd5_s] = true,
1002 [clsid.wpn_grenade_launcher_s] = true,
1003 [clsid.wpn_grenade_fake] = true,
1004 [clsid.wpn_grenade_f1] = true,
1005 [clsid.wpn_grenade_launcher] = true,
1006 [clsid.wpn_grenade_rgd5] = true,
1007 [clsid.wpn_grenade_rpg7] = true
1008 }
1009 end
1010 return c and grenade_classes[c] or false
1011end
1012
1013function IsArtefact(o,c)
1014 if not (c) then
1015 c = o and o:clsid()
1016 end
1017 if not (artefact_classes) then
1018 artefact_classes = {
1019 [clsid.art_bast_artefact] = true,
1020 [clsid.art_black_drops] = true,
1021 [clsid.art_dummy] = true,
1022 [clsid.art_electric_ball] = true,
1023 [clsid.art_faded_ball] = true,
1024 [clsid.art_galantine] = true,
1025 [clsid.art_gravi] = true,
1026 [clsid.art_gravi_black] = true,
1027 [clsid.art_mercury_ball] = true,
1028 [clsid.art_needles] = true,
1029 [clsid.art_rusty_hair] = true,
1030 [clsid.art_thorn] = true,
1031 [clsid.art_zuda] = true,
1032 [clsid.artefact] = true,
1033 [clsid.artefact_s] = true
1034 }
1035 end
1036 return c and artefact_classes[c] or false
1037end
1038
1039function IsInvbox(o,c)
1040 if not (c) then
1041 c = o and o:clsid()
1042 end
1043 return c and (c == clsid.inventory_box_s or c == clsid.inventory_box)
1044end
1045-------------------------------------------------------------
1046-- SQUAD BEHAVIOR TESTING
1047-------------------------------------------------------------
1048is_squad_monster = {
1049 ["monster_predatory_day"] = true,
1050 ["monster_predatory_night"] = true,
1051 ["monster_vegetarian"] = true,
1052 ["monster_zombied_day"] = true,
1053 ["monster_zombied_night"] = true,
1054 ["monster_special_day"] = true,
1055 ["monster_special_night"] = true,
1056 ["monster"] = true,
1057 ["zoo_monster"] = true
1058}
1059squad_community_by_behaviour = {
1060 ["stalker"] = "stalker",
1061 ["bandit"] = "bandit",
1062 ["renegade"] = "renegade",
1063 ["csky"] = "csky",
1064 ["dolg"] = "dolg",
1065 ["freedom"] = "freedom",
1066 ["army"] = "army",
1067 ["ecolog"] = "ecolog",
1068 ["killer"] = "killer",
1069 ["zombied"] = "zombied",
1070 ["monolith"] = "monolith",
1071 ["monster"] = "monster",
1072 ["monster_predatory_day"] = "monster",
1073 ["monster_predatory_night"] = "monster",
1074 ["monster_vegetarian"] = "monster",
1075 ["monster_zombied_day"] = "monster",
1076 ["monster_zombied_night"] = "monster",
1077 ["monster_special_day"] = "monster",
1078 ["monster_special_night"] = "monster",
1079 ["zoo_monster"] = "monster"
1080}
1081-------------------------------------------------------------------------------------------
1082function get_object_community(obj)
1083 if type(obj.id) == "function" then
1084 return character_community(obj)
1085 else
1086 return alife_character_community(obj)
1087 end
1088end
1089
1090function character_community (obj)
1091 if not (obj) then
1092 return
1093 end
1094 if IsStalker(obj) then
1095 return obj:character_community()
1096 end
1097 return "monster"
1098end
1099
1100function alife_character_community (obj)
1101 if not (obj) then
1102 return
1103 end
1104 if IsStalker(obj, obj:clsid()) then
1105 return obj:community()
1106 end
1107 return "monster"
1108end
1109
1110-- получить геймобжект по Ñтори_айди.
1111function level_object_by_sid( sid )
1112 local sim = alife()
1113 if sim then
1114 local se_obj = sim:story_object( sid )
1115 if se_obj then
1116 return level.object_by_id( se_obj.id )
1117 end
1118 end
1119 return nil
1120end
1121-- Получить айдишник обьекта по Ñтори айди.
1122function id_by_sid( sid )
1123 local sim = alife()
1124 if sim then
1125 local se_obj = sim:story_object( sid )
1126 if se_obj then
1127 return se_obj.id
1128 end
1129 end
1130 return nil
1131end
1132
1133function abort(msg, ...)
1134 if not (msg) then return end
1135 local fmt = tostring(msg)
1136
1137 if (select('#',...) >= 1) then
1138 local i = 0
1139 local p = {...}
1140 local function sr(a)
1141 i = i + 1
1142 if (type(p[i]) == 'userdata') then
1143 return 'userdata'
1144 end
1145 return tostring(p[i])
1146 end
1147 fmt = string.gsub(fmt,"%%s",sr)
1148 end
1149 callstack()
1150 log(fmt)
1151 --[[
1152 error(fmt, 2)
1153 --]]
1154end
1155
1156function set_inactivate_input_time(delta)
1157 db.storage[db.actor:id()].disable_input_time = game.get_game_time()
1158 db.storage[db.actor:id()].disable_input_idle = delta
1159 level.disable_input()
1160end
1161
1162-- проверÑет целую чаÑть чиÑла на нечетноÑть
1163function odd( x )
1164 return math.floor( x * 0.5 ) * 2 == math.floor( x )
1165end
1166
1167--' находитÑÑ Ð»Ð¸ NPC во фруÑтруме игрока
1168function npc_in_actor_frustrum(npc)
1169 local actor_dir = device().cam_dir
1170 --local actor_dir = db.actor:direction()
1171 local npc_dir = vec_sub(npc:position(),db.actor:position())
1172 local yaw = yaw_degree3d(actor_dir, npc_dir)
1173 --printf("YAW %s", tostring(yaw))
1174 return yaw < 35
1175end
1176
1177--' LÑталоÑть
1178function on_actor_critical_power()
1179
1180end
1181
1182function on_actor_critical_max_power()
1183end
1184
1185--' іровотечение
1186function on_actor_bleeding()
1187
1188end
1189
1190function on_actor_satiety()
1191end
1192
1193--' іадиациÑ
1194function on_actor_radiation()
1195
1196end
1197
1198--' іаклинило оружие
1199function on_actor_weapon_jammed()
1200
1201end
1202
1203--' не может ходить изза веÑа
1204function on_actor_cant_walk_weight()
1205
1206end
1207
1208--' пÑи воздейÑтвие
1209function on_actor_psy()
1210end
1211
1212function give_info (info)
1213 db.actor:give_info_portion(info)
1214 --printf("DEBUG: GIVE INFO %s",info)
1215 --if (xrs_debug_tools and xrs_debug_tools.actor_info) then
1216 -- xrs_debug_tools.actor_info[info] = true
1217 --end
1218end
1219function disable_info (info)
1220 if has_alife_info(info) then
1221 --printf("DEBUG: DISABLE INFO %s",info)
1222 --printf("*INFO*: disabled npc='single_player' id='%s'", info)
1223 db.actor:disable_info_portion(info)
1224 --if (xrs_debug_tools and xrs_debug_tools.actor_info) then
1225 -- xrs_debug_tools.actor_info[info] = nil
1226 --end
1227 end
1228end
1229
1230function create_ammo(section, position, lvi, gvi, pid, num)
1231 local ini = system_ini()
1232
1233 local num_in_box = ini:r_u32(section, "box_size")
1234 local t = {}
1235 while num > num_in_box do
1236 t[#t+1] = alife():create_ammo(section, position, lvi, gvi, pid, num_in_box)
1237 num = num - num_in_box
1238 end
1239 local obj = alife():create_ammo(section, position, lvi, gvi, pid, num)
1240 table.insert(t, obj)
1241 return t
1242end
1243
1244-- преобразует Ñтроку в ÑоответÑтвии Ñо значением
1245function get_param_string(src_string , obj)
1246 --printf("src_string is [%s] obj name is [%s]", tostring(src_string), obj:name())
1247 local script_ids = db.script_ids[obj:id()]
1248 local out_string, num = string.gsub(src_string, "%$script_id%$", tostring(script_ids))
1249 if num > 0 then
1250 return out_string , true
1251 else
1252 return src_string , false
1253 end
1254end
1255
1256local save_marker_result = {}
1257-- Функции Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸ корректноÑти Ñейв лоад
1258function set_save_marker(p, mode, check, prefix)
1259 prefix = tostring(prefix)
1260 if (check ~= true) then
1261 if mode == "save" then
1262 save_marker_result[prefix] = p:w_tell() or 0
1263 if p:w_tell() > 16000 then
1264 abort("ERROR: You are saving too much")
1265 end
1266 else
1267 save_marker_result[prefix] = p:r_tell() or 0
1268 end
1269 return
1270 end
1271 if not (save_marker_result[prefix]) then
1272 abort("ERROR set_save_marker:%s: Trying to check without marker mode=%s",prefix,mode)
1273 if (mode == "save") then
1274 p:w_u16(0)
1275 elseif (mode == "load") then
1276 p:r_u16()
1277 end
1278 return
1279 end
1280 if mode == "save" then
1281 local dif = p:w_tell() - save_marker_result[prefix]
1282 if dif >= 8000 then
1283 printf("ERROR set_save_marker:%s: WARNING! may be this is problem save point dif=%s",prefix,dif)
1284 end
1285 p:w_u16(dif)
1286 else
1287 local c_dif = p:r_tell() - save_marker_result[prefix]
1288 local dif = p:r_u16()
1289 if dif ~= c_dif then
1290 printf("ERROR set_save_marker:%s: INCORRECT LOAD dif=%s c_dif=%s", prefix, dif, c_dif)
1291 end
1292 end
1293 save_marker_result[prefix] = nil
1294end
1295
1296-- переводит вектор в Ñтроку.
1297function vec_to_str (vector)
1298 if vector == nil then return "nil" end
1299 return string.format("[%s:%s:%s]", vector.x, vector.y, vector.z)
1300end
1301-- выводит в лог ÑÑ‚Ñк вызова функций.
1302function callstack()
1303 if (log and debug and type(debug.traceback) == 'function') then
1304 log(debug.traceback('\n', 2))
1305 end
1306end
1307-- менÑет team:squad:group обьекта.
1308function change_team_squad_group(se_obj, team, squad, group)
1309 local cl_obj = db.storage[se_obj.id] and db.storage[se_obj.id].object
1310 if cl_obj ~= nil then
1311 cl_obj:change_team(team, squad, group)
1312 else
1313 se_obj.team = team
1314 se_obj.squad = squad
1315 se_obj.group = group
1316 end
1317 --printf("_G:TSG: [%s][%s][%s]", tostring(se_obj.team), tostring(se_obj.squad), tostring(se_obj.group))
1318end
1319----------------------------------------Story_ID -------------------------------------
1320function get_story_se_object(story_id)
1321 local obj_id = story_objects.object_id_by_story_id[story_id]
1322 return obj_id and alife_object(obj_id)
1323end
1324
1325function get_story_object(story_id)
1326 local obj_id = story_objects.object_id_by_story_id[story_id]
1327 return obj_id and level.object_by_id(obj_id)
1328end
1329
1330function get_object_story_id(obj_id)
1331 return obj_id and story_objects.story_id_by_object_id[obj_id]
1332end
1333
1334function get_story_object_id(story_id)
1335 return story_id and story_objects.object_id_by_story_id[story_id]
1336end
1337
1338-----------------------------------------------------------------------------------------------
1339-- Получить Ñквад обьекта!!!!!
1340function get_object_squad(object,caller)
1341 if not (object) then
1342 return
1343 end
1344 if (object.group_id ~= nil and object.group_id ~= 65535) then
1345 return alife_object(object.group_id)
1346 end
1347 local sim = alife()
1348 local se_obj = type(object.id) == "function" and sim:object(object:id())
1349 return se_obj and se_obj.group_id ~= 65535 and sim:object(se_obj.group_id) or nil
1350end
1351
1352function get_story_squad(story_id)
1353 return get_story_se_object(story_id)
1354end
1355
1356--Проверка по временному интервалу.
1357function in_time_interval(val1, val2)
1358 local game_hours = level.get_time_hours()
1359 if val1 >= val2 then
1360 return game_hours < val2 or game_hours >= val1
1361 else
1362 return game_hours < val2 and game_hours >= val1
1363 end
1364end
1365
1366function show_all_ui(show)
1367 local hud = get_hud()
1368 if not (hud) then
1369 return
1370 end
1371 if(show) then
1372 level.show_indicators()
1373-- db.actor:restore_weapon()
1374 db.actor:disable_hit_marks(false)
1375 hud:show_messages()
1376 else
1377 if db.actor:is_talking() then
1378 db.actor:stop_talk()
1379 end
1380 level.hide_indicators_safe()
1381 hud:HideActorMenu()
1382 hud:HidePdaMenu()
1383 hud:hide_messages()
1384-- db.actor:hide_weapon()
1385 db.actor:disable_hit_marks(true)
1386 end
1387end
1388
1389------------------------------------------------------------------------------------------------------
1390-- ENGINE EXPORTS!!!
1391------------------------------------------------------------------------------------------------------
1392local flags = { ret_value = true }
1393function CInventoryBox_CanTake(obj,itm)
1394 flags.ret_value = true
1395 SendScriptCallback("inventory_box_can_take",obj,itm,flags)
1396 return flags.ret_value
1397end
1398-- called when an inventory item is eaten/used
1399-- returning false will prevent the item from being used
1400function CInventory__eat(npc,item)
1401 flags.ret_value = true
1402 SendScriptCallback("on_before_item_use",npc,item,flags)
1403 return flags.ret_value
1404end
1405
1406-- Called before actor hit callback
1407-- returning false will ignore the hit completely
1408function CActor__BeforeHitCallback(actor,shit,bone_id)
1409 --[[
1410 local hit_to_section = {
1411 [hit.light_burn] = "light_burn",
1412 [hit.burn] = "burn",
1413 [hit.strike] = "strike",
1414 [hit.shock] = "shock",
1415 [hit.wound] = "wound",
1416 [hit.radiation] = "radiation",
1417 [hit.telepatic] = "telepatic",
1418 [hit.chemical_burn] = "chemical_burn",
1419 [hit.explosion] = "explosion",
1420 [hit.fire_wound] = "fire_wound",
1421 }
1422 printf("power=%s impuse=%s type=%s dir=%s who=%s",shit.power,shit.impulse,hit_to_section[shit.type],shit.direction and vec_to_str(shit.direction),shit.draftsman and shit.draftsman:name())
1423 --]]
1424 if (shit.type ~= hit.strike) then
1425 if (bind_stalker_ext.invulnerable_time and time_global() < bind_stalker_ext.invulnerable_time) then
1426 if (db.actor) then
1427 if (db.actor.bleeding > 0) then
1428 db.actor.bleeding = 0.2
1429 end
1430 if (db.actor.radiation > 0) then
1431 db.actor.radiation = -1
1432 end
1433 end
1434 bind_stalker_ext.invulnerable_time = bind_stalker_ext.invulnerable_time - 500
1435 return false
1436 end
1437 end
1438
1439 if (shit.power > 0) then
1440 if (shit.draftsman and shit.draftsman:id() ~= 0 and shit.draftsman:cast_Stalker() and shit.draftsman:relation(db.actor) == game_object.friend) then
1441 return false
1442 end
1443 end
1444
1445 flags.ret_value = true
1446 SendScriptCallback("actor_on_before_hit",shit,bone_id,flags)
1447 return flags.ret_value
1448end
1449
1450-- called in ai_stalker_fire.cpp CAI_Stalker::Hit()
1451-- returning false will ignore the hit completely
1452function CAI_Stalker__BeforeHitCallback(npc,shit,bone_id)
1453 -- friendly fire
1454 if (shit.power > 0) then
1455 if (shit.draftsman and shit.draftsman:id() ~= 0 and shit.draftsman:cast_Stalker() and shit.draftsman:relation(npc) == game_object.friend) then
1456 return false
1457 end
1458 end
1459 flags.ret_value = true
1460 SendScriptCallback("npc_on_before_hit",shit,bone_id,flags)
1461 return flags.ret_value
1462end
1463
1464get_console():execute("r__clear_models_on_unload 0")
1465function CALifeUpdateManager__on_before_change_level(packet)
1466--[[
1467 C++:
1468 net_packet.r (&graph().actor()->m_tGraphID,sizeof(graph().actor()->m_tGraphID));
1469 net_packet.r (&graph().actor()->m_tNodeID,sizeof(graph().actor()->m_tNodeID));
1470 net_packet.r_vec3 (graph().actor()->o_Position);
1471 net_packet.r_vec3 (graph().actor()->o_Angle);
1472--]]
1473-- Here you can do stuff when level changes BEFORE save is called, even change destination!. Packet is constructed as stated above
1474
1475 -- Release dead bodies on level change (TODO: Determine if it's a bad idea to do this here)
1476 --[[
1477 local rbm = release_body_manager.get_release_body_manager()
1478 if (rbm) then
1479 rbm:clear(true)
1480 end
1481 --]]
1482
1483 -- READ PACKET
1484 local pos,angle = vector(),vector()
1485 local gvid = packet:r_u16()
1486 local lvid = packet:r_u32()
1487 packet:r_vec3(pos)
1488 packet:r_vec3(angle)
1489 -- crazy hack to help prevent crash on Trucks Cemetery
1490 local gg = game_graph()
1491 if (gg:valid_vertex_id(gvid) and alife():level_name(gg:vertex(gvid):level_id()) == "k02_trucks_cemetery") then
1492 log("k02_trucks_cemetery hack r__clear_models_on_unload 1")
1493 get_console():execute("r__clear_models_on_unload 1")
1494 end
1495 --printf("CALifeUpdateManager__on_before_change_level pos=%s gvid=%s lvid=%s angle=%s",pos,gvid,lvid,angle)
1496 -- fix for car in 1.6 (TODO*kinda For some reason after loading a game ALL physic objects will not be teleported by TeleportObject need to investigate as to why, possibly something to do with object flags)
1497 local car = db.actor and db.actor:get_attached_vehicle()
1498 if (car) then
1499 TeleportObject(car:id(),pos,lvid,gvid)
1500 end
1501 -- REPACK it for engine method to read as normal
1502 --[[
1503 packet:w_begin(13)
1504 packet:w_u16(gvid)
1505 packet:w_u32(lvid)
1506 packet:w_vec3(pos)
1507 packet:w_vec3(angle)
1508 --]]
1509 -- reset read pointer
1510 packet:r_seek(2)
1511
1512 if (bind_container.se_inv_box_containers) then
1513 for id,v in pairs(bind_container.se_inv_box_containers) do
1514 pos.y = pos.y+100
1515 TeleportObject(id,pos,lvid,gvid)
1516 end
1517 end
1518end
1519
1520-- 'ЗапуÑк динамичеÑкого окна.
1521function run_dynamic_element(folder,close_inv)
1522 if close_inv==false then
1523 folder:ShowDialog(true)
1524 elseif close_inv==true then
1525 folder:ShowDialog(true)
1526 local hud = get_hud()
1527 if (hud) then
1528 hud:HideActorMenu()
1529 hud:HidePdaMenu()
1530 end
1531 level.show_weapon(false)
1532 else
1533 folder:ShowDialog(true)
1534 end
1535end
1536
1537-- 'Создание предмета в рюкзаке ГГ.
1538function give_object_to_actor(obj,count)
1539 if count==nil then count=1 end
1540 for i=1, count do
1541 alife():create(obj,db.actor:position(),db.actor:level_vertex_id(),db.actor:game_vertex_id(),db.actor:id())
1542 end
1543end
1544
1545function string.gsplit(s, sep, plain)
1546 local start = 1
1547 local done = false
1548 local function pass(i, j, ...)
1549 if i then
1550 local seg = s:sub(start, i - 1)
1551 start = j + 1
1552 return seg, ...
1553 else
1554 done = true
1555 return s:sub(start)
1556 end
1557 end
1558 return function()
1559 if done then return end
1560 if sep == '' then done = true return s end
1561 return pass(s:find(sep, start, plain))
1562 end
1563end
1564
1565-- INI Extensions
1566function ini_file.r_string_ex(ini,s,k,def)
1567 --callstack()
1568 --printf("r_string_ex(%s,%s)",s,k)
1569 if not (ini:section_exist(s) and ini:line_exist(s,k)) then
1570 return def
1571 end
1572 return ini:r_string(s,k) or def
1573end
1574function ini_file.r_float_ex(ini,s,k,def)
1575 --callstack()
1576 --printf("r_float_ex(%s,%s)",s,k)
1577 if not (ini:section_exist(s) and ini:line_exist(s,k)) then
1578 return def
1579 end
1580 return ini:r_float(s,k) or def
1581end
1582-- It is wise to use the def with r_bool_ex, because false and nil are consider 'not'. def is only returned on nil
1583function ini_file.r_bool_ex(ini,s,k,def)
1584 --callstack()
1585 if not (ini:section_exist(s) and ini:line_exist(s,k)) then
1586 return def
1587 end
1588 --printf("r_bool_ex(%s,%s)",s,k)
1589 local v = ini:r_string(s,k)
1590 return v == nil and def or v == "true" or v == "1" or false
1591end
1592function ini_file.r_line_ex(ini,s,k)
1593 --callstack()
1594 return ini:r_line(s,k,"","")
1595end
1596function ini_file.r_string_to_condlist(ini,s,k,def)
1597 local src = ini:r_string_ex(s,k) or def
1598 if (src) then
1599 return xr_logic.parse_condlist(nil, s, k, src)
1600 end
1601end
1602function ini_file.r_list(ini,s,k,def)
1603 local src = ini:r_string_ex(s,k) or def
1604 if (src) then
1605 return parse_names(src)
1606 end
1607end
1608function ini_file.r_mult(ini,s,k,...)
1609 local src = ini:r_string_ex(s,k)
1610 if (src) then
1611 return unpack(parse_names(src))
1612 end
1613 return ...
1614end
1615-----------------------------------------
1616-- New INI wrapper to replace alun_utils.cfg_file
1617class "ini_file_ex"
1618function ini_file_ex:__init(fname,advanced_mode)
1619 self.fname = getFS():update_path('$game_config$', '')..fname
1620 self.ini = ini_file(fname)
1621 self.cache = {}
1622 if (advanced_mode) then
1623 self.ini:set_override_names(true)
1624 self.ini:set_readonly(false)
1625 --self.ini:save_at_end(true)
1626 end
1627end
1628
1629function ini_file_ex:save()
1630 self.ini:save_as(self.fname)
1631end
1632
1633-- r_value and w_value cache results
1634function ini_file_ex:r_value(s,k,typ,def)
1635 local cache_result = self.cache[s.."&"..k]
1636 if (cache_result) then
1637 return cache_result
1638 end
1639 if not (self.ini:section_exist(s) and self.ini:line_exist(s,k)) then
1640 return def
1641 end
1642 local v = self.ini:r_string(s,k)
1643 if (typ == 1) then
1644 v = v == nil and def or v == "true" or false
1645 elseif (typ == 2) then
1646 v = tonumber(v) or def
1647 end
1648 self.cache[s.."&"..k] = v
1649 return v == nil and def or v
1650end
1651
1652function ini_file_ex:w_value(s,k,val,comment)
1653 self.cache[s.."&"..k] = val
1654 self.ini:w_string(s,k,val ~= nil and tostring(val) or "",comment ~= nil and tostring(comment) or "")
1655end
1656
1657function ini_file_ex:collect_section(section)
1658 local _t = {}
1659
1660 local n = self.ini:section_exist(section) and self.ini:line_count(section) or 0
1661 if (n > 0) then
1662 for i = 0,n-1 do
1663 local res,id,val = self.ini:r_line(section,i,"","")
1664 _t[id] = val
1665 end
1666 end
1667
1668 return _t
1669end
1670
1671function ini_file_ex:get_sections(keytable)
1672 local t = {}
1673 local function itr(section)
1674 if (keytable) then
1675 t[section] = true
1676 else
1677 t[#t+1] = section
1678 end
1679 end
1680 self.ini:section_for_each(itr)
1681 return t
1682end
1683
1684function ini_file_ex:remove_line(section,key)
1685 self.ini:remove_line(section,key)
1686end
1687
1688function ini_file_ex:section_exist(section)
1689 return self.ini:section_exist(section)
1690end
1691
1692function ini_file_ex:line_exist(section,key)
1693 return self.ini:section_exist(section) and self.ini:line_exist(section,key)
1694end
1695
1696function ini_file_ex:r_string_ex(s,k)
1697 return self.ini:section_exist(s) and self.ini:line_exist(s,k) and self.ini:r_string(s,k) or nil
1698end
1699
1700function ini_file_ex:r_bool_ex(s,k,def)
1701 if not(self.ini:section_exist(s) and self.ini:line_exist(s,k)) then
1702 return def
1703 end
1704 local v = self.ini:r_string(s,k)
1705 return v == nil and def or v == "true" or v == "1" or false
1706end
1707
1708function ini_file_ex:r_float_ex(s,k)
1709 return self.ini:section_exist(s) and self.ini:line_exist(s,k) and tonumber(self.ini:r_string(s,k)) or nil
1710end
1711
1712function ini_file_ex:r_string_to_condlist(s,k,def)
1713 local src = self:r_string_ex(s,k) or def
1714 if (src) then
1715 return xr_logic.parse_condlist(nil, s, k, src)
1716 end
1717end
1718
1719function ini_file_ex:r_list(s,k,def)
1720 local src = self:r_string_ex(s,k) or def
1721 if (src) then
1722 return parse_names(src)
1723 end
1724end
1725
1726function ini_file_ex:r_mult(s,k,...)
1727 local src = self:r_string_ex(s,k) or def
1728 if (src) then
1729 return unpack(parse_names(src))
1730 end
1731 return ...
1732end
1733-----------------------------------------
1734-- Constants
1735-----------------------------------------
1736VEC_ZERO = vector():set(0,0,0)
1737VEC_X = vector():set(1,0,0)
1738VEC_Y = vector():set(0,1,0)
1739VEC_Z = vector():set(0,0,1)
1740
1741function vec_sub(a,b)
1742 return vector():set(a):sub(b)
1743end
1744
1745function vec_add(a,b)
1746 return vector():set(a):add(b)
1747end