435 lines
No EOL
16 KiB
Text
435 lines
No EOL
16 KiB
Text
|
||
function printf()
|
||
end
|
||
local pi_2 = math.pi / 3 -- 60 degree
|
||
|
||
local def_min_delta_per_sec = 0.2
|
||
local def_min_car_explode_time = 1000
|
||
|
||
local def_fire_angle = 120
|
||
|
||
local def_min_fire_time = 1.0
|
||
local def_fire_rep = 0.5
|
||
local def_fire_range = 50
|
||
|
||
local def_max_fc_upd_num = 1 -- default maximum fastcall updates num
|
||
|
||
local state_cannon_rotate = 1
|
||
local state_cannon_follow = 2
|
||
local state_cannon_delay = 3
|
||
local state_cannon_stop = 4
|
||
|
||
local state_shooting_on = 1
|
||
local state_none = 0
|
||
|
||
local state_firetarget_points = 1
|
||
local state_firetarget_enemy = 2
|
||
|
||
class "action_mgun"
|
||
function action_mgun:__init(obj, storage)
|
||
printf("mgun <state>: init.")
|
||
self.object = obj
|
||
self.mgun = self.object:get_car()
|
||
self.st = storage
|
||
self.start_direction = self.object:direction()
|
||
self.start_look_pos = vector():set(0,0,0)
|
||
self.start_look_pos.x = self.object:position().x + 5*math.sin(self.start_direction.x)
|
||
self.start_look_pos.z = self.object:position().z + 5*math.cos(self.start_direction.x)
|
||
self.start_look_pos.y = self.object:position().y
|
||
|
||
end
|
||
function action_mgun:reset_scheme(loading)
|
||
printf("car <state>: START INITIALIZING ======================================================")
|
||
printf("car <state>: action_mgun:reset_scheme: self.object:name()='%s'", self.object:name())
|
||
|
||
self.start_delaying_time = time_global()
|
||
self.start_shooting_time = time_global()
|
||
|
||
printf("pl mgun direction x:--- %s , z:--- %s", tostring(self.start_direction.x), tostring(self.start_direction.z))
|
||
|
||
self.fc_upd_num = 0 -- fastcall updates num
|
||
self.fc_upd_avg = 10 -- average time of the fastcall updates (in millisecond)
|
||
self.fc_last_upd_tm = -1 -- fastcall last update time
|
||
|
||
self.st.signals = {}
|
||
|
||
self.last_pos = nil
|
||
self.last_pos_time = 0
|
||
|
||
self.state_delaying = false
|
||
|
||
self.destroyed = false
|
||
self.object:set_nonscript_usable(false)
|
||
|
||
self.object:set_tip_text("")
|
||
if self.mgun:HasWeapon() then
|
||
printf("car <fire>: car has weapon.")
|
||
self.mgun:Action(CCar.eWpnActivate,1)
|
||
self.hasWeapon = true
|
||
else
|
||
printf("car <fire>: car hasn't weapon.")
|
||
self.hasWeapon = false
|
||
end
|
||
|
||
self.state_firetarget = state_none
|
||
self.state_cannon = state_none
|
||
self.state_shooting = state_none
|
||
|
||
self.target_fire_pt = nil
|
||
self.target_fire_pt_idx = 0
|
||
self.target_obj = nil
|
||
|
||
self.on_target_vis = nil
|
||
self.on_target_nvis = nil
|
||
|
||
if self.hasWeapon then
|
||
printf("car <fire>: target = %s", self.st.fire_target)
|
||
local n
|
||
if self.st.fire_target == "points" then
|
||
self.state_firetarget = state_firetarget_points
|
||
else
|
||
if self.st.fire_target == "actor" and db.actor:alive() then
|
||
self.target_obj = db.actor
|
||
self.state_firetarget = state_firetarget_enemy
|
||
else
|
||
n = self.st.fire_target
|
||
if n then
|
||
obj = get_story_object(n)
|
||
if obj and obj:alive() then
|
||
self.target_obj = obj
|
||
self.state_firetarget = state_firetarget_enemy
|
||
end
|
||
end
|
||
end
|
||
end
|
||
self.fire_track_target = self.st.fire_track_target
|
||
|
||
if self.st.on_target_vis then
|
||
vis = self.st.on_target_vis
|
||
if vis.v1 ~= nil then
|
||
obj = get_story_object(vis.v1)
|
||
if obj and obj:alive() then
|
||
vis.v1 = obj
|
||
self.on_target_vis = vis
|
||
printf("car <vis>: target %d", n)
|
||
|
||
end
|
||
end
|
||
end
|
||
if self.st.on_target_nvis then
|
||
nvis = self.st.on_target_nvis
|
||
if nvis.v1 ~= nil then
|
||
obj = get_story_object(nvis.v1)
|
||
if obj and obj:alive() then
|
||
nvis.v1 = obj
|
||
self.on_target_nvis = nvis
|
||
printf("car <nvis>: target %d", n)
|
||
end
|
||
end
|
||
end
|
||
|
||
self.path_fire = self.st.path_fire
|
||
self.path_fire_point = nil
|
||
--' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||
if self.path_fire ~= nil then
|
||
if level.patrol_path_exists(self.path_fire) then
|
||
self.path_fire_point = patrol(self.path_fire):point(0)
|
||
else
|
||
abort("[ph_minigun] patrol path %s doesnt exist.", tostring(self.path_fire))
|
||
end
|
||
end
|
||
|
||
self.def_fire_time = self.st.fire_time
|
||
self.def_fire_rep = self.st.fire_rep
|
||
self.fire_rep = self.def_fire_rep
|
||
|
||
printf("car <fire>: def_rep = %d (%s)", self.fire_rep, utils.to_str(self.st.fire_rep))
|
||
|
||
self.fire_range_sqr = self.st.fire_range * self.st.fire_range
|
||
|
||
--' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||
if self.state_firetarget == state_firetarget_points and self.path_fire then
|
||
printf("car <state>: firetarget = points")
|
||
self.state_cannon = state_cannon_follow
|
||
self.state_shooting = state_none
|
||
elseif self.state_firetarget == state_firetarget_enemy then
|
||
printf("car <state>: firetarget = enemy")
|
||
self.state_shooting = state_none
|
||
self.state_cannon = state_cannon_follow
|
||
else
|
||
printf("car <state>: firetarget = none")
|
||
self.state_firetarget = state_none
|
||
self.state_cannon = state_none
|
||
self.state_shooting = state_none
|
||
end
|
||
end
|
||
|
||
self.object:set_fastcall(self.fastcall, self)
|
||
printf("car <state>: END INITIALIZING ========================================================\n")
|
||
end
|
||
|
||
function action_mgun:set_shooting(shooting)
|
||
self.mgun:Action(CCar.eWpnFire, shooting)
|
||
--'printf("car <fire>: action_car:set_shooting(%d)", shooting)
|
||
end
|
||
function action_mgun:check_fire_time()
|
||
if self.st.fire_rep == -1 then return end
|
||
|
||
if 1000*self.st.fire_time + self.start_shooting_time >= time_global() and self.state_delaying == false then
|
||
self.state_delaying = false
|
||
self.start_delaying_time = time_global() + math.random(-0.2, 0.2)*1000*self.st.fire_time
|
||
return
|
||
else
|
||
self.state_delaying = true
|
||
end
|
||
if self.start_delaying_time + 1000*self.st.fire_rep >= time_global() and self.state_delaying == true then
|
||
self.state_delaying = true
|
||
self.start_shooting_time = time_global()
|
||
else
|
||
self.state_delaying = false
|
||
end
|
||
end
|
||
|
||
function action_mgun:save()
|
||
end
|
||
|
||
function action_mgun:rot_to_firedir(direction)
|
||
if direction then
|
||
self.mgun:SetParam(CCar.eWpnDesiredPos, direction)
|
||
end
|
||
end
|
||
|
||
function action_mgun:rot_to_firepoint(pt)
|
||
if pt then
|
||
self.mgun:SetParam(CCar.eWpnDesiredPos, pt)
|
||
end
|
||
end
|
||
|
||
function action_mgun:set_signal(sig)
|
||
local stor = db.storage[self.object:id()]
|
||
stor[stor.active_scheme].signals[sig] = true
|
||
printf("car <sig>: %s", sig)
|
||
end
|
||
|
||
function action_mgun:fastcall()
|
||
if db.storage[self.object:id()].active_scheme ~= "ph_minigun" then
|
||
--' <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> - <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
self:set_shooting(0)
|
||
return true
|
||
end
|
||
|
||
return self:fast_update()
|
||
end
|
||
|
||
function action_mgun:update(delta)
|
||
if xr_logic.try_switch_to_another_section(self.object, self.st, db.actor) then
|
||
return
|
||
end
|
||
|
||
if self.destroyed then
|
||
xr_logic.switch_to_section(self.object, self.st.ini, "nil")
|
||
return
|
||
end
|
||
self:check_fire_time()
|
||
end
|
||
|
||
|
||
function action_mgun:fast_update()
|
||
--printf("car <state>: START FAST UPDATE ======================================================")
|
||
|
||
if self.mgun:GetfHealth() <= 0 then
|
||
printf("car <state>: killed.")
|
||
self:destroy_car()
|
||
return true
|
||
end
|
||
|
||
local cur_time = time_global()
|
||
|
||
if self.fc_upd_num < def_max_fc_upd_num then
|
||
local last_upd = self.fc_last_upd_tm
|
||
if last_upd ~= -1 then
|
||
local n = self.fc_upd_num
|
||
if n < 3000 then
|
||
self.fc_upd_avg = (self.fc_upd_avg * n + (cur_time - last_upd))/(n + 1)
|
||
self.fc_upd_num = n + 1
|
||
else
|
||
self.fc_upd_num = 1
|
||
end
|
||
end
|
||
self.fc_last_upd_tm = cur_time
|
||
-- printf("car <state>: average update = %f, time(%f)", self.fc_upd_avg, cur_time)
|
||
end
|
||
|
||
|
||
if self.state_cannon == state_cannon_stop and
|
||
self.state_firetarget == state_none
|
||
then
|
||
if xr_logic.mob_captured(self.object) and not self.object:action() then
|
||
printf("car <state>: stopped")
|
||
self:destroy_car()
|
||
return true -- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>
|
||
end
|
||
return false
|
||
end
|
||
|
||
|
||
|
||
--printf("car <fire>: TEST")
|
||
if self.hasWeapon then
|
||
-- printf("car <fire>: target(%d)", self.state_firetarget)
|
||
if self.on_target_vis and self.on_target_vis.v1:alive() and self.mgun:IsObjectVisible(self.on_target_vis.v1) then
|
||
-- printf("car <vis>: try")
|
||
local new_section = xr_logic.pick_section_from_condlist(db.actor, self.object, self.on_target_vis.condlist)
|
||
if new_section then
|
||
-- printf("car <vis>: switch to section [%s]", new_section)
|
||
xr_logic.switch_to_section(self.object, self.st.ini, new_section)
|
||
end
|
||
end
|
||
|
||
if self.on_target_nvis and self.on_target_nvis.v1:alive() and not self.mgun:IsObjectVisible(self.on_target_nvis.v1) then
|
||
--printf("car <nvis>: try")
|
||
local new_section = xr_logic.pick_section_from_condlist(db.actor, self.object, self.on_target_nvis.condlist)
|
||
if new_section then
|
||
printf("car <nvis>: switch to section [%s]", new_section)
|
||
xr_logic.switch_to_section(self.object, self.st.ini, new_section)
|
||
end
|
||
end
|
||
|
||
if self.state_firetarget == state_firetarget_points then
|
||
--' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
local fire_angle = angle_xz(self.object, self.path_fire_point, self.start_direction)
|
||
local can_rotate = fire_angle <= self.st.fire_angle*math.pi/360 and fire_angle >= -(self.st.fire_angle*math.pi/360)
|
||
|
||
if can_rotate then
|
||
self:rot_to_firepoint(self.path_fire_point)
|
||
if self.state_delaying then
|
||
if self.state_shooting ~= state_none and self.st.auto_fire == true then
|
||
self.state_shooting = state_none
|
||
self:set_shooting(self.state_shooting)
|
||
end
|
||
else
|
||
if self.state_shooting == state_none then
|
||
self.state_shooting = state_shooting_on
|
||
self:set_shooting(self.state_shooting)
|
||
end
|
||
end
|
||
end
|
||
elseif self.state_firetarget == state_firetarget_enemy then
|
||
local fire_angle = angle_xz(self.object, self.target_obj:position(), self.start_direction)
|
||
local can_rotate = fire_angle <= self.st.fire_angle*math.pi/360 and fire_angle >= -(self.st.fire_angle*math.pi/360)
|
||
local object_visible = self.mgun:IsObjectVisible(self.target_obj) or self.st.shoot_only_on_visible == false
|
||
|
||
--' printf("object visible:---------> %s can_rotate = %s fire_angle = %s", tostring(object_visible), tostring(can_rotate), tostring(fire_angle))
|
||
if self.target_obj:alive() and
|
||
self.object:position():distance_to_sqr(self.target_obj:position()) <= self.fire_range_sqr and
|
||
object_visible and can_rotate
|
||
then
|
||
if not self.state_delaying then
|
||
self.target_fire_pt = self.target_obj:position()
|
||
if self.target_obj:id() ~= db.actor:id() then
|
||
if self.target_obj:target_body_state() == move.crouch then
|
||
self.target_fire_pt.y = self.target_fire_pt.y + 0.5 --' FAKE
|
||
elseif not xr_wounded.is_heavy_wounded_by_id(self.target_obj:id()) then
|
||
self.target_fire_pt.y = self.target_fire_pt.y + 1.2 --' FAKE
|
||
else
|
||
self.target_fire_pt.y = self.target_fire_pt.y + 0.10 --' FAKE
|
||
end
|
||
else
|
||
self.target_fire_pt.y = self.target_fire_pt.y + 1.0 --' FAKE
|
||
end
|
||
self:rot_to_firepoint(self.target_fire_pt)
|
||
|
||
if self.mgun:CanHit() then
|
||
if self.state_shooting == state_none and self.st.auto_fire == true then
|
||
--'printf("car <fire>: shooting enemy (first).")
|
||
self.state_shooting = state_shooting_on
|
||
self:set_shooting(self.state_shooting)
|
||
end
|
||
else
|
||
if self.state_shooting ~= state_none then
|
||
--'printf("car <fire>: targeting enemy (first).")
|
||
self.state_shooting = state_none
|
||
self:set_shooting(self.state_shooting)
|
||
end
|
||
--'printf("car <fire>: targeting enemy.")
|
||
end
|
||
else
|
||
self.state_shooting = state_none
|
||
self:set_shooting(self.state_shooting)
|
||
end
|
||
else
|
||
if self.state_shooting ~= state_none or not can_rotate or self.state_delaying then
|
||
--' printf("car <fire>: enemy isn't visible (first).%s %s %s", tostring(self.object:direction().x), tostring(self.object:direction().y), tostring(self.object:direction().z))
|
||
self.state_shooting = state_none
|
||
self:set_shooting(self.state_shooting)
|
||
self:rot_to_firedir(self.start_look_pos)
|
||
end
|
||
--' printf("car <fire>: enemy isn't visible.")
|
||
if self.fire_track_target then
|
||
self.target_fire_pt = self.target_obj:position()
|
||
self.target_fire_pt.y = self.target_fire_pt.y + 1.0 -- FAKE
|
||
self:rot_to_firepoint(self.target_fire_pt)
|
||
--' printf("car <fire>: target tracking.")
|
||
end
|
||
end
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
|
||
function action_mgun:destroy_car()
|
||
printf("car <destroy>: START ===============================")
|
||
self.state_cannon = state_none
|
||
self.state_firetarget = state_none
|
||
self.state_shooting = state_none
|
||
self.mgun:Action(CCar.eWpnAutoFire, 0)
|
||
self:set_shooting(self.state_shooting)
|
||
|
||
xr_logic.mob_release(self.object)
|
||
if self.st.on_death_info ~= nil then
|
||
db.actor:give_info_portion(self.st.on_death_info)
|
||
printf("car <destroy>: on_death_info [%s]", self.st.on_death_info)
|
||
end
|
||
self.destroyed = true
|
||
printf("car <destroy>: END =================================")
|
||
end
|
||
|
||
function angle_xz(npc , target_pos, start_direction)
|
||
local dir1 = start_direction
|
||
dir1.y = 0
|
||
local dir2 = vector():set(target_pos.x, target_pos.y, target_pos.z)
|
||
dir2 = dir2:sub(npc:position())
|
||
dir2.y = 0
|
||
return yaw(dir1, dir2)
|
||
end
|
||
|
||
|
||
--' ******************************************************************************************************************
|
||
--' * bind *
|
||
--' ******************************************************************************************************************
|
||
function add_to_binder(npc, ini, scheme, section, storage)
|
||
-- printf("DEBUG: add_to_binder: scheme='%s', section='%s'", scheme, section)
|
||
|
||
local new_action = action_mgun(npc, storage)
|
||
|
||
-- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> actions, <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> reset_scheme <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>:
|
||
xr_logic.subscribe_action_for_events(npc, storage, new_action)
|
||
end
|
||
function set_scheme(npc, ini, scheme, section, gulag_name)
|
||
local st = xr_logic.assign_storage_and_bind(npc, ini, scheme, section)
|
||
|
||
st.logic = xr_logic.cfg_get_switch_conditions(ini, section, npc)
|
||
st.path_fire = utils.cfg_get_string(ini, section, "path_fire", npc, false, gulag_name, nil)
|
||
st.auto_fire = utils.cfg_get_bool(ini, section, "auto_fire", npc, false, false) --' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD>.
|
||
st.fire_time = utils.cfg_get_number(ini, section, "fire_time", npc, false, def_min_fire_time) --' <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
st.fire_rep = utils.cfg_get_number(ini, section, "fire_repeat", npc, false, def_fire_rep) --' <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
st.fire_range = utils.cfg_get_number(ini, section, "fire_range", npc, false, def_fire_range) --' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
st.fire_target = utils.cfg_get_string(ini, section, "target", npc, false, gulag_name, "points") --' <20><><EFBFBD><EFBFBD>: actor|story_ids|points
|
||
st.fire_track_target= utils.cfg_get_bool(ini, section, "track_target", npc, false, false) --' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>.
|
||
st.fire_angle = utils.cfg_get_number(ini, section, "fire_angle", npc, false, def_fire_angle) --' <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
st.shoot_only_on_visible = utils.cfg_get_bool(ini, section, "shoot_only_on_visible", npc, false, true)
|
||
|
||
st.on_target_vis = xr_logic.cfg_get_string_and_condlist(ini, section, "on_target_vis", npc) --' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
st.on_target_nvis = xr_logic.cfg_get_string_and_condlist(ini, section, "on_target_nvis", npc) --' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
end |