# 战斗与刺杀系统 ## assassination.lua ```lua local CONST = require 'config.const' local Balance = require 'config.balance' local Suspicion = require 'systems.suspicion' 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 -- 准备期不可攻击 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) y3.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 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 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) Suspicion.add(attacker, Balance.SUS_KILL_MISS) M.set_cooldown(attacker, Balance.KILL_CD_MISS) end ----------------------------------------------------------- -- 击杀后处理 ----------------------------------------------------------- function M.on_kill(killer, victim) -- NPC死亡 if victim:has_tag('npc') then victim:add_tag('corpse') victim:set_recycle_on_remove(false) -- 触发区域恐慌 local NpcMgr = require 'core.npc_manager' NpcMgr.trigger_panic(victim:get_point(), Balance.PANIC_RADIUS) -- 30秒后尸体消失 y3.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 -- 夹角 > 120 度 = 攻击者在目标背后 return diff > 120 end ----------------------------------------------------------- -- 目击者计数 ----------------------------------------------------------- function M.count_witnesses(attacker, radius) local area = y3.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) y3.timer.wait(duration, function() unit:storage_set('kill_cd', 0) end) end return M ``` --- ## 消声器物品联动 当玩家持有消声器buff时,击杀不触发恐慌、不发送全局通知: ```lua 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 NpcMgr.trigger_panic(victim:get_point(), Balance.PANIC_RADIUS) end if is_silent then killer:remove_buffs_by_key(CONST.BUFF_SILENCER) end y3.timer.wait(Balance.CORPSE_DECAY_TIME, function() if victim:has_tag('corpse') and not victim:has_tag('hidden') then victim:remove() end end) end -- ... end ```