Playtest #6 follow-up (v2.5.1): leader HP review — 直接乘算, one debuff instance, 999 layer cap, 限伤; elite to the board; 假想敌:胄 kit
- co-op leaders died within seconds: leader HP is the official data (40 values pinned); bond / strategy / item / 机变 / 特质 "+X%" bonuses now add up (PRTS 直接乘算) instead of compounding; a same-named enemy debuff keeps one (strongest) instance per target (奥术 across a pair field); leader parts pass damage 1:1; fixed co-op pool (the user's call) - official limits read from the client: each bond stops at 999 layers (MAX_GARRISON_STACK); a leader hit of 300000+ in boss / hidden battles is cancelled (MAX_BATTLE_DAMAGE, 限伤) — shared/constants.js switches, research 11 - a merge that consumes a deployed copy puts the elite on that tile and facing (user-confirmed); gold target highlight, promotion effect - 假想敌:胄 per PRTS: one shell, shells within range, any drone death costs 2 % max HP, 剑/锤 sorties and 瘫痪 - DESIGN §20.10–§20.14; 2842 unit tests, all 28 browser suites green; Chrome = Node on boss fields
This commit is contained in:
1 parent
6464e4b46a
commit
620f88c0d8
81 files changed
+4194
-447
No files matched your search
+8
-7
@@ -7,8 +7,8 @@
|
||||
// matches (stage, factions, bans, boss and lineups all vary with the seed), simulates the real wave of that round with
|
||||
// the real Battle and FULL content (kits, talents, bonds incl. layers, IN_BATTLE garrisons, items, bands, tokens) and
|
||||
// reports the leak distribution and clear time. Boss rounds (R14 / solo 标准 R9) and the Hidden Core (R15) run the
|
||||
// real Final Assault fields (pairs, `_s` template for a lone player, shared boss HP pool = bloodPoint (co-op; solo
|
||||
// ×0.25 [ASSUMED]) — GameData.bossPoolHp —, team LP, overtime drain) and report the pool damage by 150 s, the kill time and the team LP spent.
|
||||
// real Final Assault fields (pairs, `_s` template for a lone player, shared boss HP pool = bloodPoint (co-op; × alive / 4
|
||||
// only with config bossHpScale.aliveScaling; solo ×0.25 [ASSUMED]) — GameData.bossPoolHp —, team LP, overtime drain) and report the pool damage by 150 s, the kill time and the team LP spent.
|
||||
//
|
||||
// The competent board of round r (curves below, profile-scaled): shop level, deployed units, elites (精锐), equipment,
|
||||
// 2–3 active bonds built around a core bond (3 members early, 6 from R11 when the pool allows it) and the layers those
|
||||
@@ -31,7 +31,8 @@
|
||||
// --group N boards per multi match (default 4; 1 = no 联防)
|
||||
// --boss-samples N sampled matches per boss / hidden round (default 8)
|
||||
// --rehearsal N layout variants a board rehearses (default 5 = every bot variant; 0 = heuristic planner only)
|
||||
// --profile curve multiplier for elites, items and layers (weak 0.6, competent 1, strong 1.4, or a number)
|
||||
// --profile curve multiplier for elites, items and layers (weak 0.6, competent 1, strong 1.4, or a number;
|
||||
// a bond's layers stop at BOND_LAYER_CAP, 999)
|
||||
// --tuning off ignore data/tuning.json (it only holds title rules now: no effect on the numbers)
|
||||
// --legacy-time read the combat limits as game seconds (the reading before the fix of docs/BALANCE.md §2.1)
|
||||
// --boss-lp N team LP per alive player entering the Final Assault (default 15)
|
||||
@@ -54,7 +55,7 @@ import { positionClass } from '../server/match/board.js';
|
||||
import { pairPlayers, bossPoolHp, SharedBossPool } from '../server/match/finalAssault.js';
|
||||
import { planUnite, uniteBattleOpts, uniteSurvivors } from '../server/match/unite.js';
|
||||
import { createRng, deriveSeed } from '../server/sim/rng.js';
|
||||
import { GEO, PHASE } from '../shared/constants.js';
|
||||
import { GEO, PHASE, layerGainRoom } from '../shared/constants.js';
|
||||
import { TICK } from '../server/sim/constants.js';
|
||||
|
||||
export const DIFFS = ['FUNNY', 'NORMAL', 'HARD', 'ABYSS'];
|
||||
@@ -270,7 +271,7 @@ export function applyBoard(m, ps, plan, r, rng, { profile = 1 } = {}) {
|
||||
for (const [id, b] of Object.entries(planned)) {
|
||||
if (!b.active) continue;
|
||||
const base = id === plan.core ? curve('coreLayers', r) : curve('addonLayers', r);
|
||||
const v = Math.round(base * profile * jit());
|
||||
const v = layerGainRoom(0, Math.round(base * profile * jit())); // ≤ BOND_LAYER_CAP (999) for any --profile
|
||||
if (v > 0) ps.layers[id] = v;
|
||||
}
|
||||
ps.recompute();
|
||||
@@ -376,8 +377,8 @@ export function runBoss({ data, mode, difficulty, round, seed, profile = 1, boss
|
||||
}
|
||||
}
|
||||
const alive = m.alivePlayers();
|
||||
// the official pool (co-op = bloodPoint, no alive scaling) — finalAssault.bossPoolHp until the match adopts it
|
||||
const pool = new SharedBossPool(typeof gd.bossPoolHp === 'function' ? gd.bossPoolHp(id) : bossPoolHp(gd, id, alive.length));
|
||||
// the official pool (co-op = bloodPoint, × alive / 4 only with aliveScaling, DESIGN §20.10; solo ×0.25) — the match's own rule
|
||||
const pool = new SharedBossPool(bossPoolHp(gd, id, alive.length));
|
||||
let teamLp = alive.reduce((s, p) => s + p.lp, 0);
|
||||
const teamLp0 = teamLp;
|
||||
let leakLp = 0;
|
||||
|
||||
@@ -2580,7 +2580,7 @@ function buildConfig(ctx, waves, stages, bands) {
|
||||
combatTimeLimit, enemyScale,
|
||||
bossHpScale: type === 'SINGLE'
|
||||
? { bloodPointKey: DIFF_KEYS[m.modeDifficulty] || null, solo: 0.25, soloAssumed: true, unaffectedByEnemyScale: true }
|
||||
: { bloodPointKey: DIFF_KEYS[m.modeDifficulty] || null, coop: 1, aliveScaling: false, unaffectedByEnemyScale: true },
|
||||
: { bloodPointKey: DIFF_KEYS[m.modeDifficulty] || null, coop: 1, aliveScaling: false, aliveFull: 4, aliveAssumed: true, unaffectedByEnemyScale: true },
|
||||
upgradePrices: levels.slice(0, -1).map((l) => shopLv[l].initialUpgradePrice),
|
||||
maxShopLevel: levels.length ? levels[levels.length - 1] : 6,
|
||||
shopSlots: Object.fromEntries(levels.map((l) => [l, { chess: shopLv[l].charChessCount, item: shopLv[l].itemCount }])),
|
||||
@@ -2645,8 +2645,8 @@ function buildConfig(ctx, waves, stages, bands) {
|
||||
lpCapPerRound: act.constData.costPlayerHpLimit ?? 10,
|
||||
bossOvertimeAfter: 150, bossOvertimeDrainPerSec: 1,
|
||||
bossHpScale: {
|
||||
formula: 'co-op: bloodPoint[difficulty] (one pool shared by the split leaders, no alive-player scaling — "敌方领袖的总生命值不变"); solo: bloodPoint[difficulty] × solo [ASSUMED]',
|
||||
coop: 1, solo: 0.25, soloAssumed: true, aliveScaling: false, unaffectedByEnemyScale: true,
|
||||
formula: 'co-op: bloodPoint[difficulty] — one pool for every field ("所有人将一起对敌方领袖造成伤害"; the mirrored copies of a pair field share it, "两侧的敌方领袖共享生命值(敌方领袖的总生命值不变)"); aliveScaling true would scale it × alive players at the Final Assault / aliveFull (巴哈姆特 12294 "聯機隊友(撤退/死掉)變少,最後boss血條也會變少", one community note, no proportion: off until confirmed, the alive / 4 proportion [ASSUMED]); solo: bloodPoint[difficulty] × solo (0.25 = one player of four) [ASSUMED]',
|
||||
coop: 1, solo: 0.25, soloAssumed: true, aliveScaling: false, aliveFull: 4, aliveAssumed: true, unaffectedByEnemyScale: true,
|
||||
},
|
||||
hiddenCore: { single: 350, multi: 1200, minTeamLpExclusive: 1, difficulties: ['NORMAL', 'HARD', 'ABYSS'], checkedAfterRound: 14 },
|
||||
dp: { init: 10, perSec: 1, max: 99 },
|
||||
|
||||
+4
-2
@@ -10,7 +10,8 @@
|
||||
// --humans N the first N seats are human seats on "AI 托管" (exercises the human views / m.private paths)
|
||||
// --seeds N run N consecutive seeds and print an aggregate (rounds survived, LP by round, outcomes)
|
||||
// --lp N override every player's starting LP (reach later rounds / the Final Assault while balancing)
|
||||
// --layers N at the boss round's prep, add N layers to every bond of every player (reach the Hidden Core)
|
||||
// --layers N at the boss round's prep, add N layers to every bond of every player (reach the Hidden Core); a raw
|
||||
// write (no onLayers milestones) that stops at BOND_LAYER_CAP (999, shared/constants.js) like every gain
|
||||
// --difficulty ALL sweep all four difficulties
|
||||
// --content sim content mode (default full)
|
||||
// --rehearsal N layouts each bot rehearses per prep with the real sim (default: the Match default, 3; 0 = off,
|
||||
@@ -35,6 +36,7 @@ import { VirtualScheduler } from '../server/match/scheduler.js';
|
||||
import { attachAudit } from '../server/match/audit.js';
|
||||
import { Battle } from '../server/sim/Battle.js';
|
||||
import { getData } from '../server/data.js';
|
||||
import { layerGainRoom } from '../shared/constants.js';
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
const opt = {};
|
||||
@@ -130,7 +132,7 @@ function runOne(seed, difficulty) {
|
||||
last = key;
|
||||
if (lpOverride && m.phase === 'PREP' && m.round === 1) for (const ps of m.players.values()) ps.lp = lpOverride;
|
||||
if (layerBoost && m.phase === 'PREP' && m.round === m.gd.bossRound) {
|
||||
for (const ps of m.alivePlayers()) { for (const id of m.gd.bondIds) ps.layers[id] = (ps.layers[id] || 0) + layerBoost; ps.recompute(); }
|
||||
for (const ps of m.alivePlayers()) { for (const id of m.gd.bondIds) { const before = ps.layers[id] || 0; ps.layers[id] = before + layerGainRoom(before, layerBoost); } ps.recompute(); }
|
||||
}
|
||||
if (m.phase === 'SETTLE' || ((m.phase === 'RESULT') && (m.round === m.gd.bossRound || m.round === m.gd.hiddenRound))) {
|
||||
const row = { round: m.round, unite: !!m.unitePlan, players: {} };
|
||||
|
||||
Reference in new issue
Block a user