---------------------------------------------------------------------------------------------------- -- Mob Camp ---------------------------------------------------------------------------------------------------- -- Разработчик: Jim ---------------------------------------------------------------------------------------------------- local STATE_CAMP = 1 local STATE_ALIFE = 2 local STATE_MOVE_HOME = 3 class "mob_camp" ---------------------------------------------------------------------------------------------------- -- CONSTRUCTION SCHEME ---------------------------------------------------------------------------------------------------- function mob_camp:__init(obj, storage) self.object = obj self.st = storage end ---------------------------------------------------------------------------------------------------- -- RESET SCHEME ---------------------------------------------------------------------------------------------------- function mob_camp:reset_scheme() printf("Camp: reset_scheme: %s", self.object:name()) xr_logic.mob_capture (self.object, true) mob_state_mgr.set_state (self.object, db.actor, self.st.state) -- reset signals self.st.signals = {} -- initialize look point self.look_path = patrol(self.st.look_point) if not self.look_path then _G.abort("object '%s': unable to find look_point '%s' on the map", self.object:name(), self.st.look_point) end -- initialize home point if self.st.home_point then self.home_path = patrol(self.st.home_point) if not self.home_path then _G.abort("object '%s': unable to find home_point '%s' on the map", self.object:name(), self.st.home_point) end else self.home_path = nil end -- checkings -- if there is home path and look path - point count must be equal if self.home_path then if (self.home_path:count() ~= self.look_path:count()) then _G.abort("object '%s': you must setup home path points count must be equal to look path points count!", self.object:name()) end end -- save position and node of object self.camp_position = vector():set(self.object:position()) self.camp_node = self.object:level_vertex_id() self.state_current = STATE_CAMP self.state_prev = self.state_current -- select cur point self.cur_point_index = 0 self:select_current_home_point(true) self.time_point_changed = time_global() self.prev_enemy = false -- check enemy transfering if self.st.skip_transfer_enemy then self.object:skip_transfer_enemy(true) end end ---------------------------------------------------------------------------------------------------- -- UPDATE ---------------------------------------------------------------------------------------------------- function mob_camp:update(delta) if xr_logic.try_switch_to_another_section(self.object, self.st, db.actor) then return end -- if dead then release if not self.object:alive() then xr_logic.mob_release(self.object) return end -- update point changer if (self.time_point_changed + self.st.time_change_point < time_global()) then self:select_current_home_point(false) self.time_point_changed = time_global() end -- update fsm self:select_state () self:execute_state () end ---------------------------------------------------------------------------------------------------- -- SERVICE FUNCTIONS ---------------------------------------------------------------------------------------------------- function mob_camp:select_current_home_point(first_call) local prev_point_index = self.cur_point_index if self.home_path then -- fill table of free points local free_points = {} if (db.camp_storage[self.st.home_point] == nil) then db.camp_storage[self.st.home_point] = {} end for i = 1, self.home_path:count() do if (db.camp_storage[self.st.home_point][i] == nil) or (db.camp_storage[self.st.home_point][i] == false) then table.insert(free_points, i) end end if (#free_points < 1) then if first_call == true then _G.abort("Mob_Camp : too many campers for home path") end else local free_points_index = math.random(1, #free_points) self.cur_point_index = free_points[free_points_index]-1 end if not first_call then if prev_point_index ~= self.cur_point_index then db.camp_storage[self.st.home_point][prev_point_index+1] = false end end db.camp_storage[self.st.home_point][self.cur_point_index+1] = true else self.cur_point_index = math.random(0, self.look_path:count() - 1) end end --------------------------------------------------------------------------------- function mob_camp:select_state() self.state_prev = self.state_current local home_position = self.camp_position local home_node = self.camp_node if self.home_path then home_position = self.home_path:point(self.cur_point_index) home_node = self.home_path:level_vertex_id(self.cur_point_index) end -- if enemy local enemy = self.object:get_enemy() -- if enemy just appeared - signal if enemy ~= nil then if not self.prev_enemy then self.st.signals["enemy"] = true end self.prev_enemy = true else self.prev_enemy = false end if enemy ~= nil then local enemy_dist = enemy:position():distance_to(home_position) --local my_dist = self.object:position():distance_to(home_position) if (self.state_prev == STATE_MOVE_HOME) and (enemy_dist > self.st.home_min_radius) then elseif (self.state_prev == STATE_ALIFE) and (enemy_dist > self.st.home_max_radius) then self.state_current = STATE_MOVE_HOME elseif (self.state_prev == STATE_CAMP) and (enemy_dist > self.st.home_min_radius) then else self.state_current = STATE_ALIFE end end -- select MOVE_HOME OR CAMP if (enemy == nil) or ((enemy ~= nil) and (self.state_current ~= STATE_ALIFE) ) then -- check if we go home if (home_position:distance_to(self.object:position()) > 1) and (home_node ~= self.object:level_vertex_id()) then self.state_current = STATE_MOVE_HOME else -- we are on place - camp! self.state_current = STATE_CAMP end end -- Отпускать в alife монстров, которых обстреляли local h = self.object:get_monster_hit_info() if (enemy == nil) and (h.who) and (h.time ~= 0) and (IsMonster(h.who) or IsStalker(h.who))then local dist = self.object:position():distance_to(home_position) if (dist < self.st.home_min_radius) then self.state_current = STATE_ALIFE end end end ---------------------------------- -- EXECUTE_STATE function mob_camp:execute_state() -- DBG -- if (self.state_current ~= self.state_prev) then -- local str1 = "" -- local str2 = "" -- -- if self.state_current == STATE_CAMP then str1 = "STATE_CAMP" end -- if self.state_current == STATE_JUMP then str1 = "STATE_JUMP" end -- if self.state_current == STATE_ALIFE then str1 = "STATE_ALIFE" end -- if self.state_current == STATE_MOVE_HOME then str1 = "STATE_MOVE_HOME" end -- -- if self.state_prev == STATE_CAMP then str2 = "STATE_CAMP" end -- if self.state_prev == STATE_JUMP then str2 = "STATE_JUMP" end -- if self.state_prev == STATE_ALIFE then str2 = "STATE_ALIFE" end -- if self.state_prev == STATE_MOVE_HOME then str2 = "STATE_MOVE_HOME" end -- -- printf("~MOB_CAMP: From [%s] To [%s]", str2, str1) -- end if (self.state_current == STATE_ALIFE) and (self.state_prev == STATE_ALIFE) then return end if (self.state_current == STATE_ALIFE) and (self.state_prev ~= STATE_ALIFE) then xr_logic.mob_release(self.object) return end if (self.state_current ~= STATE_ALIFE) and (self.state_prev == STATE_ALIFE) then xr_logic.mob_capture(self.object, true) end -- STATE_CAMP if self.state_current == STATE_CAMP then -- handle look point if not self.object:action() then action( self.object, anim(anim.stand_idle), look(look.point, self.look_path:point(self.cur_point_index)), cond(cond.look_end) ) end return end -- STATE_MOVE_HOME if self.state_current == STATE_MOVE_HOME then if not self.object:action() then local home_position = self.camp_position local home_node = self.camp_node if self.home_path then home_position = self.home_path:point(self.cur_point_index) home_node = self.home_path:level_vertex_id(self.cur_point_index) end action( self.object, move(move.run_fwd, home_node, home_position), cond(cond.move_end)) end return end end function mob_camp:deactivate() if self.home_path then db.camp_storage[self.st.home_point][self.cur_point_index+1] = false end self.object:skip_transfer_enemy(false) end function mob_camp:net_destroy() if self.home_path then db.camp_storage[self.st.home_point][self.cur_point_index+1] = false end self.object:skip_transfer_enemy(false) end ---------------------------------------------------------------------------------------------------- -- ADD_TO_BINDER ---------------------------------------------------------------------------------------------------- function add_to_binder(npc, ini, scheme, section, storage) printf("DEBUG: add_to_binder: npc:name()='%s', scheme='%s', section='%s'", npc:name(), scheme, section) local new_action = mob_camp(npc, storage) -- Зарегистрировать все actions, в которых должен быть вызван метод reset_scheme при изменении настроек схемы: xr_logic.subscribe_action_for_events(npc, storage, new_action) end ---------------------------------------------------------------------------------------------------- -- SET_SCHEME ---------------------------------------------------------------------------------------------------- function set_scheme(npc, ini, scheme, section, gulag_name) local storage = xr_logic.assign_storage_and_bind(npc, ini, scheme, section) storage.logic = xr_logic.cfg_get_switch_conditions(ini, section, npc) storage.state = mob_state_mgr.get_state(ini, section, npc) storage.look_point = utils.cfg_get_string(ini, section, "path_look", npc, false, gulag_name) storage.home_point = utils.cfg_get_string(ini, section, "path_home", npc, false, gulag_name) storage.time_change_point = utils.cfg_get_number(ini, section, "time_change_point", npc, false, 10000) storage.home_min_radius = utils.cfg_get_number(ini, section, "home_min_radius", npc, false, 30) storage.home_max_radius = utils.cfg_get_number(ini, section, "home_max_radius", npc, false, 40) -- check min and max radius if storage.home_min_radius > storage.home_max_radius then _G.abort("Mob_Camp : Home Min Radius MUST be < Max Radius") end -- check if there is look point (must be!) if (not storage.look_point) or (not patrol(storage.look_point)) then _G.abort("Mob_Camp : object '%s': unable to find look_point '%s' on the map", npc:name(), storage.look_point) end -- load transfer enemy flag () storage.skip_transfer_enemy = ini:line_exist( section, "skip_transfer_enemy") end