修复战令商城的显示,还有无限体力特效调整
This commit is contained in:
parent
fcb0e7a43d
commit
13ea6d64ab
|
|
@ -2415,7 +2415,7 @@
|
|||
"__id__": 50
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_active": false,
|
||||
"_components": [],
|
||||
"_prefab": null,
|
||||
"_opacity": 255,
|
||||
|
|
@ -2586,8 +2586,8 @@
|
|||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 92,
|
||||
"height": 86
|
||||
"width": 290,
|
||||
"height": 252
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
|
|
@ -2598,15 +2598,15 @@
|
|||
"__type__": "TypedArray",
|
||||
"ctor": "Float64Array",
|
||||
"array": [
|
||||
-107.106,
|
||||
-91.077,
|
||||
-1.322,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0.27,
|
||||
0.27,
|
||||
1
|
||||
]
|
||||
},
|
||||
|
|
@ -2642,7 +2642,7 @@
|
|||
"__uuid__": "b6b570b9-d928-4646-8a2a-70f18cc10991"
|
||||
},
|
||||
"_type": 0,
|
||||
"_sizeMode": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillType": 0,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
|
|
@ -2913,15 +2913,15 @@
|
|||
"__type__": "TypedArray",
|
||||
"ctor": "Float64Array",
|
||||
"array": [
|
||||
-110.285,
|
||||
-94.256,
|
||||
-0.124,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0.35,
|
||||
0.35,
|
||||
0.27,
|
||||
0.27,
|
||||
1
|
||||
]
|
||||
},
|
||||
|
|
@ -3105,14 +3105,14 @@
|
|||
"__type__": "TypedArray",
|
||||
"ctor": "Float64Array",
|
||||
"array": [
|
||||
0,
|
||||
-18,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0.8,
|
||||
1,
|
||||
1
|
||||
]
|
||||
|
|
@ -3724,15 +3724,15 @@
|
|||
"__type__": "TypedArray",
|
||||
"ctor": "Float64Array",
|
||||
"array": [
|
||||
-107,
|
||||
-108,
|
||||
-1.322,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0.85,
|
||||
0.85,
|
||||
1
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -757,7 +757,7 @@ export class GameConfig {
|
|||
vibrateOpen: true, //震动
|
||||
coinnum: 0, //每局的金币数
|
||||
paid_user: false, //是否是付费用户
|
||||
version: "1.0.66", //版本号
|
||||
version: "1.0.67", //版本号
|
||||
shushu_DistinctId: "", //数数访客ID
|
||||
shushu_AccountId: "", //数数账号ID
|
||||
uid: "", //用户和后端唯一id
|
||||
|
|
|
|||
|
|
@ -18806,8 +18806,8 @@
|
|||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 92,
|
||||
"height": 86
|
||||
"width": 290,
|
||||
"height": 252
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
|
|
@ -18818,15 +18818,15 @@
|
|||
"__type__": "TypedArray",
|
||||
"ctor": "Float64Array",
|
||||
"array": [
|
||||
-107.106,
|
||||
-91.077,
|
||||
-1.322,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0.27,
|
||||
0.27,
|
||||
1
|
||||
]
|
||||
},
|
||||
|
|
@ -18862,7 +18862,7 @@
|
|||
"__uuid__": "b6b570b9-d928-4646-8a2a-70f18cc10991"
|
||||
},
|
||||
"_type": 0,
|
||||
"_sizeMode": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillType": 0,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
|
|
@ -19281,15 +19281,15 @@
|
|||
"__type__": "TypedArray",
|
||||
"ctor": "Float64Array",
|
||||
"array": [
|
||||
-110.285,
|
||||
-94.249,
|
||||
-0.124,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0.35,
|
||||
0.35,
|
||||
0.27,
|
||||
0.27,
|
||||
1
|
||||
]
|
||||
},
|
||||
|
|
@ -19499,14 +19499,14 @@
|
|||
"__type__": "TypedArray",
|
||||
"ctor": "Float64Array",
|
||||
"array": [
|
||||
0,
|
||||
-18,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0.8,
|
||||
1,
|
||||
1
|
||||
]
|
||||
|
|
@ -20092,15 +20092,15 @@
|
|||
"__type__": "TypedArray",
|
||||
"ctor": "Float64Array",
|
||||
"array": [
|
||||
-107,
|
||||
-108,
|
||||
-1.322,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0.85,
|
||||
0.85,
|
||||
1
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -125,7 +125,6 @@ export default class passCheck extends cc.Component {
|
|||
passCheck = { ...this.stage };
|
||||
const maxLevel = Math.min(passCheckData.length, passTier(this.stage) === 30 ? passCheckData.length : 35);
|
||||
this.activateBtn.node.active = Number(this.stage.time) > Date.now() && passTier(this.stage) < 30;
|
||||
this.ProgressIndex = cc.fx.StorageMessage.getStorage("ProgressIndex") || 1;
|
||||
|
||||
|
||||
// 设置固定开始时间:2025年11月4日10:00
|
||||
|
|
@ -147,29 +146,12 @@ export default class passCheck extends cc.Component {
|
|||
}
|
||||
}
|
||||
this.timeNumber = 0;
|
||||
this.progressNum1 = passCheck.progress;
|
||||
if (passCheck.progressLevel > maxLevel) {
|
||||
passCheck.progressLevel = maxLevel;
|
||||
this.progressNum1 = passCheckData[passCheck.progressLevel - 1].token;
|
||||
}
|
||||
this.progressNum2 = passCheckData[passCheck.progressLevel - 1].token;
|
||||
|
||||
let lvNum = passCheck.progressLevel;
|
||||
|
||||
if (this.progressNum1 == this.progressNum2) {
|
||||
lvNum += 1;
|
||||
this.currentLvNum = passCheck.progressLevel;
|
||||
} else {
|
||||
this.currentLvNum = passCheck.progressLevel - 1;
|
||||
}
|
||||
if (this.currentLvNum <= 0) {
|
||||
this.currentLvNum = 1;
|
||||
} else if (this.currentLvNum > maxLevel) {
|
||||
this.currentLvNum = maxLevel;
|
||||
}
|
||||
if (lvNum >= maxLevel) {
|
||||
lvNum = maxLevel;
|
||||
}
|
||||
const completed = Math.min(this.stage.unlocked, maxLevel);
|
||||
const lvNum = Math.min(completed + 1, maxLevel);
|
||||
this.currentLvNum = Math.max(1, completed);
|
||||
this.progressNum2 = Number(passCheckData[lvNum - 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);
|
||||
// NumberToImage.numberToImageNodesShop(lvNum, 23, 35, "Black", this.currentLv, true);
|
||||
|
||||
|
|
@ -182,73 +164,7 @@ export default class passCheck extends cc.Component {
|
|||
NumberToImage.numberToImageNodesShop(this.progressNum1, 20, 15, "Black", this.progress_1, true);
|
||||
NumberToImage.numberToImageNodesShop(this.progressNum2, 20, 15, "Black", this.progress_2, true);
|
||||
cc.fx.GameConfig.GM_INFO.passCheckActivate = passCheck.activate;
|
||||
// this.barSpr.fillRange = this.progressNum1 / this.progressNum2;
|
||||
let barSprFillRange = this.progressNum2 ? this.progressNum1 / this.progressNum2 : 1;
|
||||
this.barSpr.node.active = true;
|
||||
let forInt = passCheck.progressLevel - this.ProgressIndex;
|
||||
// if (forInt < 1) {
|
||||
// forInt = 1;
|
||||
// }
|
||||
if (cc.fx.GameConfig.GM_INFO.getItemType == 1) {
|
||||
forInt = 0;
|
||||
// 当前是第二期领第一期物品
|
||||
}
|
||||
// let time = 0.3;
|
||||
let fillRangeFun = (fillRange, time, reset?: boolean) => {
|
||||
cc.tween(this.barSpr)
|
||||
.delay(time)
|
||||
.to(time, { fillRange: fillRange })
|
||||
.call(() => {
|
||||
if (reset) {
|
||||
this.barSpr.fillRange = 0;
|
||||
if (this.progressNum1 == this.progressNum2) {
|
||||
if (this.currentLvNum >= maxLevel) {
|
||||
// NumberToImage.numberToImageNodes(4, 20, 15, "mul", this.progress_1, true);
|
||||
NumberToImage.numberToImageNodesShop(this.progressNum2, 20, 15, "Black", this.progress_1, true);
|
||||
|
||||
} else {
|
||||
// NumberToImage.numberToImageNodes(0, 20, 15, "mul", this.progress_1, true);
|
||||
NumberToImage.numberToImageNodesShop(0, 20, 15, "Black", this.progress_1, true);
|
||||
}
|
||||
|
||||
let lvNumber = passCheck.progressLevel;
|
||||
if (passCheck.progressLevel >= maxLevel) {
|
||||
lvNumber = maxLevel - 1;
|
||||
}
|
||||
// NumberToImage.numberToImageNodes(passCheckData[lvNumber].token, 20, 15, "mul", this.progress_2, true);
|
||||
NumberToImage.numberToImageNodesShop(passCheckData[lvNumber].token, 20, 15, "Black", this.progress_2, true);
|
||||
|
||||
}
|
||||
// NumberToImage.numberToImageNodes(0, 20, 15, "mul", this.progress_1, true);
|
||||
// NumberToImage.numberToImageNodes(0, 20, 15, "mul", this.progress_2, true);
|
||||
// 可能还需要更新其他相关数据
|
||||
// self.currentLvNum += 1;
|
||||
// NumberToImage.numberToImageNodes(self.currentLvNum, 35, 20, "month_", self.currentLv, true);
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
// 确保动画按顺序执行
|
||||
for (let j = 0; j < forInt; j++) {
|
||||
cc.tween(this.barSpr).delay(j * 0.6 + 0.3).call(() => {
|
||||
if (j == forInt - 1) {
|
||||
// 最后一个动画,根据当前 fillRange 值决定
|
||||
if (barSprFillRange == 1) {
|
||||
// 延迟执行最后一个动画,确保前面的动画完成
|
||||
fillRangeFun(1, 0.3, true);
|
||||
} else {
|
||||
fillRangeFun(barSprFillRange, 0.3);
|
||||
}
|
||||
} else {
|
||||
// 中间的动画,都填满到1
|
||||
fillRangeFun(1, 0.3, true);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
if (forInt == 0) {
|
||||
fillRangeFun(1, 0, true);
|
||||
}
|
||||
this.updateProgressAnimation(completed, maxLevel);
|
||||
|
||||
|
||||
this.setbarNodePosX()
|
||||
|
|
@ -289,10 +205,39 @@ export default class passCheck extends cc.Component {
|
|||
this.onShowList(this.passListData);
|
||||
}
|
||||
|
||||
private updateProgressAnimation(completed: number, maxLevel: number) {
|
||||
const fill = this.progressNum2 ? this.progressNum1 / this.progressNum2 : 1;
|
||||
const uid = cc.fx.GameConfig.GM_INFO.uid || cc.fx.StorageMessage.getStorage("uid") || "";
|
||||
const key = "PassProgress:" + uid + ":" + this.endTime;
|
||||
const saved = cc.fx.StorageMessage.getStorage(key);
|
||||
// 旧 ProgressIndex 没有期次信息;首次打开只记录当前状态,不补播历史动画。
|
||||
const valid = saved && Number.isInteger(saved.completed) && saved.completed >= 0
|
||||
&& saved.completed <= completed && Number.isFinite(saved.fill) && saved.fill >= 0 && saved.fill <= 1
|
||||
&& (saved.completed < completed || saved.fill <= fill);
|
||||
const previous = valid && cc.fx.GameConfig.GM_INFO.getItemType !== 1 ? saved : { completed, fill };
|
||||
this.ProgressIndex = Math.max(1, previous.completed);
|
||||
cc.Tween.stopAllByTarget(this.barSpr);
|
||||
this.barSpr.node.active = true;
|
||||
this.barSpr.fillRange = previous.fill;
|
||||
// 打开即记为已展示,关闭或刷新弹窗时不重复播放这一段。
|
||||
cc.fx.StorageMessage.setStorage(key, { completed, fill });
|
||||
if (previous.completed === completed && previous.fill === fill) return;
|
||||
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;
|
||||
});
|
||||
}
|
||||
if (completed < maxLevel && (completed === previous.completed || fill > 0)) {
|
||||
animation.delay(0.3).to(0.3, { fillRange: fill });
|
||||
}
|
||||
animation.start();
|
||||
}
|
||||
|
||||
onShowList(data: any) {
|
||||
this.node.getChildByName("scrollViewNode").active = true;
|
||||
var passCheckMgr = this.node.getChildByName("scrollViewNode").getComponent("passCheckMgr");
|
||||
passCheckMgr.init(data, this.currentLvNum, this.oldPassCheckInfo, this.endTime, this.stage, this.passData);
|
||||
passCheckMgr.init(data, this.currentLvNum, this.oldPassCheckInfo, this.endTime, this.stage, this.passData, this.ProgressIndex);
|
||||
this.passCheckMgr = passCheckMgr;
|
||||
// passCheckMgr.updateProgressBars();
|
||||
}
|
||||
|
|
@ -768,6 +713,7 @@ export default class passCheck extends cc.Component {
|
|||
}
|
||||
|
||||
onDestroy() {
|
||||
if (this.barSpr) cc.Tween.stopAllByTarget(this.barSpr);
|
||||
// 组件销毁时清除定时器
|
||||
if (this.countdownTimer) {
|
||||
clearInterval(this.countdownTimer);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export default class passCheckMgr extends cc.Component {
|
|||
start() {
|
||||
}
|
||||
|
||||
init(data: any[], progressLevel: number, oldPassCheckInfo: any[], time: any, stage?: any, passData?: any) {
|
||||
init(data: any[], progressLevel: number, oldPassCheckInfo: any[], time: any, stage?: any, passData?: any, progressStart?: number) {
|
||||
// 确保组件已经被正确加载
|
||||
if (!this.node) {
|
||||
console.error("passCheckMgr: node is null");
|
||||
|
|
@ -45,6 +45,7 @@ export default class passCheckMgr extends cc.Component {
|
|||
if (progressLevel !== undefined) {
|
||||
this.progressLevel = progressLevel;
|
||||
}
|
||||
this.ProgressIndex = progressStart === undefined ? this.progressLevel : progressStart;
|
||||
|
||||
// 获取ScrollView组件(使用不区分大小写的查找)
|
||||
let scrollViewNode: cc.Node | null = null;
|
||||
|
|
@ -238,8 +239,7 @@ export default class passCheckMgr extends cc.Component {
|
|||
this.progressBar3.setAnchorPoint(0.5, 1);
|
||||
this.progressBar3.setPosition(centerX, firstItemCenterY);
|
||||
|
||||
// 获取已保存的进度索引
|
||||
this.ProgressIndex = cc.fx.StorageMessage.getStorage("ProgressIndex") || 0;
|
||||
// 与上方动画使用同一期次、同一次读取的已完成档数。
|
||||
if (this.ProgressIndex > this.progressLevel) {
|
||||
this.ProgressIndex = this.progressLevel;
|
||||
}
|
||||
|
|
@ -259,6 +259,7 @@ export default class passCheckMgr extends cc.Component {
|
|||
|
||||
this.progressBar4.setAnchorPoint(0.5, 1);
|
||||
this.progressBar4.setPosition(centerX, firstItemCenterY);
|
||||
cc.Tween.stopAllByTarget(this.progressBar4);
|
||||
this.progressBar4.height = progressLength1;
|
||||
|
||||
let growthLength = progressLength2 - progressLength1;
|
||||
|
|
@ -279,7 +280,6 @@ export default class passCheckMgr extends cc.Component {
|
|||
// 特殊情况:当进度为1级时,进度条应该为0
|
||||
this.progressBar4.height = 0;
|
||||
}
|
||||
cc.fx.StorageMessage.setStorage("ProgressIndex", this.progressLevel);
|
||||
}
|
||||
|
||||
public updatePassCheckStatus(updates?: Array<{ itemIndex: number, isLeftReward: boolean }>) {
|
||||
|
|
@ -621,6 +621,8 @@ export default class passCheckMgr extends cc.Component {
|
|||
console.log("销毁passCheckMgr");
|
||||
cc.Tween.stopAllByTarget(this);
|
||||
|
||||
if (this.progressBar4) cc.Tween.stopAllByTarget(this.progressBar4);
|
||||
|
||||
// 清理可能存在的列表数据
|
||||
if (this.listData) {
|
||||
this.listData = null;
|
||||
|
|
|
|||
|
|
@ -500,7 +500,7 @@ export default class NewClass extends cc.Component {
|
|||
|
||||
setHealthInfo(type) {
|
||||
if (cc.fx.GameConfig.GM_INFO.hp >= cc.fx.GameConfig.GM_INFO.hp_Max && this.Stamina) {
|
||||
this.Stamina.getChildByName("man").active = true;
|
||||
this.Stamina.getChildByName("man").active = !(cc.fx.GameConfig.GM_INFO.userPowerTime > Math.floor(Date.now() / 1000));
|
||||
this.Stamina.getChildByName("health").active = true;
|
||||
this.Stamina.getChildByName("time").active = false;
|
||||
NumberToImage.numberToImageNodes(cc.fx.GameConfig.GM_INFO.hp, 25, 15, "hp_", this.Stamina.getChildByName("health"), false);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ class Node {
|
|||
addChild(node) { node.parent = this; }
|
||||
getChildByName(name) { return this.children.find(n => n.name === name) || null; }
|
||||
removeAllChildren() { [...this.children].forEach(n => n.parent = null); }
|
||||
destroyAllChildren() { [...this.children].forEach(n => n.destroy()); }
|
||||
setContentSize(width, height) { this.width = width; this.height = height; }
|
||||
destroy() { this.destroyed = true; this.parent = null; }
|
||||
setPosition(x, y) { this.x = x; this.y = y; }
|
||||
setAnchorPoint(x, y) { this.anchorX = x; this.anchorY = y; }
|
||||
|
|
@ -34,6 +36,7 @@ class Node {
|
|||
function runtime() {
|
||||
class Component { scheduleOnce() {} }
|
||||
class Sprite extends Component {}
|
||||
Sprite.SizeMode = { TRIMMED: 0 };
|
||||
class Button extends Component {}
|
||||
class ScrollView extends Component { getMaxScrollOffset() { return { y: this.content.height }; } getScrollOffset() { return { y: 0 }; } }
|
||||
class NodePool { nodes = []; size() { return this.nodes.length; } get() { return this.nodes.pop(); } put(node) { node.parent = null; this.nodes.push(node); } }
|
||||
|
|
@ -61,12 +64,176 @@ function runtime() {
|
|||
}
|
||||
const item = node.addComponent(Item);
|
||||
item.passCheckItemLvPrefab = () => { const level = new Node('level'); child(level, 'bright_bg').addComponent(Sprite); child(level, 'levelNum'); return level; };
|
||||
item.uiAtlas = item.texture_atlas = { getSpriteFrame: name => name };
|
||||
item.uiAtlas = item.texture_atlas = item.passCheckFont = { getSpriteFrame: name => name };
|
||||
return node;
|
||||
};
|
||||
return { cc, Item, Pool, List, Manager, child, itemPrefab };
|
||||
}
|
||||
const stageAt = (level, tier) => model.normalizePass({ time: '100', experience: rows.slice(0, level).reduce((sum, row) => sum + row.token, 0), tier, activate: tier > 0 }, rows);
|
||||
|
||||
function animationFixture() {
|
||||
const { cc, Manager } = runtime();
|
||||
const storage = new Map(), actions = [], stopped = new Set();
|
||||
cc.fx.StorageMessage = { getStorage: key => storage.get(key), setStorage: (key, value) => storage.set(key, value) };
|
||||
cc.fx.GameConfig.GM_INFO = { uid: 'animation-player', getItemType: 2 };
|
||||
cc.fx.GameConfig.PASS_CHECK_OLD = JSON.parse(fs.readFileSync('assets/resources/Json/PASS_CHECK_OLD.json', 'utf8')).Sheet1;
|
||||
cc.Tween.stopAllByTarget = target => actions.filter(a => a.target === target).forEach(a => stopped.add(a));
|
||||
cc.tween = target => {
|
||||
const steps = [];
|
||||
const chain = { delay: seconds => { steps.push({ seconds }); return chain; },
|
||||
to: (seconds, values) => { steps.push({ seconds, values }); return chain; },
|
||||
call: fn => { steps.push({ fn }); return chain; },
|
||||
start: () => { actions.push({ target, steps }); return chain; } };
|
||||
return chain;
|
||||
};
|
||||
const writeNumber = (value, _w, _h, _prefix, node) => { node.number = value; };
|
||||
const Panel = load('assets/shop/script/passCheck.ts', {
|
||||
'../../Script/module/Config/BattlePassModel': model,
|
||||
'../../Script/NumberToImage': { default: { numberToImageNodesPassCheck: writeNumber, numberToImageNodesShop: writeNumber } },
|
||||
}, { cc }).default;
|
||||
const panel = new Panel(), manager = new Manager();
|
||||
Object.assign(panel, { activateBtn: { node: {} }, currentLv: new Node(), progress_1: new Node(), progress_2: new Node(),
|
||||
barSpr: { node: {}, fillRange: 0 }, setbarNodePosX() {}, initCountDown() {} });
|
||||
const content = new Node();
|
||||
Object.assign(manager, { list: { itemHeight: 350, spacingY: 0, paddingTop: 0, paddingBottom: 0 },
|
||||
progressBar3: new Node(), progressBar4: new Node() });
|
||||
manager.progressBar3.parent = manager.progressBar4.parent = content;
|
||||
// Use the real panel -> manager init arguments, but skip unrelated reward rendering and scrolling.
|
||||
manager.scrollToLastClaimedReward = () => {};
|
||||
manager.setupProgressBars = () => manager.updateProgressBars();
|
||||
const list = manager.list;
|
||||
Object.assign(list, { init() {}, setData(data) { manager.listData = data; } });
|
||||
const scroll = new Node('scrollView');
|
||||
scroll.getComponent = type => type === cc.ScrollView ? { content } : list;
|
||||
manager.node = new Node('scrollViewNode'); manager.node.addChild(scroll);
|
||||
panel.node = { getChildByName: () => ({ active: true, getComponent: () => manager }) };
|
||||
function show(level, partial = 0, options = {}) {
|
||||
const season = options.season || '2000000000000', tier = options.tier || 0;
|
||||
cc.fx.GameConfig.GM_INFO.getItemType = options.old ? 1 : 2;
|
||||
const config = options.old ? cc.fx.GameConfig.PASS_CHECK_OLD : rows;
|
||||
const stage = { time: season, experience: config.slice(0, level).reduce((sum, row) => sum + row.token, 0) + partial,
|
||||
free: Array(level).fill(0), passCheck: Array(level).fill(1), tier, activate: tier > 0 };
|
||||
const input = { 1: options.old ? stage : { time: '0' }, 2: options.old ? { time: '0' } : stage };
|
||||
const before = JSON.stringify(input);
|
||||
actions.length = 0; stopped.clear(); panel.onInit(input);
|
||||
assert.equal(JSON.stringify(input), before, 'animation must not mutate XP or claim data');
|
||||
return actions;
|
||||
}
|
||||
const topFills = () => actions.filter(a => a.target === panel.barSpr && !stopped.has(a))
|
||||
.flatMap(a => a.steps.filter(s => s.values).map(s => s.values.fillRange));
|
||||
function finish() { for (const action of actions) if (!stopped.has(action)) for (const step of action.steps) {
|
||||
if (step.values) Object.assign(action.target, step.values); if (step.fn) step.fn();
|
||||
} }
|
||||
return { cc, panel, manager, storage, actions, stopped, show, topFills, finish };
|
||||
}
|
||||
|
||||
test('battle pass animation ignores unscoped history and preserves already claimed tier five', () => {
|
||||
const f = animationFixture(); f.storage.set('ProgressIndex', 4);
|
||||
f.show(7);
|
||||
assert.equal(f.actions.length, 0);
|
||||
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.storage.get('ProgressIndex'), 4);
|
||||
});
|
||||
|
||||
test('battle pass animation plays only new completed tiers and does not repeat on reopening', () => {
|
||||
const f = animationFixture(); f.show(4); f.show(7);
|
||||
assert.deepEqual(f.topFills(), [1, 1, 1]);
|
||||
assert.equal(f.manager.progressBar4.height, 3 * 350);
|
||||
f.finish(); assert.equal(f.manager.progressBar4.height, 6 * 350);
|
||||
assert.equal(f.panel.barSpr.fillRange, 0);
|
||||
f.show(7); assert.equal(f.actions.length, 0);
|
||||
});
|
||||
|
||||
test('battle pass partial progress animates within a tier without moving the completed-tier bar', () => {
|
||||
const f = animationFixture(); f.show(5, 1); f.show(5, 2);
|
||||
assert.equal(f.panel.barSpr.fillRange, 0.25);
|
||||
assert.deepEqual(f.topFills(), [0.5]);
|
||||
assert.equal(f.actions.length, 1);
|
||||
f.finish(); assert.equal(f.panel.barSpr.fillRange, 0.5);
|
||||
assert.equal(f.panel.progress_1.number, 2); assert.equal(f.panel.progress_2.number, 4);
|
||||
f.show(5, 2); assert.equal(f.actions.length, 0); assert.equal(f.panel.barSpr.fillRange, 0.5);
|
||||
});
|
||||
|
||||
test('battle pass crosses a tier from its saved fraction and finishes at the new fraction', () => {
|
||||
const f = animationFixture(); f.show(5, 3); f.show(6, 1);
|
||||
assert.equal(f.panel.barSpr.fillRange, 0.75);
|
||||
assert.deepEqual(f.topFills(), [1, 0.25]);
|
||||
f.finish(); assert.equal(f.panel.barSpr.fillRange, 0.25);
|
||||
assert.equal(f.manager.progressBar4.height, 5 * 350);
|
||||
});
|
||||
|
||||
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);
|
||||
f.show(7); assert.deepEqual(f.topFills(), [1, 1]);
|
||||
f.show(4, 0, { old: true, season: '1900000000000' }); assert.equal(f.actions.length, 0);
|
||||
f.show(7); assert.equal(f.actions.length, 0);
|
||||
f.show(1, 0, { season: '2100000000000' }); assert.equal(f.actions.length, 0);
|
||||
f.cc.fx.GameConfig.GM_INFO.uid = 'another-player'; f.show(7); assert.equal(f.actions.length, 0);
|
||||
});
|
||||
|
||||
test('battle pass cap stays full and gaining experience above the cap does not replay', () => {
|
||||
for (const tier of [0, 18, 30]) {
|
||||
const f = animationFixture(), cap = tier === 30 ? rows.length : 35;
|
||||
f.show(cap - 1, 0, { tier }); f.show(cap, 0, { tier });
|
||||
assert.deepEqual(f.topFills(), [1]); f.finish(); assert.equal(f.panel.barSpr.fillRange, 1);
|
||||
f.show(cap, 1, { tier }); assert.equal(f.actions.length, 0); assert.equal(f.panel.barSpr.fillRange, 1);
|
||||
}
|
||||
});
|
||||
|
||||
test('battle pass invalid or ahead-of-server records show the current state without a backward replay', () => {
|
||||
const f = animationFixture(), key = 'PassProgress:animation-player:2000000000000';
|
||||
for (const value of [null, 4, { completed: -1, fill: 0 }, { completed: 7, fill: 0 }, { completed: 5, fill: NaN }, { completed: 5, fill: 0.9 }]) {
|
||||
f.storage.set(key, value); f.show(5, 1);
|
||||
assert.equal(f.actions.length, 0); assert.equal(f.panel.barSpr.fillRange, 0.25);
|
||||
}
|
||||
});
|
||||
|
||||
test('battle pass refresh and destroy stop pending progress animations', () => {
|
||||
const f = animationFixture(); f.show(4); f.show(7);
|
||||
const previous = [...f.actions];
|
||||
// Refresh without removing pending tween handles from the test scheduler.
|
||||
f.panel.onInit({ 1: { time: '0' }, 2: { time: '2000000000000', experience: 16, free: [], passCheck: [] } });
|
||||
previous.forEach(action => assert.equal(f.stopped.has(action), true));
|
||||
f.finish(); assert.equal(f.panel.barSpr.fillRange, 0); assert.equal(f.manager.progressBar4.height, 6 * 350);
|
||||
f.show(8); f.panel.onDestroy(); f.manager.onDestroy();
|
||||
f.actions.forEach(action => assert.equal(f.stopped.has(action), true));
|
||||
});
|
||||
|
||||
test('battle pass one/two XP increments agree between both bars at every early-tier boundary', () => {
|
||||
for (const tier of [0, 18, 30]) {
|
||||
const f = animationFixture();
|
||||
let previous;
|
||||
for (let xp = 0; xp <= 100; xp += tier === 30 ? 2 : 1) {
|
||||
const stage = model.normalizePass({ time: '100', experience: xp }, rows);
|
||||
const level = stage.unlocked;
|
||||
const partial = xp - rows.slice(0, level).reduce((sum, row) => sum + row.token, 0);
|
||||
f.show(level, partial, { tier });
|
||||
const fills = f.topFills();
|
||||
if (previous !== undefined) assert.equal(fills.filter(value => value === 1).length, level - previous);
|
||||
f.finish();
|
||||
assert.equal(f.manager.progressBar4.height, Math.max(0, level - 1) * 350);
|
||||
assert.equal(f.panel.barSpr.fillRange, partial / rows[level].token);
|
||||
previous = level;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('battle pass zero XP, cost changes and premium extension keep progress display consistent', () => {
|
||||
const f = animationFixture();
|
||||
f.show(0, 0, { old: true, season: '1900000000000' });
|
||||
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);
|
||||
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();
|
||||
assert.equal(f.manager.progressBar4.height, 35 * 350);
|
||||
});
|
||||
function listData(count, tier) { return Array.from({ length: count }, (_, i) => ({ id: i, ItemID1: 1001, ItemNum1: 1500, ItemID2: 1001, ItemNum2: 500, free: 1, passCheck: 1, leftAllowed: i < 35 || tier === 30, rightAllowed: i < 35 ? tier >= 18 : tier === 30 })); }
|
||||
|
||||
test('free and VIP preview 40 with clickable masks after 35, pooled SVIP rows hide every mask', () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ const plain = value => JSON.parse(JSON.stringify(value));
|
|||
function load(path, deps = {}, globals = {}) {
|
||||
const exports = {};
|
||||
const code = ts.transpileModule(fs.readFileSync(path, 'utf8'), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 } }).outputText;
|
||||
vm.runInNewContext(code, { exports, require: name => deps[name], console, Date, setTimeout, clearTimeout, ...globals });
|
||||
vm.runInNewContext(code, { exports, require: name => deps[name], console, Date, setTimeout, clearTimeout,
|
||||
process: { env: { PASSCHECKTIME_ID: '69fac1528463a95668c5e5db' } }, ...globals });
|
||||
return exports;
|
||||
}
|
||||
const model = load('assets/Script/module/Config/BattlePassModel.ts');
|
||||
|
|
|
|||
90
tools/test-shop-stamina.cjs
Normal file
90
tools/test-shop-stamina.cjs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { loadMembers } = require('./new-player-ab-test-support.cjs');
|
||||
|
||||
function fixture(hp = 5, powerSeconds = 60) {
|
||||
let now = 1000;
|
||||
const info = { hp, hp_Max: 5, userPowerTime: powerSeconds ? now + powerSeconds : 0,
|
||||
min_Time: 120, useravatarIcon: '', useravaterkuang: '', coin: 100 };
|
||||
const node = () => {
|
||||
const children = new Map(), component = {};
|
||||
return { active: true, opacity: 255, getChildByName(name) {
|
||||
if (!children.has(name)) children.set(name, node());
|
||||
return children.get(name);
|
||||
}, getComponent: () => component };
|
||||
};
|
||||
const cc = { sys: { platform: 'browser', WECHAT_GAME: 'wechat', BYTEDANCE_GAME: 'bytedance' },
|
||||
Sprite: {}, Label: {}, find: () => ({ getComponent: () => null }), fx: {
|
||||
GameConfig: { GM_INFO: info }, GameTool: {
|
||||
getUserPowerTime() { if (info.userPowerTime <= now) { info.userPowerTime = 0; return false; } return true; },
|
||||
getTimeMargin: value => String(value), getTimeMargin2: value => String(value),
|
||||
getActionName() {}, loadSpineSimple() {}, getSetScreenResolutionFlag: () => false,
|
||||
},
|
||||
} };
|
||||
const Shop = loadMembers('assets/shop/script/shop.ts', [
|
||||
'init', 'setHealthInfo', 'updatePower', 'startPowerTime', 'stopPowerTime',
|
||||
'startTimeCutDown', 'stopTimeCutDown', 'removeOnShowListener',
|
||||
], { cc, Date: { now: () => now * 1000 }, SceneManager: {}, MapConroler: {},
|
||||
NumberToImage: { numberToImageNodes() {}, numberToImageNodesCoin() {} }, console: { log() {} } });
|
||||
const shop = new Shop(), scheduled = new Set();
|
||||
Object.assign(shop, { node: node(), Stamina: node(), coin: node(), ui: { getSpriteFrame() {} },
|
||||
refreshJungleEntryLayout() {}, openShop() {}, updateIcon() {},
|
||||
schedule: callback => scheduled.add(callback), unschedule: callback => scheduled.delete(callback) });
|
||||
const get = name => shop.Stamina.getChildByName(name);
|
||||
const open = () => { shop.node.active = true; shop.init({}); };
|
||||
const tick = seconds => { now += seconds; [...scheduled].forEach(callback => callback()); };
|
||||
return { shop, info, scheduled, get, open, tick, advance: seconds => { now += seconds; } };
|
||||
}
|
||||
|
||||
test('full stamina and infinite stamina never show both labels when repeatedly reopening the cached shop', () => {
|
||||
const f = fixture();
|
||||
for (let i = 0; i < 3; i++) {
|
||||
f.open();
|
||||
assert.equal(f.get('man').active, false, 'full label must already be hidden before the first timer tick');
|
||||
assert.equal(f.get('skyLine').active, true);
|
||||
assert.equal(f.scheduled.size, 1, 'reopening must not duplicate the infinite stamina timer');
|
||||
f.tick(1);
|
||||
assert.equal(f.get('man').active, false);
|
||||
assert.equal(f.get('skyLine').getChildByName('skyTime').getComponent().string, String(59 - i));
|
||||
f.shop.node.active = false;
|
||||
}
|
||||
});
|
||||
|
||||
test('a later ordinary health refresh cannot cover the infinite stamina countdown', () => {
|
||||
const f = fixture(); f.open(); f.tick(1); f.shop.setHealthInfo(true);
|
||||
assert.equal(f.get('man').active, false);
|
||||
assert.equal(f.get('skyLine').active, true);
|
||||
assert.equal(f.info.hp, 5);
|
||||
});
|
||||
|
||||
test('full stamina without infinite stamina still displays full immediately', () => {
|
||||
const f = fixture(5, 0);
|
||||
for (let i = 0; i < 2; i++) {
|
||||
f.open(); assert.equal(f.get('man').active, true); assert.equal(f.get('skyLine').active, false);
|
||||
assert.equal(f.get('time').active, false); assert.equal(f.scheduled.size, 0);
|
||||
}
|
||||
});
|
||||
|
||||
test('infinite stamina expiring while the shop is closed restores the full label on reopening', () => {
|
||||
const f = fixture(5, 2); f.open(); f.shop.node.active = false; f.advance(2); f.open();
|
||||
assert.equal(f.get('skyLine').active, false); assert.equal(f.get('man').active, true);
|
||||
assert.equal(f.scheduled.size, 0);
|
||||
});
|
||||
|
||||
test('infinite stamina expiring in the open shop restores the full label', () => {
|
||||
const f = fixture(5, 2); f.open(); f.tick(2);
|
||||
assert.equal(f.get('skyLine').active, false); assert.equal(f.get('man').active, true);
|
||||
assert.equal(f.scheduled.size, 0);
|
||||
});
|
||||
|
||||
test('non-full stamina preserves ordinary and infinite countdown visibility', () => {
|
||||
for (const power of [0, 60]) {
|
||||
const f = fixture(3, power); f.open();
|
||||
assert.equal(f.get('man').active, false);
|
||||
assert.equal(f.get('skyLine').active, power > 0);
|
||||
assert.equal(f.get('time').active, true);
|
||||
assert.equal(f.get('time').opacity, power ? 0 : 255);
|
||||
assert.equal(f.get('progresss').getComponent().fillRange, 3 / 5);
|
||||
assert.equal(f.info.hp, 3);
|
||||
}
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user