Compare commits
3 Commits
2f76e6a3ca
...
0de72a629c
| Author | SHA1 | Date | |
|---|---|---|---|
| 0de72a629c | |||
| 01bc52502f | |||
| 09aa034f52 |
@@ -84,7 +84,6 @@ _TD.a.push(function (TD) {
|
|||||||
"laser_gun": {
|
"laser_gun": {
|
||||||
damage: 25,
|
damage: 25,
|
||||||
range: 6,
|
range: 6,
|
||||||
max_range: 10,
|
|
||||||
speed: 20,
|
speed: 20,
|
||||||
// bullet_speed: 10, // laser_gun 的 bullet_speed 属性没有用
|
// bullet_speed: 10, // laser_gun 的 bullet_speed 属性没有用
|
||||||
life: 100,
|
life: 100,
|
||||||
|
|||||||
@@ -20,7 +20,20 @@
|
|||||||
for (var i = 0; i < map.buildings.length; i++) {
|
for (var i = 0; i < map.buildings.length; i++) {
|
||||||
var b = map.buildings[i];
|
var b = map.buildings[i];
|
||||||
if (!b || !b.grid) continue;
|
if (!b || !b.grid) continue;
|
||||||
buildings.push({ type: b.type, mx: b.grid.mx, my: b.grid.my, level: b.level, money: b.money });
|
buildings.push({
|
||||||
|
type: b.type,
|
||||||
|
mx: b.grid.mx,
|
||||||
|
my: b.grid.my,
|
||||||
|
level: b.level,
|
||||||
|
money: b.money,
|
||||||
|
killed: b.killed,
|
||||||
|
damage: b.damage,
|
||||||
|
range: b.range,
|
||||||
|
speed: b.speed,
|
||||||
|
life: b.life,
|
||||||
|
shield: b.shield,
|
||||||
|
upgrade_records: b._upgrade_records || null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,11 +171,39 @@
|
|||||||
var grid = map.getGrid(bi.mx, bi.my);
|
var grid = map.getGrid(bi.mx, bi.my);
|
||||||
if (grid) grid.addBuilding(bi.type);
|
if (grid) grid.addBuilding(bi.type);
|
||||||
var b = grid && grid.building;
|
var b = grid && grid.building;
|
||||||
if (b) {
|
if (b) {
|
||||||
if (typeof bi.level !== 'undefined') b.level = bi.level;
|
var hasSavedBuildingAttrs = (
|
||||||
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
typeof bi.damage !== 'undefined' ||
|
||||||
b.updateBtnDesc && b.updateBtnDesc();
|
typeof bi.range !== 'undefined' ||
|
||||||
}
|
typeof bi.speed !== 'undefined' ||
|
||||||
|
typeof bi.life !== 'undefined' ||
|
||||||
|
typeof bi.shield !== 'undefined'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasSavedBuildingAttrs && typeof bi.level !== 'undefined' && bi.level > 0 && typeof b.upgrade === 'function') {
|
||||||
|
for (var ul = 0; ul < bi.level; ul++) b.upgrade();
|
||||||
|
} else if (typeof bi.level !== 'undefined') {
|
||||||
|
b.level = bi.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof bi.damage !== 'undefined') b.damage = bi.damage;
|
||||||
|
if (typeof bi.range !== 'undefined') b.range = bi.range;
|
||||||
|
if (typeof bi.speed !== 'undefined') b.speed = bi.speed;
|
||||||
|
if (typeof bi.life !== 'undefined') b.life = bi.life;
|
||||||
|
if (typeof bi.shield !== 'undefined') b.shield = bi.shield;
|
||||||
|
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
||||||
|
if (typeof bi.killed !== 'undefined') b.killed = bi.killed;
|
||||||
|
if (bi.upgrade_records) {
|
||||||
|
b._upgrade_records = {};
|
||||||
|
for (var rk in bi.upgrade_records) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(bi.upgrade_records, rk)) {
|
||||||
|
b._upgrade_records[rk] = bi.upgrade_records[rk];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.range_px = b.range * TD.grid_size;
|
||||||
|
b.updateBtnDesc && b.updateBtnDesc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,20 @@ window.__TD_getState = function () {
|
|||||||
for (var i = 0; i < map.buildings.length; i++) {
|
for (var i = 0; i < map.buildings.length; i++) {
|
||||||
var b = map.buildings[i];
|
var b = map.buildings[i];
|
||||||
if (!b || !b.grid) continue;
|
if (!b || !b.grid) continue;
|
||||||
buildings.push({ type: b.type, mx: b.grid.mx, my: b.grid.my, level: b.level, money: b.money });
|
buildings.push({
|
||||||
|
type: b.type,
|
||||||
|
mx: b.grid.mx,
|
||||||
|
my: b.grid.my,
|
||||||
|
level: b.level,
|
||||||
|
money: b.money,
|
||||||
|
killed: b.killed,
|
||||||
|
damage: b.damage,
|
||||||
|
range: b.range,
|
||||||
|
speed: b.speed,
|
||||||
|
life: b.life,
|
||||||
|
shield: b.shield,
|
||||||
|
upgrade_records: b._upgrade_records || null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,11 +170,39 @@ window.__TD_loadState = function (s) {
|
|||||||
var grid = map.getGrid(bi.mx, bi.my);
|
var grid = map.getGrid(bi.mx, bi.my);
|
||||||
if (grid) grid.addBuilding(bi.type);
|
if (grid) grid.addBuilding(bi.type);
|
||||||
var b = grid && grid.building;
|
var b = grid && grid.building;
|
||||||
if (b) {
|
if (b) {
|
||||||
if (typeof bi.level !== 'undefined') b.level = bi.level;
|
var hasSavedBuildingAttrs = (
|
||||||
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
typeof bi.damage !== 'undefined' ||
|
||||||
b.updateBtnDesc && b.updateBtnDesc();
|
typeof bi.range !== 'undefined' ||
|
||||||
}
|
typeof bi.speed !== 'undefined' ||
|
||||||
|
typeof bi.life !== 'undefined' ||
|
||||||
|
typeof bi.shield !== 'undefined'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasSavedBuildingAttrs && typeof bi.level !== 'undefined' && bi.level > 0 && typeof b.upgrade === 'function') {
|
||||||
|
for (var ul = 0; ul < bi.level; ul++) b.upgrade();
|
||||||
|
} else if (typeof bi.level !== 'undefined') {
|
||||||
|
b.level = bi.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof bi.damage !== 'undefined') b.damage = bi.damage;
|
||||||
|
if (typeof bi.range !== 'undefined') b.range = bi.range;
|
||||||
|
if (typeof bi.speed !== 'undefined') b.speed = bi.speed;
|
||||||
|
if (typeof bi.life !== 'undefined') b.life = bi.life;
|
||||||
|
if (typeof bi.shield !== 'undefined') b.shield = bi.shield;
|
||||||
|
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
||||||
|
if (typeof bi.killed !== 'undefined') b.killed = bi.killed;
|
||||||
|
if (bi.upgrade_records) {
|
||||||
|
b._upgrade_records = {};
|
||||||
|
for (var rk in bi.upgrade_records) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(bi.upgrade_records, rk)) {
|
||||||
|
b._upgrade_records[rk] = bi.upgrade_records[rk];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.range_px = b.range * TD.grid_size;
|
||||||
|
b.updateBtnDesc && b.updateBtnDesc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -268,7 +268,20 @@ var _TD = {
|
|||||||
for (var i = 0; i < map.buildings.length; i++) {
|
for (var i = 0; i < map.buildings.length; i++) {
|
||||||
var b = map.buildings[i];
|
var b = map.buildings[i];
|
||||||
if (!b || !b.grid) continue;
|
if (!b || !b.grid) continue;
|
||||||
buildings.push({ type: b.type, mx: b.grid.mx, my: b.grid.my, level: b.level, money: b.money });
|
buildings.push({
|
||||||
|
type: b.type,
|
||||||
|
mx: b.grid.mx,
|
||||||
|
my: b.grid.my,
|
||||||
|
level: b.level,
|
||||||
|
money: b.money,
|
||||||
|
killed: b.killed,
|
||||||
|
damage: b.damage,
|
||||||
|
range: b.range,
|
||||||
|
speed: b.speed,
|
||||||
|
life: b.life,
|
||||||
|
shield: b.shield,
|
||||||
|
upgrade_records: b._upgrade_records || null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,11 +371,39 @@ var _TD = {
|
|||||||
var grid = map.getGrid(bi.mx, bi.my);
|
var grid = map.getGrid(bi.mx, bi.my);
|
||||||
if (grid) grid.addBuilding(bi.type);
|
if (grid) grid.addBuilding(bi.type);
|
||||||
var b = grid && grid.building;
|
var b = grid && grid.building;
|
||||||
if (b) {
|
if (b) {
|
||||||
if (typeof bi.level !== 'undefined') b.level = bi.level;
|
var hasSavedBuildingAttrs = (
|
||||||
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
typeof bi.damage !== 'undefined' ||
|
||||||
b.updateBtnDesc && b.updateBtnDesc();
|
typeof bi.range !== 'undefined' ||
|
||||||
}
|
typeof bi.speed !== 'undefined' ||
|
||||||
|
typeof bi.life !== 'undefined' ||
|
||||||
|
typeof bi.shield !== 'undefined'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasSavedBuildingAttrs && typeof bi.level !== 'undefined' && bi.level > 0 && typeof b.upgrade === 'function') {
|
||||||
|
for (var ul = 0; ul < bi.level; ul++) b.upgrade();
|
||||||
|
} else if (typeof bi.level !== 'undefined') {
|
||||||
|
b.level = bi.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof bi.damage !== 'undefined') b.damage = bi.damage;
|
||||||
|
if (typeof bi.range !== 'undefined') b.range = bi.range;
|
||||||
|
if (typeof bi.speed !== 'undefined') b.speed = bi.speed;
|
||||||
|
if (typeof bi.life !== 'undefined') b.life = bi.life;
|
||||||
|
if (typeof bi.shield !== 'undefined') b.shield = bi.shield;
|
||||||
|
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
||||||
|
if (typeof bi.killed !== 'undefined') b.killed = bi.killed;
|
||||||
|
if (bi.upgrade_records) {
|
||||||
|
b._upgrade_records = {};
|
||||||
|
for (var rk in bi.upgrade_records) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(bi.upgrade_records, rk)) {
|
||||||
|
b._upgrade_records[rk] = bi.upgrade_records[rk];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.range_px = b.range * TD.grid_size;
|
||||||
|
b.updateBtnDesc && b.updateBtnDesc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,20 @@
|
|||||||
for (var i = 0; i < map.buildings.length; i++) {
|
for (var i = 0; i < map.buildings.length; i++) {
|
||||||
var b = map.buildings[i];
|
var b = map.buildings[i];
|
||||||
if (!b || !b.grid) continue;
|
if (!b || !b.grid) continue;
|
||||||
buildings.push({ type: b.type, mx: b.grid.mx, my: b.grid.my, level: b.level, money: b.money });
|
buildings.push({
|
||||||
|
type: b.type,
|
||||||
|
mx: b.grid.mx,
|
||||||
|
my: b.grid.my,
|
||||||
|
level: b.level,
|
||||||
|
money: b.money,
|
||||||
|
killed: b.killed,
|
||||||
|
damage: b.damage,
|
||||||
|
range: b.range,
|
||||||
|
speed: b.speed,
|
||||||
|
life: b.life,
|
||||||
|
shield: b.shield,
|
||||||
|
upgrade_records: b._upgrade_records || null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,11 +222,39 @@
|
|||||||
var grid = map.getGrid(bi.mx, bi.my);
|
var grid = map.getGrid(bi.mx, bi.my);
|
||||||
if (grid) grid.addBuilding(bi.type);
|
if (grid) grid.addBuilding(bi.type);
|
||||||
var b = grid && grid.building;
|
var b = grid && grid.building;
|
||||||
if (b) {
|
if (b) {
|
||||||
if (typeof bi.level !== 'undefined') b.level = bi.level;
|
var hasSavedBuildingAttrs = (
|
||||||
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
typeof bi.damage !== 'undefined' ||
|
||||||
b.updateBtnDesc && b.updateBtnDesc();
|
typeof bi.range !== 'undefined' ||
|
||||||
}
|
typeof bi.speed !== 'undefined' ||
|
||||||
|
typeof bi.life !== 'undefined' ||
|
||||||
|
typeof bi.shield !== 'undefined'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasSavedBuildingAttrs && typeof bi.level !== 'undefined' && bi.level > 0 && typeof b.upgrade === 'function') {
|
||||||
|
for (var ul = 0; ul < bi.level; ul++) b.upgrade();
|
||||||
|
} else if (typeof bi.level !== 'undefined') {
|
||||||
|
b.level = bi.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof bi.damage !== 'undefined') b.damage = bi.damage;
|
||||||
|
if (typeof bi.range !== 'undefined') b.range = bi.range;
|
||||||
|
if (typeof bi.speed !== 'undefined') b.speed = bi.speed;
|
||||||
|
if (typeof bi.life !== 'undefined') b.life = bi.life;
|
||||||
|
if (typeof bi.shield !== 'undefined') b.shield = bi.shield;
|
||||||
|
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
||||||
|
if (typeof bi.killed !== 'undefined') b.killed = bi.killed;
|
||||||
|
if (bi.upgrade_records) {
|
||||||
|
b._upgrade_records = {};
|
||||||
|
for (var rk in bi.upgrade_records) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(bi.upgrade_records, rk)) {
|
||||||
|
b._upgrade_records[rk] = bi.upgrade_records[rk];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.range_px = b.range * TD.grid_size;
|
||||||
|
b.updateBtnDesc && b.updateBtnDesc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"debug:server": "cross-env NODE_OPTIONS=\"--loader ts-node/esm --inspect=5858\" ts-node-dev server/server.ts",
|
"debug:server": "cross-env NODE_OPTIONS=\"--loader ts-node/esm --inspect=5858\" ts-node-dev server/server.ts",
|
||||||
"debug:client": "cross-env PORT=3001 react-scripts start",
|
"debug:client": "cross-env PORT=3001 react-scripts start",
|
||||||
"debug": "concurrently \"npm run debug:server\" \"npm run debug:client\"",
|
"debug": "concurrently \"npm run debug:server\" \"npm run debug:client\"",
|
||||||
"build": "react-scripts build",
|
"build": "cross-env GENERATE_SOURCEMAP=false react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"migrate-passwords": "ts-node -P tsconfig.json scripts/migratePasswords.ts"
|
"migrate-passwords": "ts-node -P tsconfig.json scripts/migratePasswords.ts"
|
||||||
|
|||||||
48
public/tower-defense/td-pkg-zh-min.js
vendored
48
public/tower-defense/td-pkg-zh-min.js
vendored
@@ -265,7 +265,20 @@ var _TD = {
|
|||||||
for (var i = 0; i < map.buildings.length; i++) {
|
for (var i = 0; i < map.buildings.length; i++) {
|
||||||
var b = map.buildings[i];
|
var b = map.buildings[i];
|
||||||
if (!b || !b.grid) continue;
|
if (!b || !b.grid) continue;
|
||||||
buildings.push({ type: b.type, mx: b.grid.mx, my: b.grid.my, level: b.level, money: b.money });
|
buildings.push({
|
||||||
|
type: b.type,
|
||||||
|
mx: b.grid.mx,
|
||||||
|
my: b.grid.my,
|
||||||
|
level: b.level,
|
||||||
|
money: b.money,
|
||||||
|
killed: b.killed,
|
||||||
|
damage: b.damage,
|
||||||
|
range: b.range,
|
||||||
|
speed: b.speed,
|
||||||
|
life: b.life,
|
||||||
|
shield: b.shield,
|
||||||
|
upgrade_records: b._upgrade_records || null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { money: TD.money, life: TD.life, score: TD.score, wave: (scene && scene.wave) || 0, buildings: buildings };
|
return { money: TD.money, life: TD.life, score: TD.score, wave: (scene && scene.wave) || 0, buildings: buildings };
|
||||||
@@ -294,8 +307,36 @@ var _TD = {
|
|||||||
if (grid) grid.addBuilding(bi.type);
|
if (grid) grid.addBuilding(bi.type);
|
||||||
var b = grid && grid.building;
|
var b = grid && grid.building;
|
||||||
if (b) {
|
if (b) {
|
||||||
if (typeof bi.level !== 'undefined') b.level = bi.level;
|
var hasSavedBuildingAttrs = (
|
||||||
|
typeof bi.damage !== 'undefined' ||
|
||||||
|
typeof bi.range !== 'undefined' ||
|
||||||
|
typeof bi.speed !== 'undefined' ||
|
||||||
|
typeof bi.life !== 'undefined' ||
|
||||||
|
typeof bi.shield !== 'undefined'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasSavedBuildingAttrs && typeof bi.level !== 'undefined' && bi.level > 0 && typeof b.upgrade === 'function') {
|
||||||
|
for (var ul = 0; ul < bi.level; ul++) b.upgrade();
|
||||||
|
} else if (typeof bi.level !== 'undefined') {
|
||||||
|
b.level = bi.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof bi.damage !== 'undefined') b.damage = bi.damage;
|
||||||
|
if (typeof bi.range !== 'undefined') b.range = bi.range;
|
||||||
|
if (typeof bi.speed !== 'undefined') b.speed = bi.speed;
|
||||||
|
if (typeof bi.life !== 'undefined') b.life = bi.life;
|
||||||
|
if (typeof bi.shield !== 'undefined') b.shield = bi.shield;
|
||||||
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
||||||
|
if (typeof bi.killed !== 'undefined') b.killed = bi.killed;
|
||||||
|
if (bi.upgrade_records) {
|
||||||
|
b._upgrade_records = {};
|
||||||
|
for (var rk in bi.upgrade_records) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(bi.upgrade_records, rk)) {
|
||||||
|
b._upgrade_records[rk] = bi.upgrade_records[rk];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.range_px = b.range * TD.grid_size;
|
||||||
b.updateBtnDesc && b.updateBtnDesc();
|
b.updateBtnDesc && b.updateBtnDesc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3845,7 +3886,6 @@ _TD.a.push(function (TD) {
|
|||||||
"laser_gun": {
|
"laser_gun": {
|
||||||
damage: 25,
|
damage: 25,
|
||||||
range: 6,
|
range: 6,
|
||||||
max_range: 10,
|
|
||||||
speed: 20,
|
speed: 20,
|
||||||
// bullet_speed: 10, // laser_gun 的 bullet_speed 属性没有用
|
// bullet_speed: 10, // laser_gun 的 bullet_speed 属性没有用
|
||||||
life: 100,
|
life: 100,
|
||||||
@@ -4584,5 +4624,3 @@ _TD.a.push(function (TD) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}); // _TD.a.push end
|
}); // _TD.a.push end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3824,7 +3824,6 @@ _TD.a.push(function (TD) {
|
|||||||
"laser_gun": {
|
"laser_gun": {
|
||||||
damage: 25,
|
damage: 25,
|
||||||
range: 6,
|
range: 6,
|
||||||
max_range: 10,
|
|
||||||
speed: 20,
|
speed: 20,
|
||||||
// bullet_speed: 10, // laser_gun 的 bullet_speed 属性没有用
|
// bullet_speed: 10, // laser_gun 的 bullet_speed 属性没有用
|
||||||
life: 100,
|
life: 100,
|
||||||
|
|||||||
@@ -95,7 +95,20 @@
|
|||||||
for (var i = 0; i < map.buildings.length; i++) {
|
for (var i = 0; i < map.buildings.length; i++) {
|
||||||
var b = map.buildings[i];
|
var b = map.buildings[i];
|
||||||
if (!b || !b.grid) continue;
|
if (!b || !b.grid) continue;
|
||||||
buildings.push({ type: b.type, mx: b.grid.mx, my: b.grid.my, level: b.level, money: b.money });
|
buildings.push({
|
||||||
|
type: b.type,
|
||||||
|
mx: b.grid.mx,
|
||||||
|
my: b.grid.my,
|
||||||
|
level: b.level,
|
||||||
|
money: b.money,
|
||||||
|
killed: b.killed,
|
||||||
|
damage: b.damage,
|
||||||
|
range: b.range,
|
||||||
|
speed: b.speed,
|
||||||
|
life: b.life,
|
||||||
|
shield: b.shield,
|
||||||
|
upgrade_records: b._upgrade_records || null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,8 +203,36 @@
|
|||||||
grid.addBuilding(bi.type);
|
grid.addBuilding(bi.type);
|
||||||
var b = grid.building;
|
var b = grid.building;
|
||||||
if (b) {
|
if (b) {
|
||||||
if (typeof bi.level !== 'undefined') b.level = bi.level;
|
var hasSavedBuildingAttrs = (
|
||||||
|
typeof bi.damage !== 'undefined' ||
|
||||||
|
typeof bi.range !== 'undefined' ||
|
||||||
|
typeof bi.speed !== 'undefined' ||
|
||||||
|
typeof bi.life !== 'undefined' ||
|
||||||
|
typeof bi.shield !== 'undefined'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasSavedBuildingAttrs && typeof bi.level !== 'undefined' && bi.level > 0 && typeof b.upgrade === 'function') {
|
||||||
|
for (var ul = 0; ul < bi.level; ul++) b.upgrade();
|
||||||
|
} else if (typeof bi.level !== 'undefined') {
|
||||||
|
b.level = bi.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof bi.damage !== 'undefined') b.damage = bi.damage;
|
||||||
|
if (typeof bi.range !== 'undefined') b.range = bi.range;
|
||||||
|
if (typeof bi.speed !== 'undefined') b.speed = bi.speed;
|
||||||
|
if (typeof bi.life !== 'undefined') b.life = bi.life;
|
||||||
|
if (typeof bi.shield !== 'undefined') b.shield = bi.shield;
|
||||||
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
||||||
|
if (typeof bi.killed !== 'undefined') b.killed = bi.killed;
|
||||||
|
if (bi.upgrade_records) {
|
||||||
|
b._upgrade_records = {};
|
||||||
|
for (var rk in bi.upgrade_records) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(bi.upgrade_records, rk)) {
|
||||||
|
b._upgrade_records[rk] = bi.upgrade_records[rk];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.range_px = b.range * TD.grid_size;
|
||||||
b.updateBtnDesc && b.updateBtnDesc();
|
b.updateBtnDesc && b.updateBtnDesc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ const WORD_RECORD_MODES: WordRecordModeKey[] = [
|
|||||||
const ATTEMPT_TTL_MS = 30 * 60 * 1000;
|
const ATTEMPT_TTL_MS = 30 * 60 * 1000;
|
||||||
const MIN_SECONDS_PER_QUESTION = Number(process.env.VOCABULARY_MIN_SECONDS_PER_QUESTION || 0.4);
|
const MIN_SECONDS_PER_QUESTION = Number(process.env.VOCABULARY_MIN_SECONDS_PER_QUESTION || 0.4);
|
||||||
const ATTEMPT_SECRET = process.env.VOCABULARY_ATTEMPT_SECRET || config.JWT_SECRET;
|
const ATTEMPT_SECRET = process.env.VOCABULARY_ATTEMPT_SECRET || config.JWT_SECRET;
|
||||||
|
const ANSWER_PROOF_SALT = 'd1ktsalt';
|
||||||
|
|
||||||
const isVocabularyTestType = (value: unknown): value is VocabularyTestType => {
|
const isVocabularyTestType = (value: unknown): value is VocabularyTestType => {
|
||||||
return value === 'chinese-to-english' ||
|
return value === 'chinese-to-english' ||
|
||||||
@@ -104,8 +105,8 @@ const buildAnswerProof = (
|
|||||||
userAnswer: string,
|
userAnswer: string,
|
||||||
submittedAt: number
|
submittedAt: number
|
||||||
): string => crypto
|
): string => crypto
|
||||||
.createHash('sha256')
|
.createHash('md5')
|
||||||
.update(`${attemptId}:${questionToken}:${submittedAt}:${String(userAnswer ?? '')}`)
|
.update(`${ANSWER_PROOF_SALT}:${attemptId}:${questionToken}:${submittedAt}:${String(userAnswer ?? '')}:${ANSWER_PROOF_SALT}`)
|
||||||
.digest('hex');
|
.digest('hex');
|
||||||
|
|
||||||
const buildMultipleChoiceOptions = (word: any, pool: any[]): string[] => {
|
const buildMultipleChoiceOptions = (word: any, pool: any[]): string[] => {
|
||||||
@@ -879,6 +880,7 @@ router.post('/test-record', authMiddleware, async (req, res) => {
|
|||||||
message: '测试记录已保存',
|
message: '测试记录已保存',
|
||||||
attemptId: attempt._id.toString(),
|
attemptId: attempt._id.toString(),
|
||||||
stats: verifiedStats,
|
stats: verifiedStats,
|
||||||
|
results: evaluatedResults,
|
||||||
submittedAt: new Date(lastSubmittedAtMs)
|
submittedAt: new Date(lastSubmittedAtMs)
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ interface SavedVocabularyTestRecordResponse {
|
|||||||
endTime: string | Date;
|
endTime: string | Date;
|
||||||
duration: number;
|
duration: number;
|
||||||
};
|
};
|
||||||
|
results: TestResult[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TestAttemptResponse {
|
interface TestAttemptResponse {
|
||||||
@@ -102,6 +103,8 @@ interface TestAttemptResponse {
|
|||||||
questions: TestQuestion[];
|
questions: TestQuestion[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ANSWER_PROOF_SALT = 'd1ktsalt';
|
||||||
|
|
||||||
const VocabularyStudy: React.FC = () => {
|
const VocabularyStudy: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -124,7 +127,6 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
const [testStarted, setTestStarted] = useState(false);
|
const [testStarted, setTestStarted] = useState(false);
|
||||||
const [testFinished, setTestFinished] = useState(false);
|
const [testFinished, setTestFinished] = useState(false);
|
||||||
const [testResults, setTestResults] = useState<TestResult[]>([]);
|
const [testResults, setTestResults] = useState<TestResult[]>([]);
|
||||||
const [answerFeedback, setAnswerFeedback] = useState<TestResult | null>(null);
|
|
||||||
const [testAttemptId, setTestAttemptId] = useState<string>('');
|
const [testAttemptId, setTestAttemptId] = useState<string>('');
|
||||||
const [pendingAnswers, setPendingAnswers] = useState<PendingAnswer[]>([]);
|
const [pendingAnswers, setPendingAnswers] = useState<PendingAnswer[]>([]);
|
||||||
const [testAttemptLoading, setTestAttemptLoading] = useState(false);
|
const [testAttemptLoading, setTestAttemptLoading] = useState(false);
|
||||||
@@ -429,7 +431,6 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
setTestAttemptId('');
|
setTestAttemptId('');
|
||||||
setPendingAnswers([]);
|
setPendingAnswers([]);
|
||||||
setTestResults([]);
|
setTestResults([]);
|
||||||
setAnswerFeedback(null);
|
|
||||||
const wordIds = studyWords.map(getWordId).filter(Boolean);
|
const wordIds = studyWords.map(getWordId).filter(Boolean);
|
||||||
if (wordIds.length === 0) {
|
if (wordIds.length === 0) {
|
||||||
message.error('单词数据不完整,请重新加载后再试');
|
message.error('单词数据不完整,请重新加载后再试');
|
||||||
@@ -457,6 +458,7 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
setTestResults([]);
|
setTestResults([]);
|
||||||
setPendingAnswers([]);
|
setPendingAnswers([]);
|
||||||
setOptions(questions[0]?.options || []);
|
setOptions(questions[0]?.options || []);
|
||||||
|
setIsModalVisible(false);
|
||||||
setStudyStats({
|
setStudyStats({
|
||||||
totalWords: 0,
|
totalWords: 0,
|
||||||
correctWords: 0,
|
correctWords: 0,
|
||||||
@@ -493,42 +495,7 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
userAnswer: string,
|
userAnswer: string,
|
||||||
submittedAt: number
|
submittedAt: number
|
||||||
): string => {
|
): string => {
|
||||||
return CryptoJS.SHA256(`${attemptId}:${questionToken}:${submittedAt}:${userAnswer}`).toString();
|
return CryptoJS.MD5(`${ANSWER_PROOF_SALT}:${attemptId}:${questionToken}:${submittedAt}:${userAnswer}:${ANSWER_PROOF_SALT}`).toString();
|
||||||
};
|
|
||||||
|
|
||||||
function normalizeAnswer(str: string): string {
|
|
||||||
return str
|
|
||||||
.replace(/(/g, '(')
|
|
||||||
.replace(/)/g, ')')
|
|
||||||
.replace(/,/g, ',') // 全角逗号转半角
|
|
||||||
.replace(/\s+/g, '') // 去除所有空格
|
|
||||||
.replace(/,/g, '') // 去除所有逗号
|
|
||||||
.replace(/[^\w()]/g, '') // 只保留字母、数字、括号
|
|
||||||
.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
const getQuestionWord = (question: TestQuestion): Word | undefined => {
|
|
||||||
return studyWords.find(word => getWordId(word) === question.wordId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const buildLocalTestResult = (question: TestQuestion, rawUserAnswer: string): TestResult => {
|
|
||||||
const matchedWord = getQuestionWord(question);
|
|
||||||
const userAnswer = rawUserAnswer.trim();
|
|
||||||
const correctAnswer = testType === 'multiple-choice'
|
|
||||||
? String(matchedWord?.translation ?? question.translation ?? '').trim()
|
|
||||||
: String(matchedWord?.word ?? question.word ?? '').trim();
|
|
||||||
const isCorrect = testType === 'multiple-choice'
|
|
||||||
? userAnswer === correctAnswer
|
|
||||||
: normalizeAnswer(userAnswer) === normalizeAnswer(correctAnswer);
|
|
||||||
|
|
||||||
return {
|
|
||||||
questionToken: question.questionToken,
|
|
||||||
wordId: question.wordId,
|
|
||||||
word: String(matchedWord?.word ?? question.word ?? question.translation ?? '').trim(),
|
|
||||||
userAnswer,
|
|
||||||
correctAnswer,
|
|
||||||
isCorrect
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 提交答案
|
// 提交答案
|
||||||
@@ -543,7 +510,6 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
}
|
}
|
||||||
const submittedAt = Date.now();
|
const submittedAt = Date.now();
|
||||||
const normalizedUserAnswer = userAnswer.trim();
|
const normalizedUserAnswer = userAnswer.trim();
|
||||||
const localResult = buildLocalTestResult(currentQuestion, normalizedUserAnswer);
|
|
||||||
const answerProof = buildAnswerProof(
|
const answerProof = buildAnswerProof(
|
||||||
testAttemptId,
|
testAttemptId,
|
||||||
currentQuestion.questionToken,
|
currentQuestion.questionToken,
|
||||||
@@ -560,14 +526,8 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
answerProof
|
answerProof
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const updatedTestResults = [
|
|
||||||
...testResults,
|
|
||||||
localResult
|
|
||||||
];
|
|
||||||
|
|
||||||
setPendingAnswers(updatedPendingAnswers);
|
setPendingAnswers(updatedPendingAnswers);
|
||||||
setTestResults(updatedTestResults);
|
|
||||||
setAnswerFeedback(localResult);
|
|
||||||
setShowAnswer(true);
|
setShowAnswer(true);
|
||||||
setUserAnswer('');
|
setUserAnswer('');
|
||||||
|
|
||||||
@@ -576,7 +536,6 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
const nextIndex = testIndex + 1;
|
const nextIndex = testIndex + 1;
|
||||||
setTestIndex(nextIndex);
|
setTestIndex(nextIndex);
|
||||||
setShowAnswer(false);
|
setShowAnswer(false);
|
||||||
setAnswerFeedback(null);
|
|
||||||
answerSubmittingRef.current = false;
|
answerSubmittingRef.current = false;
|
||||||
if (testType === 'audio-to-english') {
|
if (testType === 'audio-to-english') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -588,31 +547,14 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
answerSubmittingRef.current = false;
|
answerSubmittingRef.current = false;
|
||||||
finishTestWithResults(updatedPendingAnswers, updatedTestResults);
|
finishTestWithResults(updatedPendingAnswers);
|
||||||
}
|
}
|
||||||
}, 1500);
|
}, 1500);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 使用指定结果完成测试
|
// 使用指定结果完成测试
|
||||||
const finishTestWithResults = async (finalAnswers: PendingAnswer[], finalResults: TestResult[]) => {
|
const finishTestWithResults = async (finalAnswers: PendingAnswer[]) => {
|
||||||
try {
|
try {
|
||||||
const lastSubmittedAtMs = finalAnswers.length > 0
|
|
||||||
? Math.max(...finalAnswers.map(item => item.submittedAt))
|
|
||||||
: Date.now();
|
|
||||||
const localCorrectWords = finalResults.filter(result => result.isCorrect).length;
|
|
||||||
const localStats = {
|
|
||||||
totalWords: finalResults.length,
|
|
||||||
correctWords: localCorrectWords,
|
|
||||||
accuracy: finalResults.length > 0 ? (localCorrectWords / finalResults.length) * 100 : 0,
|
|
||||||
startTime: studyStats.startTime,
|
|
||||||
endTime: new Date(lastSubmittedAtMs),
|
|
||||||
duration: Math.max(0, (lastSubmittedAtMs - studyStats.startTime.getTime()) / 1000)
|
|
||||||
};
|
|
||||||
|
|
||||||
setTestResults(finalResults);
|
|
||||||
setStudyStats(localStats);
|
|
||||||
setTestFinished(true);
|
|
||||||
|
|
||||||
// 添加调试信息
|
// 添加调试信息
|
||||||
console.log('测试完成状态(带结果):', {
|
console.log('测试完成状态(带结果):', {
|
||||||
finalResultsLength: finalAnswers.length,
|
finalResultsLength: finalAnswers.length,
|
||||||
@@ -640,8 +582,11 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
endTime: new Date(savedRecord.stats.endTime),
|
endTime: new Date(savedRecord.stats.endTime),
|
||||||
duration: savedRecord.stats.duration
|
duration: savedRecord.stats.duration
|
||||||
});
|
});
|
||||||
|
setTestResults(savedRecord.results);
|
||||||
setPendingAnswers([]);
|
setPendingAnswers([]);
|
||||||
setTestAttemptId('');
|
setTestAttemptId('');
|
||||||
|
setShowAnswer(false);
|
||||||
|
setTestFinished(true);
|
||||||
|
|
||||||
message.success('测试完成,记录已保存');
|
message.success('测试完成,记录已保存');
|
||||||
setIsModalVisible(true);
|
setIsModalVisible(true);
|
||||||
@@ -657,7 +602,7 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
// 完成测试 - 兼容原来的调用方式
|
// 完成测试 - 兼容原来的调用方式
|
||||||
const finishTest = async () => {
|
const finishTest = async () => {
|
||||||
// 使用当前的测试结果完成测试
|
// 使用当前的测试结果完成测试
|
||||||
await finishTestWithResults([...pendingAnswers], [...testResults]);
|
await finishTestWithResults([...pendingAnswers]);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重新开始测试
|
// 重新开始测试
|
||||||
@@ -670,7 +615,6 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
setUserAnswer('');
|
setUserAnswer('');
|
||||||
setShowAnswer(false);
|
setShowAnswer(false);
|
||||||
setTestResults([]);
|
setTestResults([]);
|
||||||
setAnswerFeedback(null);
|
|
||||||
setPendingAnswers([]);
|
setPendingAnswers([]);
|
||||||
setOptions([]);
|
setOptions([]);
|
||||||
answerSubmittingRef.current = false;
|
answerSubmittingRef.current = false;
|
||||||
@@ -1271,24 +1215,18 @@ const VocabularyStudy: React.FC = () => {
|
|||||||
{showAnswer && (
|
{showAnswer && (
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: 15,
|
padding: 15,
|
||||||
backgroundColor: answerFeedback?.isCorrect ? '#f6ffed' : '#fff2f0',
|
backgroundColor: '#f6f8fa',
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
marginBottom: 15,
|
marginBottom: 15,
|
||||||
border: `1px solid ${answerFeedback?.isCorrect ? '#b7eb8f' : '#ffccc7'}`
|
border: '1px solid #d9d9d9'
|
||||||
}}>
|
}}>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: answerFeedback?.isCorrect ? '#389e0d' : '#cf1322',
|
color: '#1677ff',
|
||||||
marginBottom: 5
|
marginBottom: 5
|
||||||
}}>
|
}}>
|
||||||
{answerFeedback?.isCorrect ? '回答正确' : '回答错误'}
|
本题已提交
|
||||||
</div>
|
</div>
|
||||||
{answerFeedback && (
|
|
||||||
<>
|
|
||||||
<div>正确答案: {answerFeedback.correctAnswer}</div>
|
|
||||||
<div>你的答案: {answerFeedback.userAnswer || '(空)'}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div>正在进入下一题...</div>
|
<div>正在进入下一题...</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user