----------------------------------------------------------------------------------------------------
-- Physic vehicle control
----------------------------------------------------------------------------------------------------
-- Исходный скрипт: Evgeniy Negrobov (Jon) jon@gsc-game.kiev.ua
-- Перевод на xr_logic: Andrey Fidrya (Zmey) af@svitonline.com
-- Доработка для БТР: Oleg Kreptul (Haron) haronk@ukr.net
----------------------------------------------------------------------------------------------------
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_moving_speed = 10
--local def_arriving_factor = 0.4
local def_arriving_dist = 1.0
local def_not_rotating_angle = 0.4
local def_min_fire_time = 0
local def_update_time = 1.6
local def_fire_range = 50

local def_max_fc_upd_num = 1000 -- default maximum fastcall updates num
local def_arriving_koef = 3 --70

local delay_after_ignition = 500

local state_none = 0

local state_moving_fwd = 1
local state_moving_rot_left = 2
local state_moving_rot_right = 3
local state_moving_stop = 4
local state_moving_end = 5

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_firetarget_points = 1
local state_firetarget_enemy = 2

-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class "action_car"

function action_car:__init(obj, storage)
    printf("car <state>: init.")
	self.object = obj
	self.car = self.object:get_car()
	self.st = storage
end

function action_car:reset_scheme(loading)
	printf("car <state>: START INITIALIZING ======================================================")
    printf("car <state>: action_car:reset_scheme: self.object:name()='%s'", self.object:name())

	self.destroyed = false
	self.object:set_nonscript_usable(false)

    self.object:set_tip_text("")
	self.show_tips = self.st.show_tips
	self.tip_use = self.st.tip_use
	self.tip_locked = self.st.tip_locked

	if self.st.invulnerable then
	    self.def_health = self.object.health
	end

	self.headlights = object.deactivate
	if self.st.headlights == "on" then
	    self.headlights = object.activate
	end
	printf("car <state>: headlights %s", self.st.headlights)
	xr_logic.mob_capture(self.object, true)
	action(self.object, object("left_light",  self.headlights), cond(cond.time_end, time_infinite))
	xr_logic.mob_capture(self.object, true)
	action(self.object, object("right_light", self.headlights), cond(cond.time_end, time_infinite))

    self.usable = self.st.usable
    
    --if self.usable ~= nil then
	--    self.car_holder = self.object.get_current_holder() --engaged()
	--else
    if self.usable == nil then
	    self.car:SetExplodeTime(0) -- (-1)

	    if self.car:HasWeapon() then
	    printf("car <fire>: car has weapon.")
	    self.car:Action(CCar.eWpnActivate,1)
	    self.hasWeapon = true
	    else
	        printf("car <fire>: car hasn't weapon.")
	        self.hasWeapon = false
	    end

	    if loading then
		    self.speed = xr_logic.pstor_retrieve(self.object, "speed")
		    self.loop = xr_logic.pstor_retrieve(self.object, "loop")
		    self.min_delta_per_sec = xr_logic.pstor_retrieve(self.object, "min_delta_per_sec")
		    self.min_car_explode_time = xr_logic.pstor_retrieve(self.object, "min_car_explode_time")
		    self.state_moving = xr_logic.pstor_retrieve(self.object, "state_moving")
	    else
		    self.speed = def_moving_speed
		    self.loop = false
		    self.min_delta_per_sec = def_min_delta_per_sec
		    self.min_car_explode_time = def_min_car_explode_time
		    self.state_moving = state_none
	    end

	    --xr_logic.mob_capture(self.object, true)
	    self.st.signals = {}

	    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.last_pos = nil
	    self.last_pos_time = 0
    	
	    self.state_delaying = false

	    --self.state_moving = state_none

        self.target_walk_pt = -1
	    -- path_walk
	    if self.st.path_walk then
	        self.path_walk = patrol(self.st.path_walk)
	        if not self.path_walk then
		        abort("object '%s': unable to find path_walk '%s' on the map", self.object:name(), self.st.path_walk)
	        end

            self:start_car()
	        self.path_walk_count = self.path_walk:count()

	        --if not self.path_walk_info then
		    self.path_walk_info = utils.path_parse_waypoints(self.st.path_walk)
	        if not self.path_walk_info then
		        abort("object '%s': path_walk ('%s'): unable to obtain path_walk_info from path",
			        self.object:name(), self.st.path_walk)
	        end
	        --end

	        self.arrival_signalled = false
	        self.target_dist = -1

	        if loading then
		        self.target_walk_pt = xr_logic.pstor_retrieve(self.object, "target_walk_pt")
		        if self.target_walk_pt == -1 then
		            self.target_walk_pt = self:get_nearest_walkpoint()
		        end
	        else
		        self.target_walk_pt = self:get_nearest_walkpoint()
	        end
	        printf("car <move>: target_walk_pt.1(%d)", self.target_walk_pt)

	        if self:at_target_walkpoint() then
		        self:walk_arrival_callback(self.target_walk_pt)
	        end
        	
	        self:go_to_walkpoint(self.target_walk_pt)
	    else
	        printf("car <state>: target_walk_pt.2(-2)")
	        self.target_walk_pt = -2
	        self.state_moving = state_moving_stop
	    end
	    printf("car <move>: start target_pt1 = %d", self.target_walk_pt)
    	
	    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
	        --if loading then
		    --    self.state_cannon = xr_logic.pstor_retrieve(self.object, "state_cannon")
		    --    self.state_shooting = xr_logic.pstor_retrieve(self.object, "state_shooting")
	        --end
	        
	        self:set_shooting(self.state_shooting)

	        --printf("car <fire>: target = %s", self.st.fire_target)
	        local n = 0
	        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 = tonumber(self.st.fire_target)
	                if n then
	                    obj = level_object_by_sid(n)
	                    if obj and obj:alive() then
	                        self.target_obj = obj
	                        self.state_firetarget = state_firetarget_enemy
	                    end
	                end
	            end
	            --if self.target_obj then
	            --    self.target_ph_shell = self.target_obj:get_physics_shell()
	            --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 == "actor" then
	                vis.v1 = db.actor
	                self.on_target_vis = vis
	                printf("car <vis>: target actor")
	            else
	                n = tonumber(vis.v1)
	                if n then
	                    obj = level_object_by_sid(n)
	                    if obj and obj:alive() then
	                        vis.v1 = obj
	                        self.on_target_vis = vis
	                        printf("car <vis>: target %d", n)
	                    end
	                end
	            end
	        end
	        if self.st.on_target_nvis then
	            nvis = self.st.on_target_nvis
	            if nvis.v1 == "actor" then
	                nvis.v1 = db.actor
	                self.on_target_nvis = nvis
	                printf("car <nvis>: target actor")
	            else
	                n = tonumber(nvis.v1)
	                if n then
	                    obj = level_object_by_sid(n)
	                    if obj and obj:alive() then
	                        nvis.v1 = obj
	                        self.on_target_nvis = nvis
	                        printf("car <nvis>: target %d", n)
	                    end
	                end
	            end
	        end
        	
	        self.path_fire = nil
	        self.path_fire_info = nil
	        self.fire_pt_count = 0
    	    
	        self.def_fire_time = self.st.fire_time
	        if self.st.fire_rep then
	            if self.st.fire_rep == "inf" then
	                self.def_fire_rep = -1
	            else
	                local c = tonumber(self.st.fire_rep)
		            self.def_fire_rep = if_then_else(c > 0, c, 0)
		        end
		    else
		        self.def_fire_rep = 0
	        end
	        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

	        -- path_fire
	        if self.state_firetarget == state_firetarget_points and self.st.path_fire then
	            printf("car <state>: firetarget = points")
		        self.path_fire = patrol(self.st.path_fire)
		        if not self.path_fire then
			        abort("object '%s': unable to find path_fire '%s' on the map",
				        self.object:name(), self.st.path_fire)
		        end

	            if not self.path_fire_info then
		            self.path_fire_info = utils.path_parse_waypoints(self.st.path_fire)
	                if not self.path_fire_info then
		                abort("object '%s': path_fire ('%s'): unable to obtain path_fire_info from path",
			                self.object:name(), self.st.path_fire)
	                end
	            end
        	    
	            -- точки прострела для первой точки движения
	            self:change_fire_pts()
            	
	            if self.st.auto_fire then
	                self.car:Action(CCar.eWpnAutoFire, 1)
	            else
	                self.car:Action(CCar.eWpnAutoFire, 0)
	            end
        	    
	            self:fire_arrival_callback(self.target_fire_pt_idx)
        	    
	            --self:rot_to_firepoint(self.target_fire_pt)
	            --self:set_shooting(self.state_shooting)
	        elseif self.state_firetarget == state_firetarget_enemy then
	            printf("car <state>: firetarget = enemy")
	            self.state_shooting = state_none
	            self.state_cannon = state_cannon_follow
	            --self.target_fire_pt = db.actor:position()
		        --self:rot_to_firepoint(self.target_fire_pt)
	        else
	            printf("car <state>: firetarget = none")
	            self.state_firetarget = state_none
	            self.state_cannon = state_none
	            self.state_shooting = state_none
	        end
		    --self:set_shooting(self.state_shooting)
	    end
    	
	    --if self.st.path_walk then
	    --    if self:at_target_walkpoint() and self.state_firetarget == state_none then
	    --	    self:walk_arrival_callback(self.target_walk_pt)
	    --    end
        --	
	    --    self:go_to_walkpoint(self.target_walk_pt)
        --end
	    printf("car <move>: start target_pt2 = %d", self.target_walk_pt)
    end

	self.object:set_fastcall(self.fastcall, self)
	printf("car <state>: END INITIALIZING ========================================================\n")
end

function action_car:save()
    printf("car <state>: saving")
    if self.usable == nil then
	    xr_logic.pstor_store(self.object, "speed", self.speed)
	    xr_logic.pstor_store(self.object, "loop", self.loop)
	    xr_logic.pstor_store(self.object, "min_delta_per_sec", self.min_delta_per_sec)
	    xr_logic.pstor_store(self.object, "min_car_explode_time", self.min_car_explode_time)
	    xr_logic.pstor_store(self.object, "state_moving", self.state_moving)
	    xr_logic.pstor_store(self.object, "target_walk_pt", self.target_walk_pt) -- or -2)
	    printf("car <save>: target_walk_pt(%d)", self.target_walk_pt)
	end
end

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
--++++++++++++++++++++-- MOVE SECTION --++++++++++++++++++++--
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--

function action_car:get_nearest_walkpoint()
	return utils.get_nearest_waypoint(self.object, self.st.path_walk,
		 self.path_walk, self.path_walk_count)
end

function action_car:get_next_walkpoint()
	if self.target_walk_pt >= 0 and self.target_walk_pt < self.path_walk_count - 1 then
	    printf("car <move>: action_car:get_next_walkpoint(%d)", self.target_walk_pt + 1)
		return self.target_walk_pt + 1
	elseif self.target_walk_pt == -1 or self.loop then
	    printf("car <move>: action_car:get_next_walkpoint(0)")
		return 0
	end
	printf("car <move>: action_car:get_next_walkpoint(-2)")
	return -2
end

function action_car:at_target_walkpoint()
    printf("car <move>: action_car:at_target_walkpoint(%s)", utils.to_str(self.target_walk_pt))
    if self.target_walk_pt == -2 then return true end

    local curVel = self.car:CurrentVel():magnitude()
	local dist = self.object:position():distance_to(self.path_walk:point(self.target_walk_pt))

	--local arrived = 2 * dist < def_update_time * (curVel.x + self.speed) --def_update_time > 2 * dist / (curVel.x + self.speed)
	--local arrived = dist < curVel.x * def_arriving_factor --8

    -- def_arriving_koef * self.fc_upd_avg / 1000 > 2 * dist / (curVel.x + self.speed)
	local arrived = false --2000 * dist < def_arriving_koef * self.fc_upd_avg * (curVel + self.speed)
	
	if self.fc_upd_avg < 100 then
		arrived = 2000 * dist < def_arriving_koef * self.fc_upd_avg * (curVel + self.speed)
	    printf("car <move>: check formula (%f < %f)", 2000 * dist, def_arriving_koef * self.fc_upd_avg * (curVel + self.speed))
	    if arrived then
	        printf("car <move>: at target pt by formula.")
	    end
	end
	
	if not arrived then
	    local diff_angle = angle_xz(self.object, self.path_walk:point(self.target_walk_pt))
	    printf("car <move>: dist(%f), angle(%f).", dist, diff_angle)
	    arrived = dist < 2 or diff_angle >= math.pi
	    if arrived then
	        --printf("car <move>: at target pt by dist(%f) and angle(%f).", dist, diff_angle)
	        printf("car <move>: at target pt by dist and angle.")
	    end
	end
	
	local diff = dist - self.target_dist
	printf("car <move>: dist = %f, target = %f, diff = %f, curVel = %f, speed = %f", dist, self.target_dist, diff, curVel, self.speed)
	if self.target_dist ~= -1 and not arrived then
	    if diff > 0 then
	        if diff > def_arriving_dist then
	            arrived = true
	            printf("car <move>: at target pt by diff(%f).", diff)
	        end
	    else
	        self.target_dist = dist
	    end
	else
        self.target_dist = dist
	end
--[[
	--printf("car <move>: action_car:at_target_walkpoint(self.target_walk_pt=%d) = %d (arrived = %s)",		self.target_walk_pt, dist, utils.to_str(arrived))
    --if arrived then
        printf("car <move>: arrived = %f > %f, dist(%f), diff(%f), curSpd(%f), spd(%f)",
               --def_arriving_koef * self.fc_upd_avg / 1000,
               --2 * dist / (curVel.x + self.speed),
               def_arriving_koef * self.fc_upd_avg * (curVel + self.speed),
               2000 * dist,
               dist, diff, curVel, self.speed)
        --printf("car <move>: arrived = %f < (%f,%f,%f) * %f, spd(%f)", dist, curVel.x, curVel.y, curVel.z, def_arriving_factor, self.speed)
    --end
--]]
    if arrived then
	    self.target_dist = -1
    end
	return arrived
end

function action_car:go_to_walkpoint(pt)
	--action(self.object, object("left_light", object.activate), cond(cond.time_end, 1000))
	--action(self.object, object("right_light", object.activate), cond(cond.time_end, 1000))
	printf("car <move>: action_car:go_to_walkpoint(%s)", utils.to_str(pt))
	if self.state_delaying then
	    if time_global() - self.delay_time_start >= delay_after_ignition then
	        printf("car <state>: stop delaying after ignition")
	        self.state_delaying = false
	    else
	        printf("car <state>: delaying after ignition")
	        self.state_moving = state_moving_stop
	        return
	    end
	end

	if pt == nil or pt < 0 then
	    if self.state_cannon ~= state_cannon_stop then
	        self:stop_car()
		    self.state_moving = state_moving_end
		end
		printf("car <state>: pt < 0 or pt == nil")
		return
	end

    local diff_angle = angle_xz(self.object, self.path_walk:point(pt))
    local accel = move.none
    local curVel = self.car:CurrentVel():magnitude()
    printf("car <move>: curVel = %f, speed = %f", curVel, self.speed)
    if curVel < self.speed then
        accel = move.fwd
        printf("car <move>: forward")
    elseif curVel > self.speed * 1.15 or diff_angle >= math.pi then
        accel = move.back
        printf("car <move>: backward")
    end
    --printf("car <move>: diff_angle = %f, katet = %f", diff_angle, get_katet(self.object, self.path_walk:point(pt)))
	--if utils.no_need_to_rotate_xz(self.object, self.path_walk:point(pt)) then
	if diff_angle < def_not_rotating_angle then
		if self.state_moving ~= state_moving_fwd and accel ~= move.none then
		    printf("car <move>: no_need_to_rotate. speed = %f, time = %f", self.speed, self.fc_upd_avg)
	   		xr_logic.mob_capture(self.object, true)
			action(self.object,
				move(accel, self.speed), --move.on + 
				--object("right_light", self.headlights),
				--object("left_light", self.headlights),
				cond(cond.time_end, time_infinite)) --self.fc_upd_avg))
			self.state_moving = state_moving_fwd
		end
	else
		-- Поворачиваться нужно, но в какую сторону?
		local rotate_left = utils.angle_left_xz(self.object:direction(), 
		                                        utils.vector_copy_by_val(self.path_walk:point(pt)
		                                                                ):sub(self.object:position()))
		printf("car <move>: must rotate: left? %s. speed = %f, time = %f", utils.to_str(rotate_left), self.speed, self.fc_upd_avg)
		if rotate_left then
			if self.state_moving ~= state_moving_rot_left then 
				xr_logic.mob_capture(self.object, true)
				action(self.object,
					move(accel + move.left, self.speed), --move.on + 
					--object("right_light", self.headlights),
					--object("left_light", self.headlights),
					cond(cond.time_end, time_infinite)) --self.fc_upd_avg))
				self.state_moving = state_moving_rot_left
			end
		else
			if self.state_moving ~= state_moving_rot_right then
				xr_logic.mob_capture(self.object, true)
				action(self.object,
					move(accel + move.right, self.speed), --move.on + 
					--object("right_light", self.headlights),
					--object("left_light", self.headlights),
					cond(cond.time_end, time_infinite)) --self.fc_upd_avg))
				self.state_moving = state_moving_rot_right
			end
		end
	end
end

function action_car:start_car()
    printf("car <move>: car started.")
	xr_logic.mob_capture(self.object, true)
	action(self.object, move(move.on + move.fwd, 100), cond(cond.time_end, 5000))
	--self.speed = 0
	self.state_delaying = true
	self.delay_time_start = time_global()
end

function action_car:stop_car()
    printf("car <move>: car stopping.")
	self.state_delaying = false
	self.speed = 0
	xr_logic.mob_capture(self.object, true)
	action(self.object, move(move.off + move.handbrake, 0), cond(cond.time_end, 3000)) --cond(cond.move_end))
	--self.target_walk_pt = -1
end

function action_car:walk_arrival_callback(index)
	printf("car <move>: action_car:walk_arrival_callback(%d)", index)
	if index == -2 then return end

	local suggested_explode = self.path_walk_info[index]["explode"]
	if suggested_explode == "true" then
--		printf("action_car:walk_arrival_callback(): EXPLODE")
		--self:car_explode()
		self.object.health = 0
		self.car:Action(CCar.eWpnActivate, 0)
	end

	local suggested_spd = self.path_walk_info[index]["spd"]
	if suggested_spd then
		self.speed = tonumber(suggested_spd)
	else
		self.speed = def_moving_speed
	end
	--printf("car <move>: new spd = %f", self.speed)

	local suggested_loop = self.path_walk_info[index]["loop"]
	if suggested_loop == "true" then
		self.loop = true
	else
		self.loop = false
	end

	local suggested_dps = self.path_walk_info[index]["dps"]
	if suggested_dps then
		self.min_delta_per_sec = tonumber(suggested_dps)
	else
		self.min_delta_per_sec = def_min_delta_per_sec
	end

	local suggested_exptm = self.path_walk_info[index]["exptm"]
	if suggested_exptm then
		self.min_car_explode_time = tonumber(suggested_exptm)
	else
		self.min_car_explode_time = def_min_car_explode_time
	end

	local sig = self.path_walk_info[index]["sig"]
	--printf("car <sig>: try")
	if sig then
		--self.st.signals[sig] = true
		self:set_signal(sig)
		--printf("car <sig>: %s", sig)
	end

    if self.hasWeapon and self.state_firetarget == state_firetarget_points then
        local next_idx = self:get_next_walkpoint()
        --local next_idx = self.target_walk_pt
        if next_idx >= 0 then
	        local fw = self.path_walk_info[next_idx]["fw"]
	        if fw then
		        self.fire_wait = if_then_else(fw == "true", true, false)
	        else
	            self.fire_wait = false
	        end

	        local fr = self.path_walk_info[next_idx]["fr"]
	        if fr then
	            if fr == "inf" then
	                self.fire_rep = -1
	            else
	                local c = tonumber(fr)
		            self.fire_rep = if_then_else(c > 0, c, 0)
		        end
	        else
	            self.fire_rep = self.def_fire_rep
	        end
	    else
	        self.fire_wait = false
	        self.fire_rep = self.def_fire_rep
	    end
        printf("car <fire>: fire_wait = %s", if_then_else(self.fire_wait, "true", "false"))
        printf("car <fire>: fire_rep = %d", self.fire_rep)
    end

	-- Выбрать следующую точку езды:
	self.target_walk_pt = self:get_next_walkpoint()
	printf("car <move>: target_walk_pt = %s.", utils.to_str(self.target_walk_pt))
	--if not self.target_walk_pt then
	--    printf("car <move>: stop moving.")
	--    self:stop_car()
		-- Остановить машину
	    --if self.state_cannon == state_cannon_stop then
		--    xr_logic.mob_capture(self.object, true)
		--end
		--action(self.object, move(move.off, 0), cond(cond.time_end, 1000))
		--self.state_moving = state_moving_end
	--    return
	--end

    if self.target_walk_pt >= 0 then
	    -- Если эта точка рядом и уже приехали - не ждать апдейта, чтобы вызвать callback
	    if self:at_target_walkpoint() then
		    self:walk_arrival_callback(self.target_walk_pt)
        else
            --self.target_dist = -1
	        self:go_to_walkpoint(self.target_walk_pt)
	    end
	else
	    printf("car <move>: stop moving.")
	    self.target_walk_pt = -2
	    if self.state_moving ~= state_moving_stop and self.state_moving ~= state_moving_end then
	        self:stop_car()
	        self.state_moving = state_moving_end
	    end
	end
end

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
--++++++++++++++++++++-- FIRE SECTION --++++++++++++++++++++--
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--

function action_car:change_fire_pts()
	printf("car <fire>: action_car:change_fire_pts()")

	self.state_cannon = state_none
	self.state_shooting = state_none
	
	self.fire_pt_arr = {}
	self.fire_pt_count = 0
	self.target_fire_pt_idx = 0
	self.target_fire_pt = nil
	self.fire_rot_dir = 1
	self.fire_start_time = 0

    if not self.path_fire then return end
    
    local fire_idx
    if self.target_walk_pt >= 0 then
	    -- Значение флагов огневых точек, котые будем искать:
	    local fire_flags = self.path_walk_info[self.target_walk_pt].flags
    	
	    if fire_flags:get() == 0 then
		    return
	    end

        local this_val
	    for fire_idx = 0, self.path_fire:count() - 1 do
		    this_val = self.path_fire_info[fire_idx].flags
		    if this_val:equal(fire_flags) then
			    table.insert(self.fire_pt_arr, fire_idx)
			    self.fire_pt_count = self.fire_pt_count + 1
		    end
	    end
	else
	    for fire_idx = 0, self.path_fire:count() - 1 do
			table.insert(self.fire_pt_arr, fire_idx)
	    end
	    self.fire_pt_count = self.path_fire:count()
	end
	
	printf("car <fire>: points")
	print_table(self.fire_pt_arr)

	if self.fire_pt_count == 0 then
	    self.state_cannon = state_cannon_stop
	    return
	end
	
	--if self.fire_pt_count > 0 then
	--    self.target_fire_pt_idx = 0
	--    self.target_fire_pt = self.path_fire:point(self.fire_pt_arr[0])
	--end
end

function action_car:get_next_firepoint()
    if self.fire_pt_count < 1 then return nil end

    printf("car <fire>: action_car:get_next_firepoint(%d,%d,%d)", self.target_fire_pt_idx, self.fire_pt_count, self.fire_rep)
    local pt_idx
    if self.target_fire_pt_idx > 0 then -- not first time
        if self.fire_pt_count > 1 then -- we have at least 2 point to switch
            pt_idx = self.target_fire_pt_idx + self.fire_rot_dir
            if (pt_idx < 1 or pt_idx > self.fire_pt_count) then
                self.fire_rot_dir = -self.fire_rot_dir -- change rotate direction [-1,1]
                printf("car <fire>: changing direction")
                if self.fire_rep == -1 then
                    pt_idx = self.target_fire_pt_idx + self.fire_rot_dir
                elseif self.fire_rep > 0 then
                    pt_idx = self.target_fire_pt_idx + self.fire_rot_dir
                    self.fire_rep = self.fire_rep - 1
                else
                    pt_idx = 0
                end
            end
        elseif self.fire_rep == -1 then -- left same point in the infinity loop
            pt_idx = 1
        elseif self.fire_rep > 0 then -- left same point if loop defined
            pt_idx = 1
            self.fire_rep = self.fire_rep - 1
        end
    else
        pt_idx = 1
    end
    --self.target_fire_pt_idx = pt_idx
    
    printf("car <fire>: pt_idx = %d", pt_idx)

	if pt_idx > 0 then
	    return pt_idx, self.path_fire:point(self.fire_pt_arr[pt_idx])
	end

    self.state_firetarget = state_none
	return pt_idx, nil
end

function action_car:rot_to_firepoint(pt)
--[[
    if pt then
        self.car:SetParam(CCar.eWpnDesiredPos, pt)
    end
    printf("car <fire>: action_car:rot_to_firepoint(%d)", shooting)
    self.car:Action(CCar.eWpnFire, shooting)
--]]
    if self.target_fire_pt then
    --    self.state_cannon = state_cannon_rotate
        self.car:SetParam(CCar.eWpnDesiredPos, self.target_fire_pt)
        printf("car <fire>: action_car:rot_to_firepoint(%d)", self.target_fire_pt_idx)
    --else
    --    self.state_cannon = state_cannon_stop
    end
end

function action_car:set_shooting(shooting)
    self.car:Action(CCar.eWpnFire, shooting)
    printf("car <fire>: action_car:set_shooting(%d)", shooting)
end

function action_car:fire_arrival_callback(cur_index)
    if self.fire_pt_count < 1 or self.state_cannon == state_cannon_stop then return end
    
    printf("car <fire>: action_car:fire_arrival_callback(%s)", utils.to_str(cur_index))
    if self.state_cannon == state_cannon_delay then
        printf("car <fire>: 1.delay mode off.")
	    -- Выбрать следующую точку езды:
	    self.target_fire_pt_idx, self.target_fire_pt = self:get_next_firepoint()

	    if self.target_fire_pt then
            printf("car <fire>: 1.rotate mode on.")
	        self.state_cannon = state_cannon_rotate
		    if self.fire_rot_dir == 1  and self.fire_forward or
		       self.fire_rot_dir == -1 and self.fire_backward then
		        printf("car <fire>: 1.shooting on.")
		        self.state_shooting = state_shooting_on
	        else
		        printf("car <fire>: 1.shooting off.")
		        self.state_shooting = state_none
	        end
	        self:rot_to_firepoint(self.target_fire_pt)
	        self:set_shooting(self.state_shooting)
	    else
            printf("car <fire>: 1.cannon stopped.")
	        self.state_cannon = state_cannon_stop
	        if self.state_shooting ~= state_none then
                self.state_shooting = state_none
                self:set_shooting(self.state_shooting)
                self:set_signal("fire_end")
            end
	    end
	    return
    end

	--printf("car <fire>: action_car: fire_arrival_callback(%s)", utils.to_str(cur_index))
    if cur_index > 0 then
        local idx = self.fire_pt_arr[cur_index]

	    local ff = self.path_fire_info[idx]["ff"]
	    if ff then
		    self.fire_forward = if_then_else(ff == "true", true, false)
		else
		    self.fire_forward = false
	    end

	    local fb = self.path_fire_info[idx]["fb"]
	    if fb then
		    self.fire_backward = if_then_else(fb == "true", true, false)
		else
		    self.fire_backward = false
	    end

	    local f = self.path_fire_info[idx]["f"]
	    if f then
		    self.state_shooting = if_then_else(f == "true", state_shooting_on, state_none)
	    else
		    self.state_shooting = if_then_else(self.st.auto_fire, state_shooting_on, state_none)
	    end

	    local ft = self.path_fire_info[idx]["ft"]
	    if ft then
		    self.fire_time = tonumber(ft)
	    else
		    self.fire_time = self.def_fire_time
	    end
	else
		    self.fire_forward = false
		    self.fire_backward = false
		    self.state_shooting = if_then_else(self.st.auto_fire, state_shooting_on, state_none)
		    self.fire_time = def_min_fire_time
	end
    printf("car <fire>: state_shooting = %s", if_then_else(self.state_shooting ~= 0, "true", "false"))
    --printf("car <fire>: auto_fire = %s", if_then_else(self.st.auto_fire, "true", "false"))
    printf("car <fire>: fire_time = %f", self.fire_time)

	if self.fire_time > 0 then
        printf("car <fire>: 2.delay mode on.")
	    self.state_cannon = state_cannon_delay
        self.fire_start_time = time_global()
        --printf("car <fire>: state_shooting = %s", if_then_else(self.state_shooting ~= 0, "true", "false"))
        --self:rot_to_firepoint(nil)
        self:set_shooting(self.state_shooting)
    else
	    -- Выбрать следующую точку стрельбы:
	    self.target_fire_pt_idx, self.target_fire_pt = self:get_next_firepoint()

	    -- Если мы уже целимся в эту точку - не ждать апдейта, чтобы вызвать callback
	    if self.target_fire_pt then
            printf("car <fire>: 2.rotate mode on.")
		    self.state_cannon = state_cannon_rotate
		    if self.fire_rot_dir == 1  and self.fire_forward or
		       self.fire_rot_dir == -1 and self.fire_backward then
		        self.state_shooting = state_shooting_on
		        printf("car <fire>: 2.shooting on.")
	        else
		        self.state_shooting = state_none
		        printf("car <fire>: 2.shooting off.")
	        end
	        self:rot_to_firepoint(self.target_fire_pt)
	        self:set_shooting(self.state_shooting)
	    else
            printf("car <fire>: 2.cannon stopped.")
	        self.state_cannon = state_cannon_stop
	        if self.state_shooting ~= state_none then
                self.state_shooting = state_none
                self:set_shooting(self.state_shooting)
                self:set_signal("fire_end")
            end
	    end
	end
end

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
--+++++++++++++++++++-- COMMON SECTION --+++++++++++++++++++--
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
function action_car:set_signal(sig)
	local stor = db.storage[self.object:id()]
	stor[stor.active_scheme].signals[sig] = true
	printf("car <sig>: %s", sig)
end

function angle_xz(npc, target_pos)
	local dir1 = npc:direction()
	dir1.y = 0
	local dir2 = utils.vector_copy_by_val(target_pos):sub(npc:position())
	dir2.y = 0
	local dp = dir1:dotproduct(dir2)
	if dp < 0 then
	    return math.pi - yaw(dir1, dir2)
	end
	return yaw(dir1, dir2)
end

function get_katet(npc, target_pos)
	local p = utils.vector_copy_by_val(target_pos):sub(npc:position())
	local q = npc:direction()
	local k = p.x * q.x + p.y * q.y
	if k ~= 0 then
	    return target_pos:distance_to(npc:position()) * (p.x * q.y - p.y * q.x) / k
	end
	return -1
end

function action_car:fastcall()
	if db.storage[self.object:id()].active_scheme ~= "ph_car" then
		-- Если активная схема - не машина, снять быстрый апдейт
		return true
	end
	
	if self.usable ~= nil then
	    local cu = xr_logic.pick_section_from_condlist(db.actor, self.object, self.usable.condlist)
	    local u = (cu == "true" or cu == "")

	    if u then
	        if self.show_tips and self.tip_use then
		        self.object:set_tip_text(self.st.tip_use)
	        end
	    elseif self.show_tips and self.tip_locked then
		    self.object:set_tip_text(self.st.tip_locked)
	    end

	    self.object:set_nonscript_usable(u)

	    return false
	end

	return self:fast_update()
end

function action_car: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
	
	if self.st.invulnerable then
	    self.object.health = self.def_health
	end
end

function action_car:destroy_car()
	printf("car <destroy>: START ===============================")
	self.state_moving = state_moving_end
	self:stop_car()
	self.state_cannon = state_none
	self.state_firetarget = state_none
	self.state_shooting = state_none
	self.car:Action(CCar.eWpnAutoFire, 0)
	self:set_shooting(self.state_shooting)

    -- turn off lights
	xr_logic.mob_capture(self.object, true)
	action(self.object, object("left_light",  object.deactivate), cond(cond.time_end, time_infinite))
	xr_logic.mob_capture(self.object, true)
	action(self.object, object("right_light", object.deactivate), cond(cond.time_end, time_infinite))

	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

-- Вернуть true, если апдейты больше не нужны
function action_car:fast_update()
	printf("car <state>: START FAST UPDATE ======================================================")
	printf("car <state>: check moving1 - [%d][%d]", self.state_moving, self.target_walk_pt)
    --printf("car <state>: action_car:update(): state=%d", self.state_moving)

	--if not self.object:action() then
	--	printf("_pc: CAR EXPLODE")
	--	self.object:explode(0)
	--	self.object:Explode()
	--end
	
	if self.car:GetfHealth() <= 0 then
	    printf("car <state>: killed.")
	    --self:car_explode()
	    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_moving == state_moving_end and
	   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>: stop fast updating. moving(%s) cannon(%s)", utils.to_str(self.state_moving), utils.to_str(self.state_cannon))
			--xr_logic.mob_release(self.object)
	        --if self.st.on_death_info ~= nil then
	        --    printf("car <death>: stopped")
	        --    db.actor:give_info_portion(self.st.on_death_info)
	        --end
	        printf("car <state>: stopped")
	        self:destroy_car()
			return true -- апдейты больше не нужны
		end
		return false
	end

	if self.state_moving ~= state_moving_end and
	   self.state_moving ~= state_moving_stop and 
	   cur_time >= self.last_pos_time + self.min_car_explode_time
	then
	    printf("car <state>: moving(%s)", utils.to_str(self.state_moving))
		if not self.last_pos then
			self.last_pos = self.object:position()
			self.last_pos_time = cur_time
		else
			local cur_pos = self.object:position()
			local diff = self.last_pos:distance_to(cur_pos)
			if diff < self.min_delta_per_sec then
                printf("car <move>: got stuck (%f < %f) - stop.", diff, self.min_delta_per_sec)
                self:stop_car()
				--self:car_explode()
				self.state_moving = state_moving_end
				--return false
			else
			    self.last_pos = cur_pos
			end
			self.last_pos_time = cur_time
		end
	end
	
	printf("car <state>: check moving2 - [%d][%d]", self.state_moving, self.target_walk_pt)
	if self.state_moving ~= state_moving_end and
	   self.state_moving ~= state_none
	then
	    printf("car <move>: try")
	    if self.target_walk_pt >= 0 then
	        if self:at_target_walkpoint() then
	            printf("car <move>: at target point")
	            -- если нет флага ожидания стрельбы или мы уже отстрелялись или пошли в цикле обратно
	            if not self.fire_wait or self.state_cannon == state_cannon_stop then --self.fire_rot_dir == -1 then
	                    --printf("car <move>: car go to the new point.")
		                -- Прибыли, выбрать новую точку
		                self:walk_arrival_callback(self.target_walk_pt)
		                printf("car <move>: new target = %s", utils.to_str(self.target_walk_pt))
		                -- меняем точки отстрела
		                self:change_fire_pts()
	            else
	                printf("car <move>: 1.car stopped.")
	                if self.state_moving ~= state_moving_stop then
	                    self:stop_car()
	                end
	                self.state_moving = state_moving_stop
	            end
	        else
	            printf("car <move>: car keep going.")
		        -- Продолжать ехать, или поворачиваться
		        self:go_to_walkpoint(self.target_walk_pt)
	        end
	    else
	        printf("car <move>: 2.car stopped.")
	        if self.state_cannon ~= state_cannon_stop then
	            self:stop_car()
                self.state_moving = state_moving_stop
	        end
	    end
	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.car: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.car: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
	        if self.fire_pt_count > 0 and self.state_cannon ~= state_cannon_stop then
	            --printf("car <fire>: shooting points")
	            if self.state_cannon == state_cannon_delay then
	                if self.fire_start_time + self.fire_time < cur_time then
	                    --printf("car <fire>: stop delaying(%.2f + %.2f < %.2f)", self.fire_start_time, self.fire_time, time_global())
		                printf("car <fire>: 1.new fire point.")
	                    self:fire_arrival_callback(self.target_fire_pt_idx)
	                else
	                    printf("car <fire>: delaying(%.2f + %.2f < %.2f)", self.fire_start_time, self.fire_time, cur_time)
	                end
	            else
	                if self.car:CanHit() or self.target_fire_pt_idx == 0 then
		                printf("car <fire>: 2.new fire point.")
		                -- Прибыли, выбрать новую точку
		                self:fire_arrival_callback(self.target_fire_pt_idx)
		            end
	            end
	        end
	    elseif self.state_firetarget == state_firetarget_enemy then
	        if self.target_obj:alive() and
	           self.object:position():distance_to_sqr(self.target_obj:position()) <= self.fire_range_sqr and
	           self.car:IsObjectVisible(self.target_obj)
	        then
	            printf("car <fire>: enemy(%s) is visible.", self.target_obj:name())

		        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.6 -- 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.4 -- FAKE
		            else
		                self.target_fire_pt.y = self.target_fire_pt.y + 0.15 -- FAKE
		            end
		        end
		        self:rot_to_firepoint(self.target_fire_pt)

	            if self.car:CanHit() then
	                if self.state_shooting == state_none then
	                    printf("car <fire>: shooting enemy (first).")
		                self.state_shooting = state_shooting_on
		                self:set_shooting(self.state_shooting)
		                --self:set_signal("fire_start")
		            end
	                printf("car <fire>: shooting enemy.")
		        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)
		                --self:set_signal("fire_end")
		            end
	                printf("car <fire>: targeting enemy.")
		        end
		    else
		        if self.state_shooting ~= state_none then
	                printf("car <fire>: enemy isn't visible (first).")
		            self.state_shooting = state_none
		            self:set_shooting(self.state_shooting)
		            --self:set_signal("fire_end")
		        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.4 -- FAKE
		            self:rot_to_firepoint(self.target_fire_pt)
	                printf("car <fire>: target tracking.")
	            end
		    end
	    end
	end
	
	printf("car <state>: check moving3 - [%d][%d]", self.state_moving, self.target_walk_pt)
	printf("car <state>: END FAST UPDATE ========================================================\n")
	return false
end

function action_car:car_explode()
	self.object:explode(time_global())
end

function action_car:net_destroy()
	--self:destroy_car()
	if self.object and xr_logic.mob_captured(self.object) then
	    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 <death>: give info")
	    end
	end
end

---------------------------------------------------------------------------------------------------------------------
function add_to_binder(npc, ini, scheme, section, storage)
--	printf("DEBUG: add_to_binder: scheme='%s', section='%s'", scheme, section)

	local new_action = action_car(npc, storage)

	-- Зарегистрировать все actions, в которых должен быть вызван метод reset_scheme при изменении настроек схемы:
	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.usable          = xr_logic.cfg_get_condlist(ini, section, "usable", npc)
	if st.usable == nil then
	    st.path_walk        = utils.cfg_get_string(ini, section, "path_walk",       npc, false, gulag_name)
	    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)
	    st.fire_time        = utils.cfg_get_number(ini, section, "fire_time",       npc, false, def_min_fire_time)
	    st.fire_rep         = utils.cfg_get_string(ini, section, "fire_repeat",     npc, false, gulag_name, nil)
	    st.fire_range       = utils.cfg_get_number(ini, section, "fire_range",      npc, false, def_fire_range)
	    st.fire_target      = utils.cfg_get_string(ini, section, "target",          npc, false, gulag_name, "points")
	    st.fire_track_target= utils.cfg_get_bool(ini,   section, "track_target",    npc, false, false)

	    st.on_target_vis    = xr_logic.cfg_get_string_and_condlist(ini, section, "on_target_vis", npc)
	    st.on_target_nvis   = xr_logic.cfg_get_string_and_condlist(ini, section, "on_target_nvis", npc)

	    st.path_walk_info   = nil -- Будут инициализированы в reset(), сейчас пути могут быть еще не загружены.
	    st.path_fire_info   = nil
	else
	    st.show_tips    = utils.cfg_get_bool(ini,   section, "show_tips",  npc, false, true)
	    st.tip_use      = utils.cfg_get_string(ini, section, "tip_use",    npc, false, "", "tip_car_use")		
	    st.tip_locked   = utils.cfg_get_string(ini, section, "tip_locked", npc, false, "", "tip_car_locked")
	end

	st.invulnerable     = utils.cfg_get_bool(ini,   section, "invulnerable",    npc, false, false)
	st.headlights       = utils.cfg_get_string(ini, section, "headlights",      npc, false, gulag_name, "off")
	st.on_death_info    = utils.cfg_get_string(ini, section, "on_death_info",   npc, false, gulag_name, nil)
end
