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