CD Code Digest Hub /CS:GO

玩家:状态机、购买、伤害与死亡

13 分钟阅读 8 个章节 2 张图

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.h
  • game/shared/cstrike15/cs_player_shared.cpp、game/shared/cstrike15/cs_shareddefs.h
  • game/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_WELCOME game/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_ANIM game/server/cstrike15/cs_player.cpp:2755;禁止自由观战时直接进入 DEATH_WAIT_FOR_KEY game/server/cstrike15/cs_player.cpp:10685-10692;冻结镜头结束后进入军备竞赛重生或观察者 game/server/cstrike15/cs_player.cpp:10761-10772;回合重生回到 ACTIVE game/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_NextRoundgame/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. 受伤#

伤害分两步:

  1. 命中部位倍率(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。
  2. 伤害结算(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。

约束与边界#

  • 观察者模式与死亡后视角、玩家接管 Bot(m_hControlledBot)、教练模式、投票、成就、统计、MVP 与人质营救见 玩家外围系统;刀、电击枪等非普通枪械见 特殊武器机制。
  • STATE_DEATH_WAIT_FOR_KEY(mp_forcecamera 2 时的黑屏等待)之后的转移、军备竞赛与死亡竞赛的重生细节未展开。
  • “冻结期结束后赚的钱本回合不可用”只适用于 IsPlayingClassic() 为真的模式;其余模式下 AddAccount 不记入该字段。