薛之猫大王
2026-02-22 35593fff68c76413ce82a918e56f127355bf6c55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
local CONST   = require 'config.const'
local Balance = require 'config.balance'
 
local M = {}
 
-- { player_id -> { player, unit, profession, score, respawn_left, alive, team, is_vip } }
local player_data = {}
 
-- ---------------------------------------------------------
-- 初始化所有玩家
-- ---------------------------------------------------------
function M.init_all_players()
    local players = clicli.player_group.get_all_players():pick()
    for _, player in ipairs(players) do
        M.create_player_unit(player)
    end
end
 
-- ---------------------------------------------------------
-- 为单个玩家创建单位
-- ---------------------------------------------------------
function M.create_player_unit(player)
    local spawn = M.get_random_spawn_point()
    local unit  = clicli.unit.create_unit(player, CONST.PLAYER_UNIT_KEY, spawn, math.random(0, 360))
 
    local prof = player:storage_get('selected_profession') or 'shadow_blade'
    local hp   = Balance.PROFESSION_HP[prof] or 100
    unit:set_attr('最大生命', hp, '基础')
    unit:set_hp(hp)
    unit:set_attr('移动速度', Balance.BASE_MOVE_SPEED, '基础')
    unit:set_bar_cnt(2)
    unit:set_pkg_cnt(0)
 
    -- 初始伪装:随机NPC外观
    local model = M.get_random_npc_model()
    if model then
        unit:replace_model(model)
    end
 
    unit:add_tag('player_controlled')
    unit:storage_set('current_model',  model)
    unit:storage_set('previous_model', model)
    unit:storage_set('suspicion', 0)
    unit:storage_set('profession', prof)
    unit:storage_set('idle_time', 0)
    unit:storage_set('kill_cd', 0)
 
    local abi_key = CONST.PROFESSION_ABILITY[prof]
    if abi_key and abi_key ~= 0 then
        unit:add_ability('英雄', abi_key, 0, 1)
    end
 
    player_data[player:get_id()] = {
        player       = player,
        unit         = unit,
        profession   = prof,
        score        = 0,
        respawn_left = Balance.RESPAWN_COUNT,
        alive        = true,
        team         = nil,
        is_vip       = false,
    }
 
    return unit
end
 
-- ---------------------------------------------------------
-- 玩家死亡处理
-- ---------------------------------------------------------
function M.on_player_killed(killer_unit, victim_unit)
    local victim_pd = M.get_data_by_unit(victim_unit)
    local killer_pd = M.get_data_by_unit(killer_unit)
 
    if not victim_pd then return end
    victim_pd.alive = false
 
    if killer_pd then
        killer_pd.score = killer_pd.score + Balance.SCORE_KILL_PLAYER
    end
    victim_pd.score = victim_pd.score + Balance.SCORE_DEATH
 
    victim_unit:add_tag('corpse')
    victim_unit:set_recycle_on_remove(false)
 
    local mode = require 'core.mode_manager'
    if mode.can_respawn(victim_pd) then
        victim_pd.respawn_left = victim_pd.respawn_left - 1
        clicli.timer.wait(Balance.RESPAWN_TIME, function()
            M.respawn(victim_pd)
        end)
    end
 
    mode.check_victory()
end
 
-- ---------------------------------------------------------
-- 复活
-- ---------------------------------------------------------
function M.respawn(pd)
    local spawn = M.get_random_spawn_point()
    pd.unit:reborn(spawn)
    pd.unit:set_hp(pd.unit:get_attr('最大生命'))
 
    local model = M.get_random_npc_model()
    if model then
        pd.unit:replace_model(model)
        pd.unit:storage_set('current_model', model)
    end
    pd.unit:storage_set('suspicion', 0)
    pd.alive = true
end
 
-- ---------------------------------------------------------
-- 积分
-- ---------------------------------------------------------
function M.add_score(player, delta)
    local pd = player_data[player:get_id()]
    if pd then
        pd.score = pd.score + delta
    end
end
 
-- ---------------------------------------------------------
-- 查询
-- ---------------------------------------------------------
function M.get_data_by_unit(unit)
    for _, pd in pairs(player_data) do
        if pd.unit == unit then return pd end
    end
    return nil
end
 
function M.get_all_player_units()
    local list = {}
    for _, pd in pairs(player_data) do
        table.insert(list, pd)
    end
    return list
end
 
function M.get_alive_count(camp)
    local n = 0
    for _, pd in pairs(player_data) do
        if pd.alive then
            if not camp or pd.unit:get_camp_id() == camp then
                n = n + 1
            end
        end
    end
    return n
end
 
function M.get_alive_count_by_team(team_name)
    local n = 0
    for _, pd in pairs(player_data) do
        if pd.alive and pd.team == team_name then
            n = n + 1
        end
    end
    return n
end
 
function M.get_rankings()
    local list = M.get_all_player_units()
    table.sort(list, function(a, b) return a.score > b.score end)
    for i, pd in ipairs(list) do
        pd.rank = i
    end
    return list
end
 
-- ---------------------------------------------------------
-- 工具
-- ---------------------------------------------------------
function M.get_random_spawn_point()
    local spawns = clicli.area.get_circle_areas_by_tag('player_spawn')
    if #spawns == 0 then
        return clicli.point.create(0, 0)
    end
    local area = spawns[math.random(#spawns)]
    return area:get_center_point()
end
 
function M.get_random_npc_model()
    local pool = CONST.NPC_MODEL_POOL
    if #pool == 0 then return nil end
    return pool[math.random(#pool)]
end
 
return M