feedback5 WM: merge feedback4 at 13808e3 (W4A) — 高台 for every 「可以放置于远程位」 trait holder, the 5-阿戈尔 revive slots, the 联防 devour of whoever stands in front
Ported by hand into the split layout: -83fc408board.js placeClass: the player's loadout no longer counts, as in83fc408. feedback5's 补位 rule stays: a chess the player fields as its stand-in is placed by the stand-in's record (ps.fieldRecord). positionClass(rec) takes no module. -83fc408client piecePosition → public/js/ui/gameLogic/placement.js: meleeOnHighGround(rec) on the stand-in or chess record, no module. The gameLogic.js shim header and deployedModuleId's comment follow; deployedModuleId is kept, still exported and tested. -83fc408PlayerState._placementOf comment → server/match/player/placement.js. -83fc408tools/golden.mjs layout: positionClass without the module, on feedback5's stand-in body (recOf) and in diyLayout. - bonds/core.js (6ab9a06,8a2e0c7) applied cleanly; it equals 13808e3's plus feedback5's i18n toast. shared/highGround.js equals 13808e3's. - data/: only bonds.json (the 阿戈尔 spec text). node tools/build-data.mjs --offline on the merged tree rewrites all 15 files byte-identical. Golden regenerated: 16 scenarios moved, all by83fc408. Each fields 歌蕾蒂娅 (normal, or elite with HOK-X / no module), whom tools/golden.mjs now lays out beside the enemy path like a ranged operator instead of on the busiest melee tile: roster-010, roster-012, roster-029, roster-040, roster-041; bond-deputShip-high, bond-indomShip-low, bond-preciShip-low, bond-preciShip-high, bond-suntShip-low, bond-swiftShip-high; boss-boss_2-pair, boss-boss_4-pair, boss-boss_6-pair, hidden-boss_8-pair, hidden-boss_10-pair. This is the list in 83fc408's own message. With the 阿戈尔 commits reverted the digests are the same, so those two move nothing in this corpus. The standins, diy and matches families are unchanged: no stand-in or 自选 piece there is 歌蕾蒂娅, 崖心 or 见行者.
This commit is contained in:
commit
c5c52eed34
26 files changed
+1095
-871
No files matched your search
+26
-27
@@ -4,8 +4,8 @@
|
||||
// stage legend (data/stages.json `tiles[glyph]`: height + buildable) and the stage devices:
|
||||
// 'melee' — LOW tile with buildable ALL/MELEE, or a 深水区 under an active 特制水上平台 (waterPlatform, "在水上建立可以
|
||||
// 部署任意单位的平台"): melee AND ranged chess may stand here ("所有行动内远程干员可部署在近战位").
|
||||
// 'ranged' — HIGH tile with buildable ALL/RANGED, or a tile under an active platform (射击台): ranged only (and elite
|
||||
// 歌蕾蒂娅 carrying HOK-Y, placeClass below).
|
||||
// 'ranged' — HIGH tile with buildable ALL/RANGED, or a tile under an active platform (射击台): ranged only (and the
|
||||
// melee chess whose trait reads 「可以放置于远程位」, positionClass below).
|
||||
// (absent) — not deployable (NONE, forbidden, road lanes, tiles under active crates/mounds, the 深水区 — the legend's
|
||||
// `buildable` is the effective type: tile_deepsea refuses deployment, PRTS 深水区 地形信息
|
||||
// "拒绝部署(待补充)", although its level buildableType is ALL; player report #3 after 0.1.0, 战场#08's
|
||||
@@ -23,10 +23,10 @@
|
||||
// (10–12, 8), which are '#' on the normal field. The result equals stages[id].deployTiles.bossLeft / bossRight
|
||||
// mapped to board coordinates (test/match/playtest5-deploy.test.js).
|
||||
//
|
||||
// Chess follow their `position` (MELEE ⇒ melee tiles, RANGED ⇒ any deployable), except elite 歌蕾蒂娅 carrying module
|
||||
// HOK-Y 淡金坠饰 (uniequip_003_glady: `placeClass` / shared/highGround.js ⇒ any deployable tile). The branch trait
|
||||
// 「可以放置于远程位」 is not read (owner's decision 2026-10-04, reversing DESIGN §22.6): 崖心, 见行者, a normal record,
|
||||
// any other module and no module are ground-only. The equipped module is the player's loadout at place time.
|
||||
// Chess follow their `position` (MELEE ⇒ melee tiles, RANGED ⇒ any deployable), except a MELEE chess whose trait reads
|
||||
// 「可以放置于远程位」 — the 钩索师 / 推击手 branch trait: 歌蕾蒂娅, 崖心, 见行者, normal and elite, any module or none
|
||||
// (shared/highGround.js ⇒ any deployable tile; the owner's decision of 2026-10-05, following PRTS). The loadout does
|
||||
// not change the class.
|
||||
// Tokens follow their own `position` (ALL ⇒ any deployable tile, MELEE ⇒ melee tiles, RANGED ⇒ any deployable).
|
||||
// A token whose text reads "只能部署在召唤者攻击范围内" (tokens.json `ownerRange`: the tacticians' 援军 — 伺夜's 狼群,
|
||||
// 缪尔赛思's 流形; PRTS 狼群 特性) also needs a tile of its owner's attack range: `ownerRangeKeys` = the owner's range
|
||||
@@ -135,41 +135,40 @@ export function buildDeployMap(stage, { deviceOverrides = {}, tileOverrides = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Placement class of a chess / token record: 'melee' | 'ranged' | 'all'. Widened to 'all' only for elite 歌蕾蒂娅
|
||||
* carrying HOK-Y (`moduleId` = uniequip_003_glady, shared/highGround.js): she may stand on the ranged (高台) tiles.
|
||||
* Any other module, no module, and every other MELEE record stay 'melee'. `chess.json` has no `placement` field.
|
||||
* Placement class of a chess / token record: 'melee' | 'ranged' | 'all'. A MELEE chess whose trait reads
|
||||
* 「可以放置于远程位」 (shared/highGround.js: 歌蕾蒂娅, 崖心, 见行者, normal and elite) is widened to 'all': it may also
|
||||
* stand on the ranged (高台) tiles. Every other MELEE record stays 'melee'. `chess.json` has no `placement` field.
|
||||
* @param {object|null} rec
|
||||
* @param {string|null} [moduleId] equipped module; ignored for tokens and non-golden records
|
||||
*/
|
||||
export function positionClass(rec, moduleId = null) {
|
||||
return meleeOnHighGround(rec, moduleId) ? 'all' : basePositionClass(rec);
|
||||
export function positionClass(rec) {
|
||||
return meleeOnHighGround(rec) ? 'all' : basePositionClass(rec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Placement class of a record under a player's loadout (the module `ps.loadoutFor` resolves, defaults included).
|
||||
* Tokens and records without a chess id ignore the loadout. A chess the player fields as its stand-in (0.2.0 补位,
|
||||
* `ps.fieldRecord`) is placed by the stand-in's position — the body that is deployed (圣约送葬人's 预备干员-先锋 stands on
|
||||
* the ground, 塞雷娅's Touch anywhere) [ASSUMED: the official deploy check reads the fielded character].
|
||||
* @param {{ loadoutFor?: (rec: object) => { moduleId?: string|null }|null, fieldRecord?: (rec: object) => object }|null} ps
|
||||
* Placement class of a record on a player's board — the server's single entry point for pieces (g.move, swaps, merges,
|
||||
* the audit, the invariants, the bot). Since the owner's decision of 2026-10-05 the player's loadout no longer changes
|
||||
* it (any module, or none). A chess the player fields as its stand-in (0.2.0 补位, `ps.fieldRecord`) is placed by the
|
||||
* stand-in's position — the body that is deployed (圣约送葬人's 预备干员-先锋 stands on the ground, 塞雷娅's Touch
|
||||
* anywhere) [ASSUMED: the official deploy check reads the fielded character]; tokens and records without a chess id
|
||||
* are their own body.
|
||||
* @param {{ fieldRecord?: (rec: object) => object }|null} ps
|
||||
* @param {object|null} rec
|
||||
*/
|
||||
export function placeClass(ps, rec) {
|
||||
if (!rec || !rec.chessId || !ps || typeof ps.loadoutFor !== 'function') return positionClass(rec);
|
||||
let moduleId = null;
|
||||
let body = rec;
|
||||
if (!rec || !rec.chessId || !ps || typeof ps.fieldRecord !== 'function') return positionClass(rec);
|
||||
let body;
|
||||
try {
|
||||
const lo = ps.loadoutFor(rec);
|
||||
moduleId = lo && typeof lo.moduleId === 'string' ? lo.moduleId : null;
|
||||
if (typeof ps.fieldRecord === 'function') body = ps.fieldRecord(rec) || rec;
|
||||
body = ps.fieldRecord(rec) || rec;
|
||||
} catch {
|
||||
moduleId = null;
|
||||
body = rec;
|
||||
}
|
||||
return positionClass(body, moduleId);
|
||||
return positionClass(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* The record's own position class ('melee' | 'ranged' | 'all'), without the placement widening — the class the battle
|
||||
* plays (a MELEE operator still blocks on a ground tile; on a 高台 it blocks nothing): the bots' blocker test and the
|
||||
* tiles they plan for it (bot.js).
|
||||
* plays (a MELEE operator still blocks on a ground tile; on a 高台 it blocks nothing): the bots' blocker test and their
|
||||
* 高台 preference for a widened melee chess (bot.js isBlocker / planLayoutSteps).
|
||||
*/
|
||||
export function basePositionClass(rec) {
|
||||
const p = rec && typeof rec.position === 'string' ? rec.position.toUpperCase() : 'ALL';
|
||||
|
||||
+7
-4
@@ -34,8 +34,10 @@
|
||||
// covering units against the round's DEF / RES, blocker hold time, flyers only for anti-air) is maximized
|
||||
// greedily — blockers first, then damage dealers by DPS, then healers — over every
|
||||
// (legal tile, direction) pair of the server's deploy map (no 深水区). A MELEE operator is planned on the ground
|
||||
// tiles, where it blocks — except elite 歌蕾蒂娅 carrying HOK-Y, who may also take a 高台 (placeClass; owner's
|
||||
// decision 2026-10-04). She stays a blocker in the lineup (basePositionClass). Each unit's range grid — the one it is
|
||||
// tiles, where it blocks — except one whose trait reads 「可以放置于远程位」 (歌蕾蒂娅, 崖心, 见行者, any module:
|
||||
// placeClass 'all', shared/highGround.js), which takes a 高台 whose range covers the enemy road, else the ground
|
||||
// (owner 2026-10-04 for the 高台 preference, 2026-10-05 for who may use one). It stays a blocker in the lineup
|
||||
// (basePositionClass). Each unit's range grid — the one it is
|
||||
// deployed with, rangeRec (loadoutRecord attackRangeGrid) — is rotated per direction (DESIGN §3; RIGHT is tried first
|
||||
// and kept on ties, so symmetric ranges and melee units whose front adds nothing stay facing the gates), so
|
||||
// ranged units turn toward the enemy path tiles they cover best and blockers toward the road; on 气流 tiles
|
||||
@@ -951,8 +953,9 @@ export function* planLayoutSteps(m, ps, pieces, params = LAYOUT_PARAMS, { occupi
|
||||
let bestV = -Infinity;
|
||||
// a "只能部署在召唤者攻击范围内" summon (伺夜's 狼群, 缪尔赛思's 流形): only the tiles of its owner's range
|
||||
const within = p.kind === 'token' && typeof ps.summonRange === 'function' ? ps.summonRange(p) : null;
|
||||
// ground tiles for a MELEE blocker. placeClass 'all' is only elite 歌蕾蒂娅 + HOK-Y: she may stand on a 高台,
|
||||
// and when one of those tiles covers the enemy road she is planned there (owner 2026-10-04: the bot uses the 高台).
|
||||
// ground tiles for a MELEE blocker. placeClass 'all' on a MELEE record is a chess whose trait reads 「可以放置于远程位」
|
||||
// (歌蕾蒂娅, 崖心, 见行者, any module): it may stand on a 高台, and when one of those tiles covers the enemy road it is
|
||||
// planned there even with the ground free (owner 2026-10-04: the bot uses the 高台; 2026-10-05: every such chess).
|
||||
const cls = p.kind === 'token' ? basePositionClass(r0) : placeClass(ps, m.gd.chess(p.id) || r0);
|
||||
const preferHigh = cls === 'all' && basePositionClass(r0) === 'melee';
|
||||
let bestHigh = null;
|
||||
|
||||
@@ -37,7 +37,8 @@ export class PlayerPlacement {
|
||||
|
||||
_placementOf(piece) {
|
||||
const rec = piece.kind === 'token' ? this.gd.token(piece.id) : this.gd.chess(piece.id);
|
||||
// elite 歌蕾蒂娅 + HOK-Y may use a 高台; the module is this player's loadout (owner's decision 2026-10-04)
|
||||
// a melee chess whose trait reads 「可以放置于远程位」 may use a 高台, whatever its module (shared/highGround.js); a
|
||||
// stand-in is placed by its own record (placeClass)
|
||||
return placeClass(this, rec);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
// 拉特兰 lateranoShip member ammo skills start with floor(ammo × (1 + base_ammo_percent + ammo_percent_per_stack·L));
|
||||
// 6: every ammo used by a member → all members ATK +atk_per_consume (≤ max_atk_for_consume)
|
||||
// 阿戈尔 egirShip members max HP +(base_max_hp + max_hp_per_stack·L) (直接乘算); battle start devour (see devour());
|
||||
// 5: the first max_free_respawn_cnt members by position (the devour's order, fixed at battle
|
||||
// start) each redeploy at once (free) on their first knock-out
|
||||
// 5: the first max_free_respawn_cnt members knocked out (in knock-out order, the devour's food
|
||||
// included) each redeploy at once (free) on that first knock-out
|
||||
// 叙拉古 siracusaShip every member deployment: ASPD +(base + per·L) for (base_duration + per·L) s; 6: 隐匿 for the same
|
||||
// time, and while hidden / end_duration s after, every 普通伤害 hit (siracusaRolls) procs (PRD,
|
||||
// nominal `prob`) base_damage + damage_per_stack·L true damage + fear `fear` s
|
||||
@@ -34,7 +34,7 @@
|
||||
// Two players in one field: everything is per player (own members, own deployments); the 谢拉格 wind and 炎佑 act on
|
||||
// every enemy of the field (debuffs on shared enemies help both, research 06 §8.6).
|
||||
// Hook priorities: 拉特兰 skillStart −10 (after kits' own ammo changes), 阿戈尔 death 11 (5-tier revive, before 不屈's
|
||||
// death 10), layerGain −100 (after 魔王-style modifiers).
|
||||
// death 10; every `fatal` saver runs before any death hook), layerGain −100 (after 魔王-style modifiers).
|
||||
|
||||
import * as S from '../support/index.js';
|
||||
import { mitigate } from '../../damage.js';
|
||||
@@ -334,15 +334,14 @@ function installLaterano(battle, pid, bb, members) {
|
||||
|
||||
// "更靠左和靠上": left first (on the player's own board: mirrored players count from the field's right), then top first —
|
||||
// row 0 is the BOTTOM row (DESIGN §3), so the top of the board is the highest row index. The order is a board position,
|
||||
// independent of the members' directions (only "身前" follows each member's `dir`). The devour marks in this order and the
|
||||
// 5-tier revive's beneficiaries are the first members in it (PRTS "从最先部署(更靠左和靠上的)的【阿戈尔】干员开始"; the
|
||||
// battle's initial deployment order, Battle.start).
|
||||
// independent of the members' directions (only "身前" follows each member's `dir`). The devour marks in this order (PRTS
|
||||
// "从最先部署(更靠左和靠上的)的【阿戈尔】干员开始"; the battle's initial deployment order, Battle.start).
|
||||
const boardCol = (u) => (u.player && u.player.mirror ? -u.tileC : u.tileC);
|
||||
const egirOrder = (a, b) => boardCol(a) - boardCol(b) || b.tileR - a.tileR || a.id - b.id;
|
||||
|
||||
/**
|
||||
* 联防: an operator forced out at the deployment (carry.down, Battle.start, before battleStart) still stands on its
|
||||
* deploy position. The devour and the 5-tier revive slots both count it there (PRTS 盟约记录 前3名 / 最先部署).
|
||||
* deploy position for the devour (PRTS 盟约记录 最先部署). The forced exit is not a knock-out, so it takes no 5-tier slot.
|
||||
*/
|
||||
function egirDownAtStart(battle, u) {
|
||||
return S.isOp(u) && !u.alive && u.removeReason === FORCED_EXIT && !!u.carry && u.carry.down === true
|
||||
@@ -365,11 +364,14 @@ function egirDownAtStart(battle, u) {
|
||||
* out and back in the same pass still gives its marks).
|
||||
* 联防: the operators down since the end of their own combat (forced out by Battle.start) mark, are marked and resolve
|
||||
* their marks like standing ones, but nothing resolves on them (below; per players' reports, owner's decision 2026-10-04).
|
||||
* Each devoured operator adds its tier to 阿戈尔 once (IN_BATTLE gain, disabled in 联防 / boss fields).
|
||||
* Whose operator stands in front does not matter (PRTS "依次吞噬身前一格干员", no own-side limit; the owner's decision of
|
||||
* 2026-10-05 after GitHub #140 comment 4): on a shared field (联防, boss) a teammate's operator — standing, or entering
|
||||
* 联防 down — is marked like an own one, gives the same base ATK / block count, and the chain goes on through it when it
|
||||
* is an 阿戈尔 (S.isMember: its own bonds). Its knock-out is its owner's (their bonds' revives, 不屈 …), credited to the
|
||||
* marker as usual. Each devoured operator adds its tier to 阿戈尔 once (IN_BATTLE gain, disabled in 联防 / boss fields).
|
||||
* Tokens / devices / empty tiles are never devoured.
|
||||
*/
|
||||
function devour(battle, pid, bb, members) {
|
||||
const memberSet = new Set(members);
|
||||
// 联防: an operator down at the end of its own combat (carryState.down — Battle.start forced it out right before
|
||||
// battleStart, FORCED_EXIT) takes part in the devour as if it stood on its tile, then stays out: it marks in its turn,
|
||||
// it is "the unit in front" of another (the chain goes on through it when it is a member), its base ATK / block count
|
||||
@@ -378,12 +380,14 @@ function devour(battle, pid, bb, members) {
|
||||
// report #3, GitHub #33 item 3), owner's decision 2026-10-04; until 0.1.2 the forced exit came first and the chain broke.
|
||||
const downAtStart = (u) => egirDownAtStart(battle, u);
|
||||
const order = members.filter((u) => S.onField(u) || downAtStart(u)).sort(egirOrder);
|
||||
// the operator in front, whoever owns it (until 0.1.3 only the player's own): a living one, else one lying there since
|
||||
// the 联防 start (carry.down, forced out) — a teammate's included
|
||||
const opAt = (u) => {
|
||||
const [r, c] = S.frontTile(u);
|
||||
const a = S.allyAt(battle, r, c, pid);
|
||||
const a = S.allyAt(battle, r, c);
|
||||
if (a) return S.isOp(a) && a.alive ? a : null;
|
||||
const d = battle.downOn(r, c);
|
||||
return d && d.ownerId === pid && downAtStart(d) ? d : null;
|
||||
return d && downAtStart(d) ? d : null;
|
||||
};
|
||||
const markedBy = new Map(); // marker → [targets]
|
||||
const marks = [];
|
||||
@@ -398,7 +402,8 @@ function devour(battle, pid, bb, members) {
|
||||
if ((markedBy.get(t) ?? []).includes(m)) continue;
|
||||
seen.add(t);
|
||||
mine.push(t);
|
||||
if (memberSet.has(t)) queue.push(t);
|
||||
// through a marked 阿戈尔 (for the player's own operators: exactly its members; a teammate's by its own bonds)
|
||||
if (S.isMember(battle, t, 'egirShip')) queue.push(t);
|
||||
}
|
||||
markedBy.set(m, mine);
|
||||
if (!mine.length) continue;
|
||||
@@ -437,37 +442,37 @@ function installEgir(battle, pid, bb, members) {
|
||||
};
|
||||
apply();
|
||||
onLayers(battle, pid, 'egirShip', apply);
|
||||
// 5: "前3名【阿戈尔】干员首次被击倒时立刻复活" — the 3 (bonds.json max_free_respawn_cnt) are the first 3 members BY POSITION,
|
||||
// in the devour's order above = the battle's deployment order (PRTS 盟约记录 阿戈尔 备注 "从最先部署(更靠左和靠上的)的
|
||||
// 【阿戈尔】干员开始"; players' videos: the revivers go by position, not by who is eaten or knocked out first). They are
|
||||
// fixed at battle start, before the devour; each revives once, on its own first knock-out ('killed'), whatever the order
|
||||
// of knock-outs, so a member the devour knocks out comes back only when it is one of them. Until 0.1.3 the first 3
|
||||
// members knocked out took the revives and the devour's food spent them at t = 0. A member entering 联防 down (forced
|
||||
// out before battleStart) keeps the slot its position earned: the forced exit is not a 击倒 (reason !== 'killed'), so
|
||||
// the charge waits until they are knocked out after standing back up. A 调和 member counts like any other.
|
||||
// [ASSUMED] a beneficiary 埃芒加德 / M3茧甲 save in place (items revivedInPlace, the stand-in for PRTS's 0-time redeploy)
|
||||
// has used its revive (PRTS 不屈 备注: a held 复活 is consumed even when another effect redeploys the unit at once).
|
||||
// PRTS: the knocked-out unit's next deployment has 0 redeploy time and 0 cost, i.e. it IS knocked out (被击倒 triggers,
|
||||
// 克莱门莎, 幽灵鲨 … fire) and redeploys at once where it lies (the engine's rest tile, Battle._layBody: the tile it was
|
||||
// knocked out on — a raid-relocated member comes back where it fell —, or its own home when it fell on another board
|
||||
// piece's home; PRTS 卫戍协议/帮助 §作战阶段 单位部署) with full HP, SP reset and `deploy` effects (卡西米尔 / 叙拉古).
|
||||
// Death priority 11: before 不屈 (10), whose redeploy "also consumes a 复活 charge" — with this order the charge is
|
||||
// always the one used, same outcome. The marks still pending on a revived member are cancelled (devour).
|
||||
// 5: "前3名【阿戈尔】干员首次被击倒时立刻复活" — the 3 (bonds.json max_free_respawn_cnt) slots go to the first 3 members
|
||||
// knocked out, in knock-out order: the owner's decision of 2026-10-05, following players' reports (GitHub #105, #140:
|
||||
// "没被吃的阿戈尔干员也会占用复活名额" — with 0.1.3's fixed holders by position the uneaten front members held the
|
||||
// slots, so whatever the food chain ate only 3 阿戈尔 stood). A knock-out by the battle-start devour counts like any
|
||||
// other, so the eaten food usually takes them at t = 0 and survivors = uneaten members + 3. A member takes at most one
|
||||
// slot, on its first knock-out ('killed') in the battle: its later knock-outs never take one, and a first knock-out
|
||||
// after the 3 are gone stays down. However many members mark it, its pending marks are cancelled once it is knocked
|
||||
// out (devour), so a multi-devoured member spends one slot at most. Its own saver acts first: every kit, item or band
|
||||
// revive that prevents or replaces the knock-out is a `fatal` hook (斯卡蒂's DRE-Y −50, kit savers 10 … −60, 坚固维式
|
||||
// 重锤 PRIO_REVIVE −100, M3茧甲 PRIO_RESPAWN −101, 埃芒加德 −110), so the unit is never knocked out and no slot is used —
|
||||
// the slot waits for its first real knock-out [ASSUMED for 埃芒加德, a band: as M3茧甲, the item revive the owner named].
|
||||
// An operator entering 联防 down (FORCED_EXIT) is not knocked out either; a 调和 member counts like any other. Each
|
||||
// battle (normal, 联防, boss) counts its own. PRTS: the knocked-out unit's next deployment has 0 redeploy time and 0
|
||||
// cost, i.e. it IS knocked out (被击倒 triggers, 克莱门莎, 幽灵鲨 … fire) and redeploys at once where it lies (the
|
||||
// engine's rest tile, Battle._layBody: the tile it was knocked out on — a raid-relocated member comes back where it
|
||||
// fell —, or its own home when it fell on another board piece's home; PRTS 卫戍协议/帮助 §作战阶段 单位部署) with full
|
||||
// HP, SP reset and `deploy` effects (卡西米尔 / 叙拉古). Death priority 11: before 不屈 (10), whose redeploy "also
|
||||
// consumes a 复活 charge" — with this order the slot is always the one used, same outcome. Until 0.1.3 the slots were
|
||||
// fixed at battle start for the first 3 members by position (§23.20); until 0.1.2 the first 3 knocked out took them
|
||||
// but a revived member's pending marks could knock it out again (GitHub #33, fixed in devour since).
|
||||
const max = reached(battle, pid, 'egirShip', bb.power_bond_char_cnt) ? Math.max(0, Math.floor(num(bb.max_free_respawn_cnt, 0))) : 0;
|
||||
const holders = new Map(); // beneficiary → its in-place revives (mem.revives) at battle start
|
||||
battle.on('battleStart', () => {
|
||||
for (const u of members.filter((x) => S.isOp(x) && (S.onField(x) || egirDownAtStart(battle, x))).sort(egirOrder).slice(0, max)) holders.set(u, u.mem.revives | 0);
|
||||
devour(battle, pid, bb, members);
|
||||
}, { once: true });
|
||||
battle.on('battleStart', () => devour(battle, pid, bb, members), { once: true });
|
||||
if (!(max > 0)) return;
|
||||
const used = new Set();
|
||||
const memberOps = new Set(members.filter(S.isOp));
|
||||
const knockedOut = new Set(); // members whose first knock-out of this battle has happened
|
||||
let revives = 0;
|
||||
battle.on('death', (c) => {
|
||||
const u = c.unit;
|
||||
if (c.reason !== 'killed' || !holders.has(u) || used.has(u)) return;
|
||||
used.add(u);
|
||||
if ((u.mem.revives | 0) !== holders.get(u)) return; // saved in place since the battle start: the revive is gone
|
||||
if (u.alive || u.removed) return;
|
||||
if (c.reason !== 'killed' || !memberOps.has(u) || knockedOut.has(u)) return;
|
||||
knockedOut.add(u);
|
||||
if (revives >= max || u.alive || u.removed) return;
|
||||
if (!battle.redeploy(u, { free: true })) return;
|
||||
revives++;
|
||||
S.fxOn(battle, 'revive', u, 'bond:egirShip', 'respawn', { n: revives });
|
||||
|
||||
Reference in new issue
Block a user