전투 대상 표시 UI 및 정수 표기 검증 반영 #118
18
docs/integer-format.md
Normal file
18
docs/integer-format.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Integer Formatting Rules
|
||||
|
||||
MapleStory Worlds may serialize numeric properties as strings such as `1.0`.
|
||||
This is valid numeric data, but it must never be used directly for UI entity
|
||||
names or paths because `Card1.0` and `DmgPop_1.0` do not exist.
|
||||
|
||||
- Use `self:IntStr(value)` for every numeric suffix in a UI path or entity name.
|
||||
- Use `string.format("%d", math.floor(value or 0))` or `FormatNumber` for an
|
||||
integer shown in UI text.
|
||||
- Do not concatenate `tostring(number)` into a `/ui/...` path.
|
||||
- `tools/deck/gen-slaydeck.mjs` normalizes exact integer strings such as `0.0`
|
||||
to `0` when regenerating `Global/common.gamelogic`.
|
||||
|
||||
Run this check after editing card-controller UI paths:
|
||||
|
||||
```powershell
|
||||
node tools/verify/integer-format.mjs
|
||||
```
|
||||
@@ -213,6 +213,21 @@ function writeCodeblocks() {
|
||||
writeFileSync('RootDesk/MyDesk/SlayDeckController.codeblock', JSON.stringify(combat, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
function normalizeIntegerStrings(value) {
|
||||
if (Array.isArray(value)) {
|
||||
for (let index = 0; index < value.length; index++) normalizeIntegerStrings(value[index]);
|
||||
return;
|
||||
}
|
||||
if (value == null || typeof value !== 'object') return;
|
||||
for (const [key, child] of Object.entries(value)) {
|
||||
if (typeof child === 'string') {
|
||||
value[key] = child.replace(/^(-?\d+)\.0+$/, '$1');
|
||||
} else {
|
||||
normalizeIntegerStrings(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function patchCommon() {
|
||||
const common = JSON.parse(readFileSync(COMMON_FILE, 'utf8'));
|
||||
const entity = common.ContentProto.Entities.find((e) => e.path === '/common');
|
||||
@@ -220,6 +235,7 @@ function patchCommon() {
|
||||
entity.jsonString['@components'] = [
|
||||
{ '@type': 'script.SlayDeckController', Enable: true, Energy: 0, MaxEnergy: 3, Turn: 0, TweenEventId: 0 },
|
||||
];
|
||||
normalizeIntegerStrings(common);
|
||||
JSON.parse(JSON.stringify(common));
|
||||
writeFileSync(COMMON_FILE, JSON.stringify(common, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
31
tools/verify/integer-format.mjs
Normal file
31
tools/verify/integer-format.mjs
Normal file
@@ -0,0 +1,31 @@
|
||||
import { readFileSync, readdirSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const cbDir = 'tools/deck/cb';
|
||||
const files = readdirSync(cbDir).filter((name) => name.endsWith('.mjs'));
|
||||
const violations = [];
|
||||
|
||||
for (const file of files) {
|
||||
const source = readFileSync(join(cbDir, file), 'utf8');
|
||||
const matches = source.match(/["']\/ui\/[^"']*["']\s*\.\.\s*tostring\(/g) || [];
|
||||
for (const match of matches) violations.push(`${file}: ${match}`);
|
||||
}
|
||||
|
||||
const generator = readFileSync('tools/deck/gen-slaydeck.mjs', 'utf8');
|
||||
if (!generator.includes('normalizeIntegerStrings(common)')) {
|
||||
violations.push('gen-slaydeck.mjs: common.gamelogic integer-string normalization is missing');
|
||||
}
|
||||
|
||||
const common = readFileSync('Global/common.gamelogic', 'utf8');
|
||||
const commonIntegerDecimals = common.match(/(?<![0-9.])-?[0-9]+\.0+(?![0-9])/g) || [];
|
||||
if (commonIntegerDecimals.length > 0) {
|
||||
violations.push(`Global/common.gamelogic: integer decimal values ${commonIntegerDecimals.length}`);
|
||||
}
|
||||
|
||||
if (violations.length > 0) {
|
||||
console.error('UI path integer formatting violations:');
|
||||
for (const violation of violations) console.error(`- ${violation}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`integer format: UI path tostring violations 0, common integer decimals 0 (${files.length} modules checked)`);
|
||||
Reference in New Issue
Block a user