更新一个 关卡时间AB方案
This commit is contained in:
@@ -4,6 +4,7 @@ import { GAMEPLAY_ATLAS_PATHS } from "../Tool/GameplayAtlasFrames";
|
||||
import { MiniGameSdk } from "../../Sdk/MiniGameSdk";
|
||||
import { GameTool } from "../Tool/GameTool";
|
||||
import BlockAssetManager from "../Tool/BlockAssetManager";
|
||||
import { LEVEL_TIME_BONUS } from "./LevelTimeBonusConfig";
|
||||
|
||||
interface GameplayEntryPreparation {
|
||||
startedAt: number;
|
||||
@@ -1365,7 +1366,11 @@ export class GameConfig {
|
||||
static applyLevelData(jsonData: any, type: any, time: any, levelNumber = Number(jsonData.LEVEL_INFO[0].id), preparation?: GameplayEntryPreparation) {
|
||||
this.loadedLevelNumber = levelNumber;
|
||||
this.BLOCK_INFO = jsonData["BLOCK_INFO"];
|
||||
this.LEVEL_INFO = jsonData["LEVEL_INFO"];
|
||||
const extraTime = this.GM_INFO?.abTests?.layer_three === 2 ? LEVEL_TIME_BONUS[levelNumber] || 0 : 0;
|
||||
// 只调整本局时限,保留资源缓存中的原始时间,避免重开时重复加时。
|
||||
this.LEVEL_INFO = extraTime
|
||||
? jsonData["LEVEL_INFO"].map((info, index) => index === 0 ? { ...info, time: info.time + extraTime } : info)
|
||||
: jsonData["LEVEL_INFO"];
|
||||
this.WALL_INFO = jsonData["WALL_INFO"];
|
||||
console.log("关卡配置加载完成");
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// layer_three B 组的逐关加时,单位:秒。
|
||||
export const LEVEL_TIME_BONUS: { readonly [level: number]: number } = {
|
||||
114: 30,
|
||||
132: 20,
|
||||
222: 10,
|
||||
259: 30,
|
||||
302: 30,
|
||||
317: 20,
|
||||
329: 10,
|
||||
383: 30,
|
||||
400: 30,
|
||||
411: 20,
|
||||
416: 20,
|
||||
422: 20,
|
||||
426: 30,
|
||||
433: 30,
|
||||
444: 40,
|
||||
452: 30,
|
||||
504: 30,
|
||||
517: 40,
|
||||
521: 20,
|
||||
524: 30,
|
||||
535: 50,
|
||||
536: 20,
|
||||
542: 20,
|
||||
548: 30,
|
||||
557: 20,
|
||||
563: 20,
|
||||
566: 20,
|
||||
595: 10,
|
||||
604: 20,
|
||||
612: 20,
|
||||
630: 30,
|
||||
648: 20,
|
||||
651: 30,
|
||||
666: 20,
|
||||
688: 30,
|
||||
690: 30,
|
||||
745: 20,
|
||||
748: 40,
|
||||
750: 30,
|
||||
789: 20,
|
||||
798: 30,
|
||||
799: 30,
|
||||
828: 20,
|
||||
868: 20,
|
||||
881: 20,
|
||||
895: 30,
|
||||
916: 20,
|
||||
962: 30,
|
||||
963: 40,
|
||||
981: 20,
|
||||
991: 20,
|
||||
1003: 30,
|
||||
1008: 20,
|
||||
1023: 20,
|
||||
1024: 20,
|
||||
1026: 30,
|
||||
1027: 30,
|
||||
1043: 20,
|
||||
1061: 20,
|
||||
1063: 30,
|
||||
1077: 20,
|
||||
1087: 20,
|
||||
1093: 20,
|
||||
1107: 20,
|
||||
1122: 40,
|
||||
1123: 40,
|
||||
1129: 20,
|
||||
1137: 10,
|
||||
1139: 30,
|
||||
1143: 30,
|
||||
1146: 20,
|
||||
1154: 40,
|
||||
1164: 20,
|
||||
1166: 30,
|
||||
1171: 40,
|
||||
1173: 20,
|
||||
1175: 10,
|
||||
1185: 30,
|
||||
1195: 20,
|
||||
1202: 20,
|
||||
1204: 20,
|
||||
1205: 30,
|
||||
1206: 20,
|
||||
1207: 40,
|
||||
1209: 20,
|
||||
1210: 30,
|
||||
1212: 40,
|
||||
1222: 60,
|
||||
1225: 30,
|
||||
1227: 20,
|
||||
1228: 20,
|
||||
1239: 30,
|
||||
1241: 30,
|
||||
1242: 40,
|
||||
1264: 20,
|
||||
1266: 30,
|
||||
1268: 40,
|
||||
1275: 60,
|
||||
1279: 40,
|
||||
1281: 40,
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "df6e40f7-1279-4724-afe3-55fade9817c0",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const { loadMembers, loadModule, resolveFile } = require('./new-player-ab-test-support.cjs');
|
||||
const { LEVEL_TIME_BONUS } = loadModule('assets/Script/module/Config/LevelTimeBonusConfig.ts');
|
||||
const configFile = 'assets/Script/module/Config/GameConfig.ts';
|
||||
const readLevel = level => JSON.parse(fs.readFileSync(resolveFile(`assets/custom/Json/level${level}.json`), 'utf8'));
|
||||
const plain = value => JSON.parse(JSON.stringify(value));
|
||||
|
||||
function config(info = {}) {
|
||||
const Config = loadMembers(configFile, ['applyLevelData', 'LEVEL_INFO_init', 'getLevelConfigPath'], {
|
||||
LEVEL_TIME_BONUS, console: { log() {} }, cc,
|
||||
});
|
||||
Config.GM_INFO = info;
|
||||
return Config;
|
||||
}
|
||||
|
||||
// Exercise the actual loader so file numbers, help levels and endless levels use the same key.
|
||||
const requests = [];
|
||||
const cc = {
|
||||
v2: (x, y) => ({ x, y }), director: { getScene: () => null },
|
||||
fx: { GameTool: { maxLevel: () => false } },
|
||||
assetManager: {
|
||||
loadBundle(_name, _options, done) { done(null, {}); },
|
||||
loadAny(request, _options, done) {
|
||||
requests.push(request.path);
|
||||
done(null, { json: readLevel(Number(request.path.replace('Json/level', ''))) });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
test('A, unassigned and other groups retain the original time for every configured level', () => {
|
||||
assert.equal(Object.keys(LEVEL_TIME_BONUS).length, 100);
|
||||
for (const info of [undefined, {}, { abTests: {} }, ...[0, 1, 3].map(layer_three => ({ abTests: { layer_three } }))]) {
|
||||
const Config = config(info);
|
||||
for (const level of Object.keys(LEVEL_TIME_BONUS)) {
|
||||
const data = readLevel(level);
|
||||
Config.applyLevelData(data, false, 0, Number(level));
|
||||
assert.equal(Config.LEVEL_INFO[0].time, data.LEVEL_INFO[0].time);
|
||||
assert.equal(Config.LEVEL_INFO, data.LEVEL_INFO);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('B adds time once, preserves cached data and returns to original time when switched to A', () => {
|
||||
const Config = config({ abTests: { layer_three: 2 } });
|
||||
for (const [level, bonus] of Object.entries(LEVEL_TIME_BONUS)) {
|
||||
assert.ok(Number.isInteger(bonus) && bonus > 0);
|
||||
const data = readLevel(level), before = structuredClone(data);
|
||||
Config.GM_INFO.abTests.layer_three = 2;
|
||||
for (let retry = 0; retry < 2; retry++) {
|
||||
Config.applyLevelData(data, false, 0, Number(level));
|
||||
const expected = structuredClone(before.LEVEL_INFO);
|
||||
expected[0].time += bonus;
|
||||
assert.deepEqual(plain(Config.LEVEL_INFO), expected);
|
||||
assert.equal(Config.BLOCK_INFO, data.BLOCK_INFO);
|
||||
assert.equal(Config.WALL_INFO, data.WALL_INFO);
|
||||
assert.deepEqual(data, before);
|
||||
}
|
||||
Config.GM_INFO.abTests.layer_three = 1;
|
||||
Config.applyLevelData(data, false, 0, Number(level));
|
||||
assert.equal(Config.LEVEL_INFO[0].time, before.LEVEL_INFO[0].time);
|
||||
}
|
||||
});
|
||||
|
||||
test('B leaves unlisted levels unchanged and uses the file number instead of a stale internal id', () => {
|
||||
const Config = config({ abTests: { layer_three: 2 } });
|
||||
for (const level of [1, 113, 115, 1282]) {
|
||||
const data = readLevel(level);
|
||||
Config.applyLevelData(data, false, 0, level);
|
||||
assert.equal(Config.LEVEL_INFO, data.LEVEL_INFO);
|
||||
}
|
||||
const data = readLevel(222);
|
||||
assert.equal(data.LEVEL_INFO[0].id, '158');
|
||||
Config.applyLevelData(data, false, 0, 222);
|
||||
assert.equal(Config.LEVEL_INFO[0].time, 70);
|
||||
assert.equal(Config.LEVEL_INFO[0].id, '158');
|
||||
});
|
||||
|
||||
test('loader applies B bonuses to the requested mainline, help and endless level', () => {
|
||||
for (const mode of ['mainline', 'help', 'endless']) {
|
||||
const Config = config({ level: mode === 'mainline' ? 221 : 0,
|
||||
otherLevel: mode === 'help' ? 222 : 0, randomLevel: 222, abTests: { layer_three: 2 } });
|
||||
cc.fx.GameConfig = Config;
|
||||
Config.levelLoadRequest = Config.sceneLoadRequest = 0;
|
||||
Config.takeEntryDownloadRelease = () => null;
|
||||
Config.LEVEL_INFO_init(false, 0, mode === 'help', mode === 'endless');
|
||||
assert.equal(requests.at(-1), 'Json/level222');
|
||||
assert.equal(Config.LEVEL_INFO[0].time, 70, mode);
|
||||
}
|
||||
});
|
||||
|
||||
test('server A/B assignments select original and bonus times before entering the scene', () => {
|
||||
const Manager = loadMembers('assets/Script/GameManager.ts', ['setABTests'], { cc });
|
||||
const manager = new Manager();
|
||||
const Config = config();
|
||||
cc.fx.GameConfig = Config;
|
||||
const seen = [];
|
||||
Config.enterGameSceneWhenReady = (time, preparation) => seen.push([Config.LEVEL_INFO[0].time, time, preparation]);
|
||||
const preparation = {};
|
||||
for (const [group, expected] of [['A', 135], ['B', 165], [1, 135], [2, 165]]) {
|
||||
manager.setABTests({ layer_3: { group } });
|
||||
Config.applyLevelData(readLevel(114), true, 0.5, 114, preparation);
|
||||
assert.deepEqual(seen.at(-1), [expected, 0.5, preparation]);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user