跳到主要内容

结算结果

HTTP 202 只说明服务端把计划存下来了,动作还没结算。结果要到下一条 state.data.events 里才出现。

先看 event_type,再按对应事件读其他字段:

{
"event_id": "3f360e7e-d9bd-4f48-9a51-5cf751b04075",
"tick": 10583,
"event_type": "UNIT_MOVE_FAILED",
"reason_code": "MOVE_BLOCKED_TERRAIN",
"actor_id": "9d3e4941-2816-4a39-a220-df8cd95e877d",
"position": [120, 85]
}
要查什么去哪里
Unit 自毁Unit 生命周期事件
Core 自毁经济与 Core 事件
HP 恢复恢复事件
Core 伤害、修盾或生产经济与 Core 事件
采集或交付Worker 事件
横扫、射击和伤害战斗事件
Unit 移动或 Core 迁移移动事件
Beacon 动作或重生Beacon 与重生事件

字段规则

字段出现规则和含义
event_id一定会出现。重连后用这个 UUID 去重,避免同一事件处理两次。
tick一定会出现。该结果实际结算的 Tick。
event_type一定会出现。先读它,再看其他可选字段。
reason_code只有该事件存在规定原因时才出现。成功事件不会发一个空字符串。
actor_id产生这个结果的己方 Core 或 Unit,如果存在明确动作主体的话。
target_id受影响的 Core 或 Unit,如果这个结果允许公开目标的话。
position与该结果相关的格子;每一行会说明它具体指哪儿。
values事件专用对象。每行列出的键是稳定的;没有值时整个对象省略。

用不上的可选字段直接不出现,而不是发成 null

Unit 生命周期事件

event_typereason_codeID 与位置values含义
UNIT_SELF_DESTRUCTEDactor_id:被移除的 Unit;position:最后所在格玩家主动在移动和生产定价前移除了这个 Unit。
WORKER_CARGO_DROPPEDactor_id:死亡 Worker;position:最后所在格{amount: int}Worker 的全部 Cargo 已累加到该格资源堆。

自毁也会让该玩家的 units_lost 加 1,但不会产生攻击伤害或摧毁参与。携带 Beacon 的 Unit 还会收到 BEACON_DROPPED_ON_DEATH

恢复事件

event_typereason_codeID 与位置values含义
UNIT_HEAL_SUCCEEDEDactor_id:被恢复的 Unit;position:与 Core 同格坐标{amount: int, hp: int, cost: int}Unit 实际恢复 amount HP,当前 HP 为 hp,并消耗同等数量的 cost
UNIT_HEAL_FAILEDHP_FULLNOT_AT_OWN_CORECORE_MOVINGINSUFFICIENT_RESOURCESactor_id:Unit;position:Unit 格战斗后的 Unit 恢复无法开始;不扣资源。
CORE_HEAL_SUCCEEDEDactor_id:被恢复的 Core;position:Core 格{amount: int, hp: int, cost: int}Core 实际恢复 amount HP,当前 HP 为 hp,并消耗同等数量的 cost
CORE_HEAL_FAILEDHP_FULLINSUFFICIENT_RESOURCESactor_id:Core;position:Core 格战斗后的 Core 恢复无法开始;不扣资源。

因战斗死亡的 Unit 会在恢复阶段前被移除,因此不会产生恢复事件,也不会扣资源。 unit_hp_recoveredcore_hp_recovered 记录玩家生涯实际恢复的 HP 总量。

经济与 Core 事件

event_typereason_codeID 与位置values含义
CORE_DAMAGEDATTACKtarget_id:Core;position:Core 格{damage: int, shield_damage: int, hp_damage: int}Core 受到的战斗总伤害,以及护盾和 HP 各分摊多少。
CORE_DESTROYEDATTACKSELF_DESTRUCTtarget_id:被摧毁的 Core;position:摧毁格ATTACK 存在可命名参与者时为 {destroyed_by: string[]}SELF_DESTRUCT 时无Core 和剩余 Unit 被移除;新的 Core 会在本 Tick 后续阶段立即尝试部署。
CORE_RESOURCE_OVERFLOW_DESTROYEDactor_id:Core;position:Core 格{amount: int, capacity: int}人口下降后,高于新容量的资源被销毁。
CORE_RESOURCES_CAPTUREDactor_id:获胜者存活的 Core;target_id:被摧毁 Core;position:摧毁格{amount: int, available: int, destroyed: int, capacity: int}最高伤害者从受害者 available 库存中实际存入 amount,装不下的 destroyed 被销毁;amount 可以为零。获胜者 Core 同 Tick 也死亡时不产生该事件。
CORE_ACTION_FAILEDCORE_NOT_MOVINGCORE_ALREADY_MOVINGactor_id:Core;position:Core 格对正常 Core 执行了 CANCEL_MOVE,或在迁移期间执行了不兼容的动作。
CORE_REPAIR_FAILEDSHIELD_FULLINSUFFICIENT_RESOURCESactor_id:Core;position:Core 格这一点护盾没能修上。
CORE_REPAIR_SUCCEEDEDactor_id:Core;position:Core 格{shield: int, cost: int}修完之后的护盾值和花掉的资源。
CORE_SPAWN_FAILEDCELL_UNIT_LIMITactor_id:Core;position:Core 格{limit: int}Core 所在格已经到达可占位实体上限。
CORE_SPAWN_FAILEDINSUFFICIENT_RESOURCESactor_id:Core;position:Core 格{required: int}资源低于按战后人口结算出的实际动态价格。
CORE_SPAWN_FAILEDDETERMINISTIC_ID_COLLISIONactor_id:Core;position:Core 格确定性生成 ID 没通过防御性的冲突检查。
CORE_SPAWN_SUCCEEDEDactor_id:Core;target_id:新 Unit;position:Core 格{unit_type: UnitType, cost: int}在 Core 格生产出一个 Unit;cost 是实际扣除的动态价格。

destroyed_by 是按确定性顺序排的参与者用户名。只有攻击造成的摧毁、而且至少能点出 一个参与者时,它才会出现。SELF_DESTRUCT 没有攻击者、伤害、摧毁参与或资源归属; 若同 Tick 敌方攻击已经致死,则战斗摧毁优先。

Worker 事件

event_typereason_codeID 与位置values含义
DEPOSIT_FAILEDWORKER_EMPTYactor_id:Worker;position:Worker 格Worker 身上没货。
DEPOSIT_FAILEDCORE_NOT_PRESENTactor_id:Worker;position:Worker 格己方 Core 不存在,或者不在同一格。
DEPOSIT_FAILEDCORE_MOVINGactor_id:Worker;target_id:Core;position:Worker 格同格的 Core 本 Tick 受迁移限制。
DEPOSIT_FAILEDCORE_RESOURCE_FULLactor_id:Worker;target_id:Core;position:同格坐标{capacity: int}Core 已达到 max(10, population × 5),Cargo 不变。
DEPOSIT_SUCCEEDEDactor_id:Worker;target_id:Core;position:同格坐标{amount: int, capacity: int, remaining: int}amount 已存入 Core,remaining 继续留在 Worker。
HARVEST_FAILEDNOT_RESOURCE_CELLactor_id:Worker;position:Worker 格当前地形不是资源格。
HARVEST_FAILEDCARGO_FULLactor_id:Worker;position:Worker 格Worker 身上已经有资源了。
HARVEST_FAILEDRESOURCE_DEPLETEDactor_id:Worker;position:已消耗的资源点同 Tick 另一个 UUID 更低的合格空载 Worker 赢走了这个点。
HARVEST_SUCCEEDEDactor_id:Worker;position:资源格{amount: int, source: "RESOURCE_NODE" 或 "DROPPED_CARGO"}Worker 从自然点装载资源,或从死亡 Cargo 资源堆回收资源。
BEACON_HARVEST_BONUSactor_id:Worker;position:Worker 格{amount: int}因为持有 Beacon 而多采到的那部分。

每个资源坐标上的合格空载 Worker 按 UUID 原始字节排序,只有最低 UUID 成功并消耗该点。 其他竞争者都收到 RESOURCE_DEPLETED,即使它们的玩家持有 Beacon 也一样。资源补充 不会生成玩家事件;后续完整状态只会在新点可见时暴露它。

回收 DROPPED_CARGO 不会拿走超过资源堆实际剩余量的资源,也不会增加 resources_harvestedbeacon_bonus_resources_harvested

战斗事件

event_typereason_codeID 与位置values含义
SWEEP_RESOLVEDactor_id:Vanguard;position:被横扫的相邻格{targets_hit: int}横扫已结算;命中 0 也是正常结果。
SHOT_MISSED固定为 SHOT_MISSEDactor_id:Ranger;可选 target_id:请求的精准目标 UUID;position:提交的 expected_cell射击动态失败。按格射击落空时没有 target_id;具体原因是故意藏起来的。
SHOT_HITactor_id:Ranger;target_id:被命中的 Core 或 Unit;position:目标格{damage: int}合法射击贡献了伤害。
UNIT_DAMAGEDATTACKtarget_id:受伤的 Unit;position:Unit 所在格{damage: int, hp: int}同时战斗伤害和伤后 HP,最低为 0hp: 0 表示 Unit 已被摧毁。
DESTRUCTION_PARTICIPATIONUNITCOREtarget_id:被摧毁的对象;position:摧毁格你至少对这个对象打出过 1 点伤害。

受害方不会再单独收到一条 UNIT_DESTROYED。判断击杀要靠 UNIT_DAMAGED.values.hp === 0,再结合新的完整状态里这个 Unit 已经不见了。

Ranger 的所有动态失败用的都是同一个 SHOT_MISSED——格子为空、精准目标没了、移开了 或是友军、距离不对、射线被障碍物挡住,全都一样。这个结果就是设计成什么都不透露的。 按格射击落空时不带 target_id;命中时,实际目标在 SHOT_HIT.target_id 中返回。

移动事件

event_typereason_codeID 与位置values含义
UNIT_MOVE_SUCCEEDEDactor_id:Unit;position:终点Unit 完成了一格移动。
UNIT_MOVE_FAILED见下方 Unit 移动原因actor_id:Unit;position:没变的起点Unit 留在原地。
CORE_MOVE_STARTEDactor_id:Core;position:起点{destination: Position, progress: int, required: int}迁移开始;当前值是 progress 1、required 4
CORE_MOVE_PROGRESSactor_id:Core;position:起点{progress: int, required: int}迁移进度往前走了一步,但 Core 还没真的挪。
CORE_MOVE_SUCCEEDEDactor_id:Core;position:终点迁移的最后一个 Tick 把 Core 挪过去了。
CORE_MOVE_FAILED见下方迁移结算原因actor_id:Core;position:没变的起点最终位移失败,Core 恢复成 NORMAL
CORE_MOVE_START_FAILED见下方启动原因actor_id:Core;position:起点START_MOVE 在进入迁移状态前就失败了。
CORE_MOVE_CANCELLEDactor_id:Core;position:起点现有迁移被取消,Core 恢复成 NORMAL

Unit 移动原因:

  • MOVE_OUT_OF_BOUNDS:这一步会让有符号 int64 坐标溢出;
  • MOVE_BLOCKED_TERRAIN:终点是障碍地形;
  • MOVE_CONTESTED:不同所有者争同一个终点;
  • MOVE_SWAP_BLOCKED:两个敌对实体想沿同一条边对换;
  • MOVE_DESTINATION_OCCUPIED:敌方占据者没能成功离开;
  • MOVE_DEPENDENCY_FAILED:依赖的那次离开失败了;
  • CELL_UNIT_LIMIT:终点会超出实体上限。

CORE_MOVE_FAILED 可能是 CORE_DESTINATION_TERRAIN_BLOCKEDMOVE_CONTESTEDMOVE_SWAP_BLOCKEDMOVE_DESTINATION_OCCUPIEDMOVE_DEPENDENCY_FAILEDCELL_UNIT_LIMIT

CORE_MOVE_START_FAILED 可能是 CORE_DESTINATION_OUT_OF_BOUNDSCORE_DESTINATION_TERRAIN_BLOCKEDCORE_DESTINATION_OCCUPIEDCELL_UNIT_LIMIT

Beacon 与重生事件

event_typereason_codeID 与位置values含义
BEACON_PICKUP_FAILEDCORE_MOVINGALREADY_CARRIEDBEACON_NOT_PRESENTactor_id:Core 或 Unit;position:动作主体所在格没能把 Beacon 捡起来。
BEACON_PICKED_UPactor_id:新的携带者;position:拾取格动作主体成了携带者。
BEACON_DROP_FAILEDCORE_MOVINGNOT_BEACON_CARRIERactor_id:Core 或 Unit;position:动作主体所在格没能把 Beacon 放下。
BEACON_DROPPEDactor_id:原携带者;position:放下的格子主动放下成功。
BEACON_DROPPED_ON_DEATHactor_id:被摧毁的携带者;position:死亡格Beacon 自动落到地上。
RESPAWN_DELAYEDNO_LEGAL_SPAWN无 ID、无位置没找到合法的确定性出生候选格,下一个 Tick 再试。
CORE_RESPAWNEDtarget_id:新的 Core;position:出生格{resources: int, workers: int}玩家带着初始资源和 Worker 恢复为 ACTIVE

解析示例

function applyEvent(event) {
switch (event.event_type) {
case 'UNIT_MOVE_FAILED':
markUnitBlocked(event.actor_id, event.position, event.reason_code);
break;
case 'CORE_SPAWN_SUCCEEDED':
registerSpawn(
event.target_id,
event.position,
event.values.unit_type,
);
break;
case 'SHOT_MISSED':
// 协议不会告诉你未命中的具体原因。
break;
}
}

收到新状态后,直接用新的 state.objects 把旧对象换掉。事件的作用是解释新状态是怎么 来的,别把它们当补丁往旧状态上回放。