local CONST = require 'config.const'
|
local Balance = require 'config.balance'
|
|
local M = {}
|
|
-- ---------------------------------------------------------
|
-- 刺杀入口
|
-- ---------------------------------------------------------
|
function M.attempt(attacker, target)
|
local cd = attacker:storage_get('kill_cd') or 0
|
if cd > 0 then return false end
|
|
if not attacker:is_in_radius(target, Balance.KILL_RANGE) then return false end
|
|
if attacker:storage_get('is_disguising') then return false end
|
if attacker:storage_get('is_dragging') then return false end
|
if attacker:storage_get('is_attacking') then return false end
|
|
local GameMgr = require 'core.game_manager'
|
if GameMgr.get_state() == 'prepare' then return false end
|
|
local backstab = M.is_backstab(attacker, target)
|
local windup = backstab and Balance.BACKSTAB_WINDUP or Balance.KILL_WINDUP
|
local damage = backstab and Balance.BACKSTAB_DAMAGE or Balance.KILL_DAMAGE
|
|
attacker:play_animation('attack', 1.0, 0, -1, false, true)
|
attacker:storage_set('is_attacking', true)
|
|
clicli.timer.wait(windup, function()
|
attacker:storage_set('is_attacking', false)
|
attacker:stop_cur_animation()
|
|
if not attacker:is_in_radius(target, Balance.KILL_RANGE + 0.5) then
|
M.on_miss(attacker)
|
return
|
end
|
|
if not target:is_alive() then return end
|
|
M.on_hit(attacker, target, damage)
|
end)
|
|
return true
|
end
|
|
-- ---------------------------------------------------------
|
-- 命中处理
|
-- ---------------------------------------------------------
|
function M.on_hit(attacker, target, damage)
|
if target:has_buff_by_key(CONST.BUFF_SHIELD) then
|
target:remove_buffs_by_key(CONST.BUFF_SHIELD)
|
M.set_cooldown(attacker, Balance.KILL_CD_HIT)
|
return
|
end
|
|
attacker:damage({
|
target = target,
|
damage = damage,
|
type = CONST.DAMAGE_TYPE_ASSASSINATE,
|
no_miss = true,
|
})
|
|
local Suspicion = require 'systems.suspicion'
|
local witness_count = M.count_witnesses(attacker, 10)
|
if witness_count > 0 then
|
Suspicion.add(attacker, Balance.SUS_KILL_WITNESSED)
|
else
|
Suspicion.add(attacker, Balance.SUS_KILL_UNWITNESSED)
|
end
|
|
if not target:is_alive() then
|
M.set_cooldown(attacker, Balance.KILL_CD_KILL)
|
M.on_kill(attacker, target)
|
else
|
M.set_cooldown(attacker, Balance.KILL_CD_HIT)
|
end
|
end
|
|
-- ---------------------------------------------------------
|
-- 未命中
|
-- ---------------------------------------------------------
|
function M.on_miss(attacker)
|
local Suspicion = require 'systems.suspicion'
|
Suspicion.add(attacker, Balance.SUS_KILL_MISS)
|
M.set_cooldown(attacker, Balance.KILL_CD_MISS)
|
end
|
|
-- ---------------------------------------------------------
|
-- 击杀后处理
|
-- ---------------------------------------------------------
|
function M.on_kill(killer, victim)
|
local is_silent = killer:has_buff_by_key(CONST.BUFF_SILENCER)
|
|
if victim:has_tag('npc') then
|
victim:add_tag('corpse')
|
victim:set_recycle_on_remove(false)
|
|
if not is_silent then
|
local NpcMgr = require 'core.npc_manager'
|
NpcMgr.trigger_panic(victim:get_point(), Balance.PANIC_RADIUS)
|
end
|
|
if is_silent then
|
killer:remove_buffs_by_key(CONST.BUFF_SILENCER)
|
end
|
|
clicli.timer.wait(Balance.CORPSE_DECAY_TIME, function()
|
if victim:has_tag('corpse') and not victim:has_tag('hidden') then
|
victim:remove()
|
end
|
end)
|
end
|
|
if victim:has_tag('player_controlled') then
|
local PlayerMgr = require 'core.player_manager'
|
PlayerMgr.on_player_killed(killer, victim)
|
end
|
end
|
|
-- ---------------------------------------------------------
|
-- 背刺判定
|
-- ---------------------------------------------------------
|
function M.is_backstab(attacker, target)
|
local atk_point = attacker:get_point()
|
local tgt_point = target:get_point()
|
local tgt_face = target:get_facing()
|
|
local dx = atk_point:get_x() - tgt_point:get_x()
|
local dy = atk_point:get_y() - tgt_point:get_y()
|
local angle_to_attacker = math.deg(math.atan(dy, dx))
|
|
local diff = math.abs(angle_to_attacker - tgt_face)
|
if diff > 180 then diff = 360 - diff end
|
|
return diff > 120
|
end
|
|
-- ---------------------------------------------------------
|
-- 目击者计数
|
-- ---------------------------------------------------------
|
function M.count_witnesses(attacker, radius)
|
local area = clicli.area.create_circle_area(attacker:get_point(), radius)
|
local units = area:get_all_unit_in_area()
|
area:remove()
|
|
local count = 0
|
for _, u in ipairs(units) do
|
if u ~= attacker and u:is_alive() then
|
if u:has_tag('player_controlled') or u:has_tag('npc') then
|
count = count + 1
|
end
|
end
|
end
|
return count
|
end
|
|
-- ---------------------------------------------------------
|
-- 冷却
|
-- ---------------------------------------------------------
|
function M.set_cooldown(unit, duration)
|
unit:storage_set('kill_cd', duration)
|
clicli.timer.wait(duration, function()
|
unit:storage_set('kill_cd', 0)
|
end)
|
end
|
|
return M
|