feedback6 R21H: the round's enemy effects reach the 频次 器物 and the leaders' mid-fight summons (PR #272, 感谢 @CXUtk); golden: 74 moved
器物 (the 14 hit-count keys): hits = data × the spawn's hpMul ÷ supplyHpMul, rounded as before (setHits) — 攻坚装备 / II / III leave out only 炎佑, 补给线 / 补给线II also exactly these 14 keys (activity_table aceffect_enemy_2 / 2_2 enemy_exclude, data/effects.json); until now setHits(def.maxHp) overwrote the round's HP (青铜镜 30 hits at every difficulty). build-data writes enemyScale[r].supplyHp = 1.2^(kHp − kAtk) · extra (the HP-only 补给线II steps and 补给线's 1.08; co-op 终极 R5–R15 only — the rebuild changed config.json alone), gamedata returns it as supplyHpMul and the wave / 悬赏 mods carry it (waves.js roundMods; a 频次 enemy's death spawn inherits it). Not taken from the PR: its 1.08 / 1.2 back-calculation (it counted the 补给线II steps as 攻坚装备) and its fractional-HP ceiling. Leader summons (死亡集群's 妖怪, 刺胄之弹, “裂管之音”/“断弦之音”, 不祥幻影): the boss / hidden field carries the round's scale as flags.enemyScale (bossRounds.js, golden fields, tools/balance.mjs) and bosses.js summonMods gives them HP / ATK / speed (死亡集群's summon.hp_ratio on top; 刺胄之弹 / 余音 hit counts follow their HP); the leader's pool stays exempt; 王权号令's invulnerable equipment unchanged; 炎佑 is an ally token, never scaled (no change). Unchanged and open: the 逐火 / 再生 husk counts (talent prop_max_hp). Golden — 器物 counts: roster 37 (002 003 004 005 007–020 022 023 025 026 029–034 036–041 043 045 049: each holds a 器物 extra spawn / 频次 wave / death spawn), bond-soloShip-high, diy 9 (036 040 056 062 068 073 091 114 115), boss-boss_6-solo (俗心's 小说卷轴 escorts); matches 16 (all but solo-ABYSS-2 and coop2-NORMAL-70-diy: 频次 rounds / 悬赏 and the Final Assault summons). Summons: boss-boss_1-pair/solo, hidden-boss_8-pair/solo (妖怪, 刺胄之弹), boss-boss_3-pair/solo, hidden-boss_10-pair/solo (余音), boss-boss_5-pair/solo (不祥幻影). standins unchanged.
This commit is contained in:
1 parent
783bd30afd
commit
e33ee3db8f
20 files changed
+1491
-1324
No files matched your search
@@ -397,14 +397,20 @@ export class GameData {
|
||||
return typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : null;
|
||||
}
|
||||
|
||||
/** Official enemy multipliers of round r (config enemyScale: the PRTS table + 终极 speed ×1.15 from R3). */
|
||||
/**
|
||||
* Official enemy multipliers of round r (config enemyScale: the PRTS table + 终极 speed ×1.15 from R3). `supplyHpMul`
|
||||
* (only when ≠ 1: co-op 终极 R5–R15) = the share of hpMul from 补给线 / 补给线II (config `supplyHp`), effects whose
|
||||
* `enemy_exclude` leaves out the 14 器物 hit-count keys — they take hpMul / supplyHpMul (archetypes.js `times`).
|
||||
*/
|
||||
baseEnemyScale(r) {
|
||||
const e = this.mode.enemyScale && this.mode.enemyScale[String(r)];
|
||||
if (!e || typeof e !== 'object') return { hpMul: 1, atkMul: 1, speedMul: 1 };
|
||||
const supply = numOr(e.supplyHp, 1);
|
||||
return {
|
||||
hpMul: Math.max(0.01, numOr(e.hp, 1)),
|
||||
atkMul: Math.max(0, numOr(e.atk, 1)),
|
||||
speedMul: Math.max(0.01, numOr(e.speed, 1)),
|
||||
...(supply > 0 && supply !== 1 ? { supplyHpMul: supply } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,8 @@ export class MatchBoss {
|
||||
spawns: this._sanitizeSpawns(spawns),
|
||||
routes: wave.routes,
|
||||
sharedBoss: this.bossPool,
|
||||
flags: { layerGainsEnabled: false, ...this.gd.dp },
|
||||
// the round's enemy effects for the leaders' mid-fight summons (server/sim/content/bosses.js summonMods)
|
||||
flags: { layerGainsEnabled: false, ...this.gd.dp, enemyScale: this.gd.enemyScale(this.round) },
|
||||
fieldId,
|
||||
enemyOverrides: wave.overrides,
|
||||
waveId: wave.templateId,
|
||||
|
||||
+11
-3
@@ -234,6 +234,13 @@ export function scaleFor(gd, r) {
|
||||
return gd.enemyScale(r);
|
||||
}
|
||||
|
||||
/** The spawn mods of a round's scale for a non-leader enemy: HP / ATK / speed, plus `supplyHpMul` when the round has one. */
|
||||
export function roundMods(scale) {
|
||||
const m = { hpMul: scale.hpMul, atkMul: scale.atkMul, speedMul: scale.speedMul };
|
||||
if (scale.supplyHpMul != null) m.supplyHpMul = scale.supplyHpMul;
|
||||
return m;
|
||||
}
|
||||
|
||||
/** Slot class of an enemy that has no placeholder slot (literal template keys, bounty adds). */
|
||||
export function classOf(gd, enemyKey) {
|
||||
const e = gd.enemy(enemyKey);
|
||||
@@ -327,8 +334,9 @@ function templateSpawns(gd, tpl, round, pick) {
|
||||
interval: count > 1 ? step : 0,
|
||||
// the round multipliers are ENEMY effects on every enemy but 炎佑 (aceffect_enemy_1–5 `enemy_attribute_mul`,
|
||||
// enemy_exclude = enemy_9012_acloon): leader parts take them all; the leader takes ATK / speed but not HP — its HP
|
||||
// is the server pool, "领袖单位于服务器的生命值加成不受上述加成影响" (PRTS 下半)
|
||||
mods: isBoss ? { atkMul: scale.atkMul, speedMul: scale.speedMul, slot } : { hpMul: scale.hpMul, atkMul: scale.atkMul, speedMul: scale.speedMul, slot },
|
||||
// is the server pool, "领袖单位于服务器的生命值加成不受上述加成影响" (PRTS 下半). `supplyHpMul` (roundMods) rides along for
|
||||
// the 器物 hit-count units, which 补给线 / 补给线II leave out (archetypes.js `times`; also a 频次 enemy's death spawn)
|
||||
mods: isBoss ? { atkMul: scale.atkMul, speedMul: scale.speedMul, slot } : { ...roundMods(scale), slot },
|
||||
actionIndex: i,
|
||||
preview: previewInfo(gd, key, routes[routeIndex], isBoss, leader),
|
||||
};
|
||||
@@ -528,7 +536,7 @@ function bountyPlan(gd, round, wave, bounties, playerId, side) {
|
||||
routeIndex,
|
||||
count: run.len,
|
||||
interval: run.len > 1 ? step : 0,
|
||||
mods: { hpMul: scale.hpMul, atkMul: scale.atkMul, speedMul: scale.speedMul, slot: classOf(gd, c.enemyKey), bountyId: b.id },
|
||||
mods: { ...roundMods(scale), slot: classOf(gd, c.enemyKey), bountyId: b.id },
|
||||
tag: 'bounty',
|
||||
ownerPlayerId: playerId,
|
||||
preview: previewInfo(gd, c.enemyKey, routes[routeIndex], false, leader),
|
||||
|
||||
@@ -240,6 +240,23 @@ function branchSpawn(b, tpl, name, phaseIdx, { fallback = null, mods = null, at
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn mods of a leader's mid-fight summon (死亡集群's 妖怪, 刺胄之弹, “裂管之音”/“断弦之音”, 不祥幻影): the round's enemy
|
||||
* effects (攻坚装备 / II / III, 补给线 / II, 急行军 — `enemy_attribute_mul` / `enemy_move_speed_mul` on 所有敌人, `enemy_exclude`
|
||||
* only 炎佑 and, for 补给线, the 器物) reach a summon like any enemy; only the leader's own HP, the server pool, is exempt
|
||||
* ("领袖单位于服务器的生命值加成不受上述加成影响", PRTS 下半). They come as the battle's `flags.enemyScale` (gd.enemyScale of the
|
||||
* boss round, set by the match — a BattleSpec flag, so browsers run the same); `hpRatio` = the skill's own summon HP ratio
|
||||
* (死亡集群 `summon.hp_ratio`), multiplied in. No flags (tests, tools): the ratio alone. Until 0.2.1 the summons took no
|
||||
* round effect at all (PR #272). 王权号令's equipment is left out: invulnerable, untargetable, it never fights.
|
||||
*/
|
||||
function summonMods(b, hpRatio = null) {
|
||||
const s = b.flags && b.flags.enemyScale;
|
||||
if (!s || typeof s !== 'object') return hpRatio ? { hpMul: hpRatio } : null;
|
||||
const m = { hpMul: (Number.isFinite(s.hpMul) && s.hpMul > 0 ? s.hpMul : 1) * (hpRatio || 1), atkMul: s.atkMul, speedMul: s.speedMul };
|
||||
if (s.supplyHpMul != null) m.supplyHpMul = s.supplyHpMul;
|
||||
return m;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// install
|
||||
|
||||
@@ -398,8 +415,8 @@ function kitHelm(ab, e, b, tpl) {
|
||||
const hpMul = s2.bb['summon.hp_ratio'] > 0 ? s2.bb['summon.hp_ratio'] : null;
|
||||
const key = s2.bs.enemy_key ?? 'enemy_1005_yokai';
|
||||
const drones = branchSpawn(b2, tpl, s2.bs.branch_id ?? 'boss_summon_enemy', 0, {
|
||||
mods: hpMul ? { hpMul } : null, at: { x: e2.x, y: e2.y },
|
||||
fallback: () => [b2.spawnEnemy(key, { pos: [e2.y, e2.x], route: toGoal(b2, e2.x, e2.y, 'FLY'), mods: hpMul ? { hpMul } : null })].filter(Boolean),
|
||||
mods: summonMods(b2, hpMul), at: { x: e2.x, y: e2.y },
|
||||
fallback: () => [b2.spawnEnemy(key, { pos: [e2.y, e2.x], route: toGoal(b2, e2.x, e2.y, 'FLY'), mods: summonMods(b2, hpMul) })].filter(Boolean),
|
||||
});
|
||||
b2.fx('summon', { x: e2.x, y: e2.y, id: e2.id, key, n: drones.length });
|
||||
const ratio = s2.bb.hp_ratio ?? 0;
|
||||
@@ -429,7 +446,7 @@ export function droneLinkBase(boss, base = DRONE_LINK_BASE) {
|
||||
/** Launch a 刺胄之弹 from `boss` at `target`'s tile. */
|
||||
function fireShell(b, boss, target) {
|
||||
const tr = target.tileR, tc = target.tileC;
|
||||
const sh = b.spawnEnemy(SHELL_KEY, { pos: [boss.y, boss.x], route: { motion: 'FLY', start: [boss.y, boss.x], end: [tr, tc], steps: [{ t: 'wait', s: 99999 }] }, tag: 'part', countInTotal: false, ownerPlayerId: boss.ownerId });
|
||||
const sh = b.spawnEnemy(SHELL_KEY, { pos: [boss.y, boss.x], route: { motion: 'FLY', start: [boss.y, boss.x], end: [tr, tc], steps: [{ t: 'wait', s: 99999 }] }, tag: 'part', countInTotal: false, ownerPlayerId: boss.ownerId, mods: summonMods(b) });
|
||||
if (!sh) return null;
|
||||
sh.mem.ab.shell = { tr, tc, boss };
|
||||
b.fx('shell', { x: boss.x, y: boss.y, id: sh.id, tx: tc, ty: tr, r: 1.5, kind: 'helmShell' });
|
||||
@@ -440,7 +457,7 @@ function kitShell(ab, e) {
|
||||
const stun = T(ab, 'killed.duration') ?? 0, dot = T(ab, 'killed.value') ?? 0;
|
||||
return [{
|
||||
spawn(b, e2) {
|
||||
setHits(e2, e2.def.maxHp); hitCount(b, e2, true); // 需要数次攻击击倒
|
||||
setHits(e2, e2.base.maxHp); hitCount(b, e2, true); // 需要数次攻击击倒 (× the round's HP: summonMods)
|
||||
b.addBuff(e2, { key: 'boss:shellGuard', persist: true, flags: { noDisplace: true } }); // 失衡免疫 (PRTS 天赋; also 静态刚体)
|
||||
},
|
||||
tick(b, e2, a, dt) {
|
||||
@@ -762,7 +779,7 @@ function kitEcho(ab, e) {
|
||||
const s = ab.sk.Skill;
|
||||
return [
|
||||
{
|
||||
spawn(b, e2) { setHits(e2, e2.def.maxHp); hitCount(b, e2, true); e2.blockWeight = ECHO_BLOCK_WEIGHT; setEchoForm(b, e2, 'dark'); },
|
||||
spawn(b, e2) { setHits(e2, e2.base.maxHp); hitCount(b, e2, true); e2.blockWeight = ECHO_BLOCK_WEIGHT; setEchoForm(b, e2, 'dark'); }, // hits × the round's HP (summonMods)
|
||||
taken(c, b, e2) { const s = c.source || c.credit; if (s && s.side === 'ally') echoHit(b, e2); }, // 受到伤害时以自身为中心造成一次范围伤害 (a 无来源 burst too)
|
||||
},
|
||||
s && {
|
||||
@@ -784,7 +801,7 @@ function pipeCore(ab, e, b, tpl, { form, prefix }) {
|
||||
const key = ab.tS['1.enemy_key'] ?? ECHO_KEY;
|
||||
const P = { acc: 0, atk: Infinity }; // first strike as soon as an echo of its form exists (engine attack rule)
|
||||
const summon = (b2, e2) => {
|
||||
const got = branchSpawn(b2, tpl, 'summon_enemy', 0, { at: { x: e2.x, y: e2.y }, fallback: () => [b2.spawnEnemy(key, { pos: [e2.y, e2.x], route: toGoal(b2, e2.x, e2.y) })].filter(Boolean) });
|
||||
const got = branchSpawn(b2, tpl, 'summon_enemy', 0, { mods: summonMods(b2), at: { x: e2.x, y: e2.y }, fallback: () => [b2.spawnEnemy(key, { pos: [e2.y, e2.x], route: toGoal(b2, e2.x, e2.y), mods: summonMods(b2) })].filter(Boolean) });
|
||||
for (const u of got) if (u.defId === ECHO_KEY && form === 'gold') setEchoForm(b2, u, 'gold');
|
||||
b2.fx('summon', { x: e2.x, y: e2.y, id: e2.id, key, n: got.length });
|
||||
};
|
||||
@@ -962,7 +979,7 @@ function kitLucien(ab, e) {
|
||||
const route = remainingRoute(e2);
|
||||
const from = blinkForward(b, e2, s.bb.dist ?? 1.5);
|
||||
if (!from) return;
|
||||
const ph = b.spawnEnemy(PHANTOM_KEY, { pos: [from.y, from.x], route, ownerPlayerId: e2.ownerId, sourcePlayerId: e2.sourcePlayerId });
|
||||
const ph = b.spawnEnemy(PHANTOM_KEY, { pos: [from.y, from.x], route, ownerPlayerId: e2.ownerId, sourcePlayerId: e2.sourcePlayerId, mods: summonMods(b) });
|
||||
if (ph) b.fx('summon', { x: from.x, y: from.y, id: e2.id, key: PHANTOM_KEY, n: 1 });
|
||||
},
|
||||
}];
|
||||
|
||||
@@ -34,8 +34,21 @@ const unblockable = () => ({ spawn(b, e) { b.addBuff(e, { key: 'ab:unblockable',
|
||||
*/
|
||||
const runWhenHit = (v) => ({ taken(c, b, e, a) { if (a.on || !(v > 0)) return; a.on = true; b.addBuff(e, { key: 'ab:duckRun', persist: true, mods: { moveMul: 1 + v } }); } });
|
||||
|
||||
/** 频次: maxHp = hits. */
|
||||
const times = (artsOnly = false) => ({ spawn(b, e) { setHits(e, e.def.maxHp); hitCount(b, e, true, artsOnly); } });
|
||||
/**
|
||||
* 频次 (the 14 器物 keys, kitTimes): maxHp = hits = the data's max HP × the spawn's HP multiplier. The round effects reach
|
||||
* these units like any enemy (攻坚装备 / II / III leave out only 炎佑), but not 补给线 / 补给线II, whose `enemy_exclude` lists
|
||||
* exactly these 14 keys (activity_table aceffect_enemy_2 / 2_2, data/effects.json): that share (`mods.supplyHpMul`, co-op
|
||||
* 终极 R5–R15) comes out again — a 频次 enemy's death spawn inherits the parent's mods, so it travels with them. Whole hits:
|
||||
* setHits rounds [ASSUMED: no source says how a fractional count rounds]. Until 0.2.1 the count was the data's whatever
|
||||
* the difficulty (PR #272).
|
||||
*/
|
||||
const times = (artsOnly = false) => ({
|
||||
spawn(b, e) {
|
||||
const s = Number(e.mods && e.mods.supplyHpMul);
|
||||
setHits(e, e.base.maxHp / (Number.isFinite(s) && s > 0 ? s : 1));
|
||||
hitCount(b, e, true, artsOnly);
|
||||
},
|
||||
});
|
||||
|
||||
/** "只能被阻挡数大于等于N的单位阻挡". */
|
||||
const blockWeight = (n) => ({ spawn(b, e) { e.blockWeight = n; } });
|
||||
|
||||
@@ -302,7 +302,7 @@ export function stepToward(u, tx, ty, dist) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Set a 频次 unit's hits (maxHp := hits, full). */
|
||||
/** Set a 频次 unit's hits (maxHp := hits, full): `n` rounded to a whole count, at least 1. */
|
||||
export function setHits(e, n) {
|
||||
const h = Math.max(1, Math.round(n));
|
||||
e.base.maxHp = h;
|
||||
|
||||
Reference in new issue
Block a user