diff --git a/assets/shop/script/passCheck.ts b/assets/shop/script/passCheck.ts index e86ad4d..ca55767 100644 --- a/assets/shop/script/passCheck.ts +++ b/assets/shop/script/passCheck.ts @@ -147,9 +147,10 @@ export default class passCheck extends cc.Component { } this.timeNumber = 0; const completed = Math.min(this.stage.unlocked, maxLevel); - const lvNum = Math.min(completed + 1, maxLevel); + const lvNum = completed; + const progressLevel = Math.min(completed + 1, maxLevel); this.currentLvNum = Math.max(1, completed); - this.progressNum2 = Number(passCheckData[lvNum - 1].token); + this.progressNum2 = Number(passCheckData[progressLevel - 1].token); this.progressNum1 = completed >= maxLevel ? this.progressNum2 : Math.max(0, this.stage.experience - passCheckData.slice(0, completed).reduce((sum, row) => sum + Number(row.token), 0)); NumberToImage.numberToImageNodesPassCheck(lvNum, 45, 48, "half_black_", this.currentLv, true); @@ -222,10 +223,12 @@ export default class passCheck extends cc.Component { // 打开即记为已展示,关闭或刷新弹窗时不重复播放这一段。 cc.fx.StorageMessage.setStorage(key, { completed, fill }); if (previous.completed === completed && previous.fill === fill) return; + NumberToImage.numberToImageNodesPassCheck(Math.min(previous.completed, completed, maxLevel), 45, 48, "half_black_", this.currentLv, true); const animation = cc.tween(this.barSpr).delay(0.3); for (let level = previous.completed + 1; level <= completed; level++) { animation.delay(0.3).to(0.3, { fillRange: 1 }).call(() => { this.barSpr.fillRange = level >= maxLevel ? 1 : 0; + NumberToImage.numberToImageNodesPassCheck(Math.min(level, completed, maxLevel), 45, 48, "half_black_", this.currentLv, true); }); } if (completed < maxLevel && (completed === previous.completed || fill > 0)) { diff --git a/tools/test-battle-pass-ui.cjs b/tools/test-battle-pass-ui.cjs index a9067b6..e8aa7ad 100644 --- a/tools/test-battle-pass-ui.cjs +++ b/tools/test-battle-pass-ui.cjs @@ -134,7 +134,7 @@ test('battle pass animation ignores unscoped history and preserves already claim assert.equal(f.manager.progressBar4.height, 6 * 350); assert.equal(f.panel.stage.free[4], 0); assert.equal(f.panel.barSpr.fillRange, 0); - assert.equal(f.panel.currentLv.number, 8); + assert.equal(f.panel.currentLv.number, 7); assert.equal(f.storage.get('ProgressIndex'), 4); }); @@ -165,6 +165,68 @@ test('battle pass crosses a tier from its saved fraction and finishes at the new assert.equal(f.manager.progressBar4.height, 5 * 350); }); +test('battle pass displayed level goes 5 -> 6 -> 7 only after each full bar', () => { + const f = animationFixture(); + f.show(5, 1); + f.show(7, 1); + assert.equal(f.panel.currentLv.number, 5); + const animation = f.actions.find(action => action.target === f.panel.barSpr); + const levels = [f.panel.currentLv.number]; + for (const step of animation.steps) { + if (step.values) { + const before = f.panel.currentLv.number; + Object.assign(animation.target, step.values); + assert.equal(f.panel.currentLv.number, before, 'filling must not advance the number early'); + } + if (step.fn) { step.fn(); levels.push(f.panel.currentLv.number); } + } + assert.deepEqual(levels, [5, 6, 7]); + assert.equal(f.panel.barSpr.fillRange, 0.25); + f.show(7, 1); assert.equal(f.actions.length, 0); assert.equal(f.panel.currentLv.number, 7); +}); + +test('battle pass partial-only animation keeps the displayed level unchanged', () => { + const f = animationFixture(); f.show(5, 1); f.show(5, 2); + assert.equal(f.panel.currentLv.number, 5); + f.finish(); assert.equal(f.panel.currentLv.number, 5); +}); + +test('battle pass animated level stops at the cap and refresh cancels older number updates', () => { + const f = animationFixture(); f.show(33); f.show(35); + assert.equal(f.panel.currentLv.number, 33); + f.finish(); assert.equal(f.panel.currentLv.number, 35); + f.show(4); f.show(6); + const pending = [...f.actions]; + f.panel.onInit({ 1: { time: '0' }, 2: { time: '2000000000000', experience: 13, free: [], passCheck: [] } }); + pending.forEach(action => assert.equal(f.stopped.has(action), true)); + f.finish(); assert.equal(f.panel.currentLv.number, 6); +}); + +test('battle pass level never exceeds unlocked tiers or the entitlement cap at any animation step', () => { + for (const tier of [0, 18, 30]) { + const f = animationFixture(), cap = tier === 30 ? rows.length : 35; + for (const level of [5, 7, cap - 1, cap, cap + 1]) { + f.show(Math.min(level, rows.length), 1, { tier }); + const unlocked = Math.min(f.panel.stage.unlocked, cap); + const check = () => assert.ok(f.panel.currentLv.number <= unlocked, + `tier ${tier}: displayed ${f.panel.currentLv.number} exceeds unlocked ${unlocked}`); + check(); + for (const action of f.actions) for (const step of action.steps) { + if (step.values) Object.assign(action.target, step.values); + if (step.fn) step.fn(); + check(); + } + assert.equal(f.panel.currentLv.number, unlocked); + } + f.storage.set('PassProgress:animation-player:2000000000000', { completed: cap + 10, fill: 1 }); + f.show(6, 1, { tier }); + assert.equal(f.actions.length, 0); + assert.equal(f.panel.currentLv.number, 6); + } + const empty = animationFixture(); empty.show(0, 0, { old: true, season: '1900000000000' }); + assert.equal(empty.panel.currentLv.number, 0); +}); + test('battle pass periods and accounts cannot overwrite each other animation records', () => { const f = animationFixture(); f.show(5); f.show(4, 0, { old: true, season: '1900000000000' }); assert.equal(f.actions.length, 0); @@ -228,7 +290,7 @@ test('battle pass zero XP, cost changes and premium extension keep progress disp assert.equal(f.panel.barSpr.fillRange, 0); assert.equal(f.panel.progress_1.number, 0); f.show(4); f.show(5); f.finish(); assert.equal(f.panel.progress_1.number, 0); assert.equal(f.panel.progress_2.number, 4); - assert.equal(f.panel.currentLv.number, 6); + assert.equal(f.panel.currentLv.number, 5); f.show(35, 0, { tier: 18 }); f.show(35, 0, { tier: 30 }); assert.equal(f.actions.length, 0); assert.equal(f.panel.barSpr.fillRange, 0); f.show(36, 0, { tier: 30 }); assert.deepEqual(f.topFills(), [1]); f.finish();