0.2.4-dev: round-29 community fixes and the official shared card pool

- Shared card pool and item stock as in the official mode (the owner's request, from the 路标档案馆 video and public notes): solo low-tier exceptions, co-op operators x2, shared item stock with special-item exceptions, normal / advanced occupancy 1 / 2, re-check on buy, 机变 exhaustion, release on consume / destroy / elimination; 整备 never exceeds stock; invariant held <= cap.
- 突袭: one taunt-first reselect right after a revive (the owner's rule); ordinary polls and redeploys keep the 0.2.3 rule; a revive on an enemy's tile stays. #475: online boss / hidden-boss Raid may land on the empty half (owner-limited scope).
- 浊心斯卡蒂 S1 x 坚守 no longer bounce redirected damage, and the two shares conserve the hit (idea of PR #464 by @HappyTanuki).
- Operator fixes adapted from PRs by @Xinglan233 (#459 哈洛德 S2 elemental trigger, #460 涤火杰西卡 shield on the four adjacent tiles, #461 家族徽章 first hit after stealth, #462 松果 S2 regrow per deploy); 克莱门莎 S2 splash 1.1 / 100% ATK and air auto-trigger (#467); 白面 copying 歌蕾蒂娅 / 余 skips the three-member condition (#473, the owner's video).
- 香槟炸弹 is not chosen by friendly auras / heals (PR #468 / #476 by @Siornya); 琳琅诗怀雅 coin balance (PR #450 by @IceCodeNew); Touch prep display (PR #451 by @YinqiMargatroid); lobby header (PR #471 by @YuriRestia); Japanese UI terms (PR #448 by @scutkane).
- AI-only boss countdown grace, render-failure cache / recovery without restarting a battle, device units kept on a same-field re-entry, DIY detail / skill previews, large-text result buttons.
- golden: battle families equal 0.2.3 except diy-135 / diy-136 (克莱门莎); all 18 release matches move through the shared pool, every flip named in docs/history/0.2.4.md §29.12; two coverage scenarios added.
This commit is contained in:
sganggs committed 2026-10-11 01:58:52 +08:00
1 parent 1db8e51023
commit 6bfc08270c
129 files changed
+3477 -1011

No files matched your search

+51
View File
@@ -959,6 +959,57 @@
"height": 241
}
},
"token_10031_swire2_gdtrap": {
"skel": "token_10031_swire2_gdtrap.skel",
"atlas": "token_10031_swire2_gdtrap.atlas",
"textures": [
"token_10031_swire2_gdtrap.png"
],
"pma": true,
"anims": {
"idle": "Idle",
"deploy": "Start",
"attack": {
"begin": null,
"loop": "Attack",
"end": null
},
"attackDown": null,
"skill": {
"begin": null,
"loop": "Attack",
"end": null,
"via": "attack",
"index": 0,
"idle": null
},
"die": "Die",
"move": null,
"stun": null
},
"animations": {
"Attack": 0.2,
"Default": 0,
"Die": 0.467,
"Idle": 2,
"Start": 0.667
},
"events": [
"OnAttack",
"OnStart"
],
"hits": {
"Attack": [
0.2
]
},
"bounds": {
"x": -75.631,
"y": -8.563,
"width": 152,
"height": 225.923
}
},
"token_10032_jesca2_jckshd": {
"skel": "token_10032_jesca2_jckshd.skel",
"atlas": "token_10032_jesca2_jckshd.atlas",
+17 -3
View File
@@ -3561,8 +3561,19 @@ function enemyScaleFor(table, type, difficulty, round, isHidden) {
* Build data/config.json: modes (rounds, timers, shop, enemy scaling), economy, bans, timers,
* titles, tips, broadcasts, trophies and other global tunables.
*/
function buildConfig(ctx, waves, stages, bands) {
function buildConfig(ctx, waves, stages, bands, chess, items) {
const { act, ac } = ctx;
// Original experiment: 路标档案馆, BV1eLXXBqEgF (2026-03-29), 0:30 / 1:30 / 2:00.
// These are concurrent stock counts, not limits on appearances or total purchases during a match.
const stockByName = (records, counts) => Object.fromEntries(Object.entries(counts).map(([name, count]) => {
const rec = Object.values(records).find((r) => !r.isGolden && r.name === name);
if (!rec) throw new Error(`pool stock: missing ${name}`);
return [rec.chessId || rec.id, count];
}));
const chessStock = stockByName(chess, { 普罗旺斯: 10, 德克萨斯: 10, 跃跃: 10, 古米: 10, 格雷伊: 10,
风丸: 8, 赫默: 12, 休谟斯: 12, 砾: 12, 蒂比: 12, 调香师: 12 });
const itemStock = stockByName(items, { 简易通讯机: 5, 蜂鸣器: 6, 防暴盾: 6, 浓缩嗅盐: 6,
寻呼模块: 7, 伪装服: 7, 拉特兰桥夹: 7, 商业包装方案: 2 });
const addendum = ctx.research.core?._criticAddendum || {};
const multipliers = addendum.enemyStatMultipliers || DEFAULT_ENEMY_MULTIPLIERS;
if (!addendum.enemyStatMultipliers) warn('config: using built-in enemy multiplier table (research 01 _criticAddendum missing)');
@@ -3668,7 +3679,10 @@ function buildConfig(ctx, waves, stages, bands) {
storeCntMax: act.constData.storeCntMax,
equipPerChess: 2, maxArtsPerRound: 2,
poolCopies: { 1: 12, 2: 14, 3: 18, 4: 16, 5: 8, 6: 5 },
poolCopiesOverrides: { chess_char_6_11_a: 4 },
poolCopiesOverrides: chessStock,
coopPoolMultiplier: 2,
itemPoolCopies: { 1: 4, 2: 6, 3: 7, 4: 8, 5: 7, 6: 3 },
itemPoolCopiesOverrides: itemStock,
goldenCopies: 3,
mergeCount: 3,
mergeCountOverrides: Object.fromEntries(Object.entries(act.charChessDataDict).filter(([, c]) => !c.isGolden && c.upgradeNum && c.upgradeNum !== 3).map(([id, c]) => [id, c.upgradeNum])),
@@ -4079,7 +4093,7 @@ async function main() {
// the bonds each strategy is built around (DESIGN §21.26): the bot skips, and the strategy draft marks 本局禁用, a band
// whose bond the mode switches off
for (const b of Object.values(bands)) b.bondIds = bandBondIds(b, { bonds, pools: choices.pools });
const config = buildConfig(ctx, waves, stages, bands);
const config = buildConfig(ctx, waves, stages, bands, chess, items);
const files = { config, chess, bonds, garrisons, items, bands, effects, choices, enemies, factions, waves, stages, bosses, tokens, backups };
// the unit forms rebuild every PRESET chess at every rank (before the annotation: each pass as built)
+5 -1
View File
@@ -722,6 +722,10 @@ export function matchScenarios() {
// (its 调度中心 starts at level 5, like the boosted runs' raised LP: the 自选 pieces are sold from level 5 / 6, which the
// AI otherwise reaches only with a full board, where a single new operator rarely improves its lineup)
out.push({ id: `coop2-NORMAL-${MATCH_DIY_SEED}-diy`, mode: 'coop', difficulty: 'NORMAL', bots: 1, seed: MATCH_DIY_SEED, human: { diy: MATCH_DIY, shopLevel: 5 } });
// The measured shared-stock changes leave the original seeds without a fielded DIY/stand-in operator. Keep those
// original outcomes, and add companion seeds that still exercise the complete shop → deployment → battle path.
out.push({ id: 'coop2-NORMAL-1-coverage-standins', mode: 'coop', difficulty: 'NORMAL', bots: 1, seed: 1, human: { notOwned: MATCH_NOT_OWNED } });
out.push({ id: 'coop2-NORMAL-5-coverage-diy', mode: 'coop', difficulty: 'NORMAL', bots: 1, seed: 5, human: { diy: MATCH_DIY, shopLevel: 5 } });
const humanAbout = (h) => (h.notOwned ? ` + 1 human seat (AI 托管) without ${h.notOwned.length} operators (补位 stand-ins)`
: ` + 1 human seat (AI 托管) with ${Object.keys(h.diy || {}).length} 自选 picks${h.shopLevel ? ` and its 调度中心 at level ${h.shopLevel} from the first prep` : ''}`);
return out.map((m) => ({ ...m, family: 'matches', kind: 'match', about: `${m.mode} ${m.difficulty}, ${m.bots} bot seat(s)${m.human ? humanAbout(m.human) : ''}, seed ${m.seed}${m.clientCombat === false ? ', server-run combat' : ''}${m.boost ? `, LP ${m.boost.lp} and ${m.boost.layers} layers per bond from the first prep` : ''}` }));
@@ -1029,7 +1033,7 @@ const ABOUT = {
* fields (one of them a Hidden Core), six matches (solo 标准 / 绝境, co-op 2 / 4, the boosted Hidden Core run, the 补位
* match) and the normal-record stand-in battles.
*/
const FAST_MATCHES = new Set(['solo-FUNNY-1', 'solo-HARD-1', 'coop2-NORMAL-3', 'coop4-ABYSS-7', 'solo-HARD-9-boosted', 'coop2-NORMAL-14-standins', `coop2-NORMAL-${MATCH_DIY_SEED}-diy`]);
const FAST_MATCHES = new Set(['solo-FUNNY-1', 'solo-HARD-1', 'coop2-NORMAL-3', 'coop4-ABYSS-7', 'solo-HARD-9-boosted', 'coop2-NORMAL-14-standins', `coop2-NORMAL-${MATCH_DIY_SEED}-diy`, 'coop2-NORMAL-1-coverage-standins', 'coop2-NORMAL-5-coverage-diy']);
const FAST_FIELDS = new Set(['boss-boss_1-pair', 'boss-boss_4-solo', 'boss-boss_7-pair', 'hidden-boss_9-pair', 'unite-1', 'unite-2']);
export function isFast(sc) {
if (sc.family === 'matches') return FAST_MATCHES.has(sc.id);
+1 -1
View File
@@ -137,7 +137,7 @@ TOKEN_SPINES = [
'token_10005_mgllan_drone3', 'token_10007_phatom_twin', 'token_10008_cqbw_box', 'token_10009_weedy_cannon',
'token_10020_ling_soul1', 'token_10020_ling_soul2', 'token_10020_ling_soul3', 'token_10024_ebnhlz_rcube',
'token_10025_doroth_recttp', 'token_10026_bgsnow_subbow', 'token_10027_ironmn_pile1', 'token_10027_ironmn_pile2',
'token_10027_ironmn_pile3', 'token_10029_slent2_protrb', 'token_10032_jesca2_jckshd', 'token_10034_ray_sndbst',
'token_10027_ironmn_pile3', 'token_10029_slent2_protrb', 'token_10031_swire2_gdtrap', 'token_10032_jesca2_jckshd', 'token_10034_ray_sndbst',
'token_10035_wisdel_wward', 'token_10041_cathy_catsld', 'token_10043_necras_skeltn', 'token_10050_monstr_prosts',
'token_10051_radian_tower1', 'token_10052_radian_tower2', 'token_10053_radian_tower3', 'token_10054_phatm2_encdool',
'token_10057_svash2_eagle1', 'token_10057_svash2_eagle2', 'token_10057_svash2_eagle3', 'token_10059_nasti_nstdef',