The weapon licence, the peer line, and the steward gets his vest back
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7e2649c888
commit
7d39032907
40 files changed
+62
-24
No files matched your search
+6
-3
@@ -7,9 +7,9 @@
|
||||
// (beats <-> gameplay and beats <-> zombies are runtime-only import cycles, same pattern as
|
||||
// input <-> gameplay: the imported bindings are only touched inside functions, never at load.)
|
||||
|
||||
import { TUNE, UI, bossArt, matchFoe, BENCH, ZFAMS, COURT, HOME, racketStage, leagueOf, cupRoundsFor } from './config.js';
|
||||
import { TUNE, UI, bossArt, matchFoe, BENCH, ZFAMS, COURT, HOME, racketStage, leagueOf, cupRoundsFor, foeLevel } from './config.js';
|
||||
import { endRun, MODE_MATCH } from './stats.js';
|
||||
import { award, prof, save as profSave, push as profPush, setPromo } from './profile.js';
|
||||
import { award, prof, save as profSave, push as profPush, setPromo, levelAt } from './profile.js';
|
||||
import { G, P, O, ball, score, setupPoint, sendBoy, other } from './state.js';
|
||||
import { voice, zombieDuck, aww } from './audio.js';
|
||||
import { zombies, pellets, extras, shots, spawnRacketZombie, spawnChapterBoss, spawnZombie, explode, killPlayer } from './zombies.js';
|
||||
@@ -649,8 +649,11 @@ function matchOpponent() {
|
||||
// 1.6 s intro beat until the tap, and the head wears the same z_<fam>head crop the card wore.
|
||||
// In the cup the round names itself on the second line.
|
||||
const cup = G.match.cup;
|
||||
// the "am I its peer yet?" line (Oliver, 2026-08-29): the body's ladder position spread over
|
||||
// the profile's own level scale, beside yours — display only, nothing scales off it
|
||||
const vs = 'LEVEL ' + foeLevel(G.match.opp) + ' — YOURS: ' + levelAt(prof.xp);
|
||||
showModal({ title: zb.foe.name, color: UI.hot, icon: 'z_' + zb.fam + 'head', iconH: 64,
|
||||
body: [zb.foe.line].concat(cup ? [cup.rounds[cup.round].name + (cup.rounds[cup.round].need > 1
|
||||
body: [zb.foe.line, vs].concat(cup ? [cup.rounds[cup.round].name + (cup.rounds[cup.round].need > 1
|
||||
? ' · FIRST TO ' + cup.rounds[cup.round].need + ' GAMES' : '')]
|
||||
// the top of the band walking BACK on is the arcade's natural "what now?" moment — the
|
||||
// intro answers it in place (THE LEAGUES: the cup is the only promotion)
|
||||
|
||||
+11
-5
@@ -729,11 +729,13 @@ export const UI = { hot: '#d9705f', warm: '#e0cc72', pick: '#cbd06a', cream: '#e
|
||||
// palette is near it, so "pink means a weapon is in there" needs no explaining
|
||||
pink: '#ff5cf0' };
|
||||
|
||||
// THE TIN TIERS (the leagues, phase 4; settled: tin-tier, NO starting loadout). The league
|
||||
// unlocks what a won duel's tin can PAY, independent of the run's own barrel gates — "weapons are
|
||||
// only ever won" stays literal, the league just deepens the pot: lasers join the duel ladder at
|
||||
// league 3, rockets at 4, and league 5 duels pay DOUBLE tins. The world board is not devalued by
|
||||
// starting inventory, and every weapon still arrives through resolveInterlude('P').
|
||||
// THE WEAPON LICENCE (the leagues; settled: tin-tier, NO starting loadout; hardened 2026-08-29,
|
||||
// Oliver: "in den Truhen sind nach wie vor alle Waffen verfügbar"). The league is a LICENCE and
|
||||
// the run's `from` gates stay the PACING: a weapon exists for you only when the league says it is
|
||||
// yours AND the run has reached it — barrels and duel tins agree (an OR on the tins just moved
|
||||
// his complaint one screen over). Below league 3 there are no lasers anywhere, ever; the gore
|
||||
// director's tuned income curve is untouched for licensed players. League 5 duels pay DOUBLE
|
||||
// tins. "Weapons are only ever won" stays literal.
|
||||
export const TIN_LEAGUE = { laser: 3, rocket: 4 };
|
||||
export const TIN_DOUBLE_LEAGUE = 5;
|
||||
|
||||
@@ -915,6 +917,10 @@ export const leagueOf = lg => LEAGUES[Math.min(LEAGUES.length, Math.max(1, lg |
|
||||
// which league a LADDER POSITION belongs to — THE CLIMB's tempo reads this (the world speeds up
|
||||
// as the run crosses each band border), and it is what makes the club read as five territories
|
||||
export const leagueOfRung = n => LEAGUES.find(l => n <= l.band[1]) || LEAGUES[LEAGUES.length - 1];
|
||||
// the DISPLAYED level of a ladder position (the walk-on card's "am I its peer yet?" line,
|
||||
// 2026-08-29): the ladder spread over the profile's 50 levels. Display only — nothing scales
|
||||
// off it. 49 = profile.LEVEL_CAP - 1 (config cannot import profile; the cycle runs the other way).
|
||||
export const foeLevel = n => Math.round(1 + (n - 1) / (LADDER - 1) * 49);
|
||||
|
||||
// The tournament, PER LEAGUE. Same shape CUP_ROUNDS always had (name/band/need) — round one is a
|
||||
// single game so a run opens fast, and the body's pool still scales with `need` (matchOpponent).
|
||||
|
||||
+4
-4
@@ -913,11 +913,11 @@ function spendSlot(i) {
|
||||
// (capped per type at TUNE.wCap) — old stock is kept, so you can bank a bomb for the horde
|
||||
// instead of spending it now. G.weapon picks which tin the next serve throws.
|
||||
const TINS = ['bomb', 'triple', 'fire', 'gun']; // the duel ladder, in the order it hands them out
|
||||
// THE TIN TIERS (the leagues): the league deepens the pot the duel pays from — a barrel weapon
|
||||
// joins the ladder when the RUN unlocks it (as ever) OR when the LEAGUE says it is yours
|
||||
// (config.TIN_LEAGUE). Pure and exported so the suite can pin the pot.
|
||||
// THE WEAPON LICENCE (the leagues): the league licenses a weapon, the run's `from` gate paces
|
||||
// it — BOTH must hold (config.TIN_LEAGUE; barrels apply the same licence in pickBarrelWeapon).
|
||||
// Pure and exported so the suite can pin the pot.
|
||||
export const tinLadder = () => [...TINS, ...Object.keys(BARREL_WEAPONS).filter(w =>
|
||||
G.runGames >= BARREL_WEAPONS[w].from || prof.lg >= (TIN_LEAGUE[w] || 99))];
|
||||
G.runGames >= BARREL_WEAPONS[w].from && prof.lg >= (TIN_LEAGUE[w] || 0))];
|
||||
export function deliverTin() {
|
||||
// WALK the ladder to the next rung you have room for, rather than cycling blindly onto one that
|
||||
// is already at wCap. Blind cycling meant `grantWeapon` clamped the top-up to a no-op and a won
|
||||
|
||||
+1
-1
@@ -236,7 +236,7 @@ function farB(z) {
|
||||
// Asset cache-buster — bump with main.js?v=N (see the img.src note below). EXPORTED because the
|
||||
// menu screens hang <img> tags on the same files, and two copies of this number is one copy too
|
||||
// many: the one that does not get bumped is the one that serves a cached 404.
|
||||
export const SPRV = 149;
|
||||
export const SPRV = 150;
|
||||
// The boot bar in main.js watches this. Nothing here CHANGES when things load — every sprite was
|
||||
// already requested at import — it just makes the wait visible and holds the menu back until the
|
||||
// last sheet has landed. See the onerror note below for the other half of "sometimes missing".
|
||||
|
||||
+5
-2
@@ -3,7 +3,7 @@
|
||||
// your baseline — one crossing it is a point for THEM.
|
||||
// (zombies <-> gameplay is a runtime-only import cycle, same pattern as input.)
|
||||
|
||||
import { TUNE, UI, COURT, CONTACT, BOSS_ART, bossArt, WEAPONS, BARREL_WEAPONS, woundStage, zombieH, EATEN_BURST, EATEN_SUMMARY, EATEN_FEED_R } from './config.js';
|
||||
import { TUNE, UI, COURT, CONTACT, BOSS_ART, bossArt, WEAPONS, BARREL_WEAPONS, TIN_LEAGUE, woundStage, zombieH, EATEN_BURST, EATEN_SUMMARY, EATEN_FEED_R } from './config.js';
|
||||
import { G, P, O, ball, score, sendBoy, setupPoint } from './state.js';
|
||||
import { splat, moan, pock, thud, zap, voice, boom, swish, shriek, aww, sfx , whoosh, flame } from './audio.js';
|
||||
import { haptic } from './haptics.js';
|
||||
@@ -11,7 +11,7 @@ import { drillKill, drillCaught } from './drill.js';
|
||||
import { splatPoint, cheer, spentBall, aside, grantWeapon, endBurst, sliceCarom } from './gameplay.js';
|
||||
import { resolveInterlude, endBossFight } from './beats.js'; // runtime-only cycle, like the gameplay one
|
||||
import { tally, multiKill, endRun, MODE_MATCH } from './stats.js';
|
||||
import { award, bonus } from './profile.js';
|
||||
import { award, bonus, prof } from './profile.js';
|
||||
|
||||
export const zombies = []; // {x, z, v, walkT, variant: 'a'|'b'|'c'}
|
||||
export const gore = []; // lawn decals {x, z, r, a} — the flat ground layer
|
||||
@@ -86,6 +86,9 @@ function pickBarrelWeapon() {
|
||||
const pool = [];
|
||||
for (const [id, b] of Object.entries(BARREL_WEAPONS)) {
|
||||
if (G.runGames < b.from) continue;
|
||||
// THE WEAPON LICENCE (the leagues): the league licenses, `from` paces — a barrel must not
|
||||
// hand a league-1 player the laser his duels rightly refuse (config.TIN_LEAGUE)
|
||||
if (prof.lg < (TIN_LEAGUE[id] || 0)) continue;
|
||||
if (id === lastBarrel) continue; // never twice running...
|
||||
for (let k = 0; k < b.weight; k++) pool.push(id);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user