跳到主要内容

结算结果

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]
}
要查什么去哪里
维护费、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

经济与 Core 事件

event_typereason_codeID 与位置values含义
UPKEEP_PAIDactor_id:Core;position:Core 格{due: int, paid: int, deficit: int}维护费已扣;deficit 为正时随后作为 Core 伤害应用。
CORE_DAMAGEDATTACKUPKEEP_DEFICITtarget_id:Core;position:Core 格{damage: int, shield_damage: int, hp_damage: int}Core 受到的总伤害,以及护盾和 HP 各分摊多少。
CORE_DESTROYEDATTACKUPKEEP_DEFICITtarget_id:被摧毁的 Core;position:摧毁格攻击且存在可命名参与者时为 {destroyed_by: string[]},否则无Core 和剩余 Unit 被移除,玩家开始等待重生。
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}资源不够所选 Unit 的价格。
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。

destroyed_by 是按确定性顺序排的参与者用户名。只有攻击造成的摧毁、而且至少能点出 一个参与者时,它才会出现。

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_SUCCEEDEDactor_id:Worker;target_id:Core;position:同格坐标{amount: int}Worker 身上的货全部转进 Core 的资源。
HARVEST_FAILEDNOT_RESOURCE_CELLactor_id:Worker;position:Worker 格当前地形不是资源格。
HARVEST_FAILEDCARGO_FULLactor_id:Worker;position:Worker 格Worker 身上已经有资源了。
HARVEST_SUCCEEDEDactor_id:Worker;position:Worker 格{amount: int}资源已经装进 Worker 的 cargo。
BEACON_HARVEST_BONUSactor_id:Worker;position:Worker 格{amount: int}因为持有 Beacon 而多采到的那部分。

战斗事件

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射击动态失败,具体原因是故意藏起来的。
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——目标没了、目标移开了、目标是 友军、距离不对、射线被挡,全都一样。这个结果就是设计成什么都不透露的。

移动事件

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 把旧对象换掉。事件的作用是解释新状态是怎么 来的,别把它们当补丁往旧状态上回放。