玩家:状态机、购买、伤害与死亡
CCSPlayer(game/server/cstrike15/cs_player.cpp,16,549 行)是服务端玩家实体,承载了 CS:GO 的大部分个人规则:选队、出生、购买、金钱、受伤与死亡、观察者模式。客户端对应 C_CSPlayer,主要负责表现与预测。本页沿“加入 → 出生 → 购买 → 受伤 → 死亡 → 观战 → 重生”的生命周期展开。
相关页面:CS:GO 总览 · 规则与回合 · 武器与射击 · 物品与经济 · HUD · 玩家外围系统(观战、接管 Bot、教练、投票、成就、MVP、人质)
相关源码文件#
game/server/cstrike15/cs_player.cpp、game/server/cstrike15/cs_player.hgame/shared/cstrike15/cs_player_shared.cpp、game/shared/cstrike15/cs_shareddefs.hgame/client/cstrike15/c_cs_player.cpp
1. 玩家状态机#
证据:
- 状态枚举:
game/shared/cstrike15/cs_shareddefs.h:225-246。 - 状态表把每个状态映射到 Enter/Leave/PreThink 回调;
STATE_DORMANT没有任何回调:game/server/cstrike15/cs_player.cpp:10555-10578。State_Transition = Leave + Enter,State_PreThink每帧调用当前状态的 PreThink:game/server/cstrike15/cs_player.cpp:10511-10552。 - 主要转移点:初次进入服务器时进入
STATE_WELCOMEgame/server/cstrike15/cs_player.cpp:1271;客户端关闭 MOTD 发送joingame后,非匹配模式进入选队game/server/cstrike15/cs_player.cpp:9092-9128;选队后不能立即重生则进入观察者game/server/cstrike15/cs_player.cpp:10063-10072;死亡进入DEATH_ANIMgame/server/cstrike15/cs_player.cpp:2755;禁止自由观战时直接进入DEATH_WAIT_FOR_KEYgame/server/cstrike15/cs_player.cpp:10685-10692;冻结镜头结束后进入军备竞赛重生或观察者game/server/cstrike15/cs_player.cpp:10761-10772;回合重生回到ACTIVEgame/server/cstrike15/cs_player.cpp:6636-6641;军备竞赛重生game/server/cstrike15/cs_player.cpp:11002-11013;Bot 被接管时休眠game/server/cstrike15/cs_player.cpp:15561。
2. 出生与重生#
Spawn清理命令限速、进度条、创建视图模型并选择玩家模型等:game/server/cstrike15/cs_player.cpp:1330-1345;GiveDefaultItems发放默认装备:game/server/cstrike15/cs_player.cpp:1911。- 每回合由规则对象调用
RoundRespawn(观察者调用ObserverRoundRespawn),军备竞赛模式下还会重置进阶状态:game/server/cstrike15/cs_player.cpp:6577-6600、game/shared/cstrike15/cs_gamerules.cpp:8500-8520。
3. 购买#
证据:
ClientCommand的buy分支:game/server/cstrike15/cs_player.cpp:9152-9165。HandleCommand_Buy:名称为空时按装备栏位查定义;成功后记录统计、触发item_purchase事件;死亡竞赛模式下买完立即补钱:game/server/cstrike15/cs_player.cpp:7683-7722。HandleCommand_Buy_Internal:别名翻译、库存查询、防止通过“buy <武器名>”绕过装备栏位、CanPlayerBuy与CanAcquire检查、余额检查后丢弃同槽武器:game/server/cstrike15/cs_player.cpp:7724-7882;按目标装备栏位取物品,竞技模式(非热身)使用开局缓存的装备,保证整场比赛装备一致:game/server/cstrike15/cs_player.cpp:7908-7960;扣钱:game/server/cstrike15/cs_player.cpp:8002-8008。CanPlayerBuy:必须在购买区、热身最后 3 秒不能买、队伍禁购、购买时间已过等都会拒绝并提示:game/shared/cstrike15/cs_player_shared.cpp:889-990。IsInBuyZone在mp_buy_anywhere为 1 或等于本队时恒为真:game/shared/cstrike15/cs_player_shared.cpp:218-225。
观察:
HandleCommand_Buy_Internal在声明字符数组itemNameUsed后,未对其赋值就在弹药购买分支中与"primammo"/"secammo"比较:game/server/cstrike15/cs_player.cpp:7726、game/server/cstrike15/cs_player.cpp:7761-7771。这看起来是读取未初始化缓冲区的缺陷;实际影响取决于栈内容,本次未验证。
4. 金钱#
AddAccount:被玩家接管的 Bot 不加钱;经典模式中回合进行时赚到的钱额外记入 m_iAccountMoneyEarnedForNextRound;可记录收支统计;最终把余额钳制在 [0, GetMaxMoney()],并向本人发送 CS_UM_AdjustMoney 用户消息:game/server/cstrike15/cs_player.cpp:5990-6038。余额本身是网络变量 m_iAccount(16 位无符号),客户端 HUD 同时监听该消息与网络变量:game/server/cstrike15/cs_player.cpp:478、game/client/cstrike15/Scaleform/HUD/sfhudmoney.cpp:19-20。个人奖励通过 AddAccountAward 发放:game/server/cstrike15/cs_player.cpp:5828-5835。
回合中赚到的钱何时可用(审计 U7):
| 环节 | 行为 | 证据 |
|---|---|---|
| 记入 | 仅在经典模式、非热身、非冻结期且金额为正时,把收入同时累加到 m_iAccountMoneyEarnedForNextRound(钳制到最大金钱) | game/server/cstrike15/cs_player.cpp:6000-6005 |
| 消费检查 | BAttemptToBuyCheckSufficientBalance 用“余额 − 该值”与价格比较;不够且原因是这部分钱被冻结时提示 #Not_Enough_Money_NextRound | game/server/cstrike15/cs_player.cpp:7437-7452 |
| 适用范围 | 护甲、拆弹器、夜视仪与武器购买都经过该检查 | game/server/cstrike15/cs_player.cpp:7464、game/server/cstrike15/cs_player.cpp:7615、game/server/cstrike15/cs_player.cpp:7648、game/server/cstrike15/cs_player.cpp:7863、game/server/cstrike15/cs_player.cpp:7996 |
| 解冻 | 每名玩家 RoundRespawn 时清零;冻结期结束时 CheckFreezePeriodExpired 对所有玩家再清零一次 | game/server/cstrike15/cs_player.cpp:6662-6663、game/shared/cstrike15/cs_gamerules.cpp:10485-10491 |
| 其他清零 | ResetAccount、InitializeAccount;接管 Bot 时随 m_PreControlData 保存与恢复 | game/server/cstrike15/cs_player.cpp:5769-5787、game/server/cstrike15/cs_player.cpp:15409、game/server/cstrike15/cs_player.cpp:15726 |
结论:冻结期结束后(例如购买时间长于冻结时间、或 mp_buy_anywhere 时)因击杀、回合结束奖金等获得的钱,在本回合剩余时间内不能用于购买,到下一回合重生时解冻;冻结期内获得的钱不受此限制。接管 Bot 时的金钱规则见 玩家外围系统。
5. 受伤#
伤害分两步:
- 命中部位倍率(
TraceAttack):头部 ×4 再乘头部缩放,胃部 ×1.25,腿部 ×0.75,其余 ×1,并据此施加受击抖动:game/server/cstrike15/cs_player.cpp:5426、game/server/cstrike15/cs_player.cpp:5510-5522、game/server/cstrike15/cs_player.cpp:5570-5573、game/server/cstrike15/cs_player.cpp:5609-5613。 - 伤害结算(
OnTakeDamage):未开友伤时队友伤害为 0,冻结期内队友伤害也为 0;区分枪弹、HE 手雷、火焰伤害来源;护甲按flArmorRatio/flArmorBonus吸收:game/server/cstrike15/cs_player.cpp:4720-4790。存活玩家的后续处理在OnTakeDamage_Alive:game/server/cstrike15/cs_player.cpp:2389。
子弹如何产生这些伤害见 武器与射击。
6. 死亡#
Event_Killed 计算布娃娃受力(含布娃娃磁铁)、生成布娃娃并进入 STATE_DEATH_ANIM:game/server/cstrike15/cs_player.cpp:2744-2756;随后调用规则对象的 CheckWinConditions——非军备竞赛类模式总是调用;IsPlayingGunGame() 为真时先记录上回合重购信息,只有在“击杀者不是自己(非自杀)或本人不能立即重生”时才调用:game/server/cstrike15/cs_player.cpp:2784-2806。