@pkmn/engine 0.1.0-dev.545e0ef3 → 0.1.0-dev.5ea92492
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/build/pkg/addon.d.ts +7 -0
- package/build/pkg/addon.js +111 -0
- package/build/pkg/addon.js.map +1 -0
- package/build/pkg/data.d.ts +1 -2
- package/build/pkg/data.js +3 -5
- package/build/pkg/data.js.map +1 -1
- package/build/test/display.js +382 -0
- package/build/test/display.js.map +1 -0
- package/build/tools/debug.js +133 -0
- package/build/tools/debug.js.map +1 -0
- package/build.zig +9 -12
- package/package.json +33 -23
- package/src/bin/install-pkmn-engine +57 -30
- package/src/bin/pkmn-debug +62 -0
- package/src/lib/gen1/README.md +18 -5
- package/src/lib/gen1/data.zig +2 -1
- package/src/lib/gen1/mechanics.zig +2340 -2254
- package/src/test/display/pkmn.html.tmpl +368 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.run = exports.display = void 0;
|
|
27
|
+
const fs = __importStar(require("fs"));
|
|
28
|
+
const data_1 = require("@pkmn/data");
|
|
29
|
+
const pkg_1 = require("../pkg");
|
|
30
|
+
const addon = __importStar(require("../pkg/addon"));
|
|
31
|
+
const data_2 = require("../pkg/data");
|
|
32
|
+
const gen1 = __importStar(require("../pkg/gen1"));
|
|
33
|
+
const display_1 = require("../test/display");
|
|
34
|
+
class SpeciesNames {
|
|
35
|
+
gen;
|
|
36
|
+
battle;
|
|
37
|
+
constructor(gen, battle) {
|
|
38
|
+
this.gen = gen;
|
|
39
|
+
this.battle = battle;
|
|
40
|
+
}
|
|
41
|
+
get p1() {
|
|
42
|
+
const [p1] = Array.from(this.battle.sides);
|
|
43
|
+
const team = Array.from(p1.pokemon)
|
|
44
|
+
.sort((a, b) => a.position - b.position)
|
|
45
|
+
.map(p => ({ species: p.stored.species }));
|
|
46
|
+
return new pkg_1.SideInfo(this.gen, { name: 'Player 1', team });
|
|
47
|
+
}
|
|
48
|
+
get p2() {
|
|
49
|
+
const [, p2] = Array.from(this.battle.sides);
|
|
50
|
+
const team = Array.from(p2.pokemon)
|
|
51
|
+
.sort((a, b) => a.position - b.position)
|
|
52
|
+
.map(p => ({ species: p.stored.species }));
|
|
53
|
+
return new pkg_1.SideInfo(this.gen, { name: 'Player 2', team });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function display(gens, data, error, seed) {
|
|
57
|
+
if (!data.length)
|
|
58
|
+
throw new Error('Invalid input');
|
|
59
|
+
const view = data_2.Data.view(data);
|
|
60
|
+
let offset = 0;
|
|
61
|
+
const showdown = !!view.getUint8(offset++);
|
|
62
|
+
const gen = gens.get(view.getUint8(offset++));
|
|
63
|
+
const lookup = data_2.Lookup.get(gen);
|
|
64
|
+
const size = data_2.LAYOUT[gen.num - 1].sizes.Battle;
|
|
65
|
+
const deserialize = (buf) => {
|
|
66
|
+
// We don't care about the native addon, we just need to load it so other checks don't fail
|
|
67
|
+
void addon.supports(true);
|
|
68
|
+
switch (gen.num) {
|
|
69
|
+
case 1: return new gen1.Battle(lookup, data_2.Data.view(buf), { showdown });
|
|
70
|
+
default: throw new Error(`Unsupported gen: ${gen.num}`);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const battle = deserialize(data.subarray(offset, offset += size));
|
|
74
|
+
const names = new SpeciesNames(gen, battle);
|
|
75
|
+
const log = new pkg_1.Log(gen, lookup, names);
|
|
76
|
+
let partial = undefined;
|
|
77
|
+
const frames = [];
|
|
78
|
+
while (offset < view.byteLength) {
|
|
79
|
+
partial = { parsed: [] };
|
|
80
|
+
const it = log.parse(data_2.Data.view(data.subarray(offset)))[Symbol.iterator]();
|
|
81
|
+
let r = it.next();
|
|
82
|
+
while (!r.done) {
|
|
83
|
+
partial.parsed.push(r.value);
|
|
84
|
+
r = it.next();
|
|
85
|
+
}
|
|
86
|
+
offset += r.value;
|
|
87
|
+
if (offset >= view.byteLength)
|
|
88
|
+
break;
|
|
89
|
+
partial.battle = deserialize(data.subarray(offset, offset += size));
|
|
90
|
+
if (offset >= view.byteLength)
|
|
91
|
+
break;
|
|
92
|
+
partial.result = pkg_1.Result.decode(data[offset++]);
|
|
93
|
+
if (offset >= view.byteLength)
|
|
94
|
+
break;
|
|
95
|
+
partial.c1 = pkg_1.Choice.decode(data[offset++]);
|
|
96
|
+
if (offset >= view.byteLength)
|
|
97
|
+
break;
|
|
98
|
+
partial.c2 = pkg_1.Choice.decode(data[offset++]);
|
|
99
|
+
frames.push(partial);
|
|
100
|
+
partial = undefined;
|
|
101
|
+
}
|
|
102
|
+
return (0, display_1.render)(gen, showdown, error, seed, frames, partial);
|
|
103
|
+
}
|
|
104
|
+
exports.display = display;
|
|
105
|
+
async function run(gens) {
|
|
106
|
+
let input;
|
|
107
|
+
if (process.argv.length > 2) {
|
|
108
|
+
if (process.argv.length > 3) {
|
|
109
|
+
console.error('Invalid input');
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
input = fs.readFileSync(process.argv[2]);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
let length = 0;
|
|
116
|
+
const result = [];
|
|
117
|
+
for await (const chunk of process.stdin) {
|
|
118
|
+
result.push(chunk);
|
|
119
|
+
length += chunk.length;
|
|
120
|
+
}
|
|
121
|
+
input = Buffer.concat(result, length);
|
|
122
|
+
}
|
|
123
|
+
process.stdout.write(display(gens, input));
|
|
124
|
+
}
|
|
125
|
+
exports.run = run;
|
|
126
|
+
if (require.main === module) {
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
128
|
+
run(new data_1.Generations(require('@pkmn/sim').Dex)).catch(err => {
|
|
129
|
+
console.error(err.message);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../../src/tools/debug.ts"],"names":[],"mappings":"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"}
|
package/build.zig
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const builtin = @import("builtin");
|
|
3
3
|
|
|
4
|
-
const NativeTargetInfo = std.zig.system.NativeTargetInfo;
|
|
5
|
-
|
|
6
4
|
pub const Options = struct { showdown: ?bool = null, trace: ?bool = null };
|
|
7
5
|
|
|
8
6
|
pub fn module(b: *std.Build, options: Options) *std.Build.Module {
|
|
@@ -29,8 +27,9 @@ pub fn build(b: *std.Build) !void {
|
|
|
29
27
|
const dynamic = b.option(bool, "dynamic", "Build a dynamic library") orelse false;
|
|
30
28
|
const strip = b.option(bool, "strip", "Strip debugging symbols from binary") orelse false;
|
|
31
29
|
const pic = b.option(bool, "pic", "Force position independent code") orelse false;
|
|
30
|
+
const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
|
|
32
31
|
|
|
33
|
-
const cmd = b.findProgram(
|
|
32
|
+
const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
|
|
34
33
|
|
|
35
34
|
var parser = std.json.Parser.init(b.allocator, false);
|
|
36
35
|
defer parser.deinit();
|
|
@@ -66,7 +65,7 @@ pub fn build(b: *std.Build) !void {
|
|
|
66
65
|
lib.linkLibC();
|
|
67
66
|
if (node_import_lib) |il| {
|
|
68
67
|
lib.addObjectFile(il);
|
|
69
|
-
} else if ((try NativeTargetInfo.detect(target)).target.os.tag == .windows) {
|
|
68
|
+
} else if ((try std.zig.system.NativeTargetInfo.detect(target)).target.os.tag == .windows) {
|
|
70
69
|
try std.io.getStdErr().writeAll("Must provide --node-import-library path on Windows\n");
|
|
71
70
|
std.process.exit(1);
|
|
72
71
|
}
|
|
@@ -74,7 +73,7 @@ pub fn build(b: *std.Build) !void {
|
|
|
74
73
|
maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
|
|
75
74
|
if (pic) lib.force_pic = pic;
|
|
76
75
|
// Always emit to build/lib because this is where the driver code expects to find it
|
|
77
|
-
// TODO(ziglang/zig#2231): using the following used to work (though
|
|
76
|
+
// TODO(ziglang/zig#2231): using the following used to work (though was hacky):
|
|
78
77
|
//
|
|
79
78
|
// lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
|
|
80
79
|
// b.getInstallStep().dependOn(&lib.step);
|
|
@@ -98,13 +97,10 @@ pub fn build(b: *std.Build) !void {
|
|
|
98
97
|
lib.rdynamic = true;
|
|
99
98
|
lib.strip = strip;
|
|
100
99
|
if (pic) lib.force_pic = pic;
|
|
101
|
-
const opt = b.findProgram(
|
|
102
|
-
&[_][]const u8{"wasm-opt"},
|
|
103
|
-
&[_][]const u8{"./node_modules/.bin"},
|
|
104
|
-
) catch null;
|
|
100
|
+
const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
|
|
105
101
|
if (optimize != .Debug and opt != null) {
|
|
106
102
|
const out = b.fmt("build/lib/{s}.wasm", .{name});
|
|
107
|
-
const sh = b.addSystemCommand(
|
|
103
|
+
const sh = b.addSystemCommand(&.{ opt.?, "-O4" });
|
|
108
104
|
sh.addArtifactArg(lib);
|
|
109
105
|
sh.addArg("-o");
|
|
110
106
|
sh.addFileSourceArg(.{ .path = out });
|
|
@@ -139,6 +135,7 @@ pub fn build(b: *std.Build) !void {
|
|
|
139
135
|
lib.bundle_compiler_rt = true;
|
|
140
136
|
maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
|
|
141
137
|
if (pic) lib.force_pic = pic;
|
|
138
|
+
if (emit_asm) lib.emit_asm = .emit;
|
|
142
139
|
lib.install();
|
|
143
140
|
c = true;
|
|
144
141
|
}
|
|
@@ -235,7 +232,7 @@ fn maybeStrip(
|
|
|
235
232
|
if (mac and artifact.isDynamicLibrary()) return;
|
|
236
233
|
// Assuming GNU strip, which complains "illegal pathname found in archive member"...
|
|
237
234
|
if (!mac and artifact.isStaticLibrary()) return;
|
|
238
|
-
const sh = b.addSystemCommand(
|
|
235
|
+
const sh = b.addSystemCommand(&.{ cmd.?, if (mac) "-x" else "-s" });
|
|
239
236
|
sh.addArtifactArg(artifact);
|
|
240
237
|
step.dependOn(&sh.step);
|
|
241
238
|
}
|
|
@@ -248,7 +245,7 @@ const Config = struct {
|
|
|
248
245
|
cmd: ?[]const u8,
|
|
249
246
|
};
|
|
250
247
|
|
|
251
|
-
const TESTS =
|
|
248
|
+
const TESTS = .{
|
|
252
249
|
"src/lib/common/test.zig",
|
|
253
250
|
"src/lib/gen1/test.zig",
|
|
254
251
|
"src/lib/gen2/test.zig",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pkmn/engine",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.5ea92492",
|
|
4
4
|
"description": "A minimal, complete, Pokémon battle simulation engine optimized for performance",
|
|
5
5
|
"repository": "github:pkmn/engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,6 +9,28 @@
|
|
|
9
9
|
"types": "build/pkg/index.d.ts",
|
|
10
10
|
"files": [
|
|
11
11
|
"build.zig",
|
|
12
|
+
"build/pkg/data.d.ts",
|
|
13
|
+
"build/pkg/data.js.map",
|
|
14
|
+
"build/pkg/addon.d.ts",
|
|
15
|
+
"build/pkg/addon.js.map",
|
|
16
|
+
"build/pkg/addon.js",
|
|
17
|
+
"build/pkg/data.js",
|
|
18
|
+
"build/pkg/data/ids.json",
|
|
19
|
+
"build/pkg/data/layout.json",
|
|
20
|
+
"build/pkg/data/protocol.json",
|
|
21
|
+
"build/pkg/gen1.d.ts",
|
|
22
|
+
"build/pkg/gen1.js.map",
|
|
23
|
+
"build/pkg/gen1.js",
|
|
24
|
+
"build/pkg/index.d.ts",
|
|
25
|
+
"build/pkg/index.js.map",
|
|
26
|
+
"build/pkg/index.js",
|
|
27
|
+
"build/pkg/protocol.d.ts",
|
|
28
|
+
"build/pkg/protocol.js.map",
|
|
29
|
+
"build/pkg/protocol.js",
|
|
30
|
+
"build/test/display.js.map",
|
|
31
|
+
"build/test/display.js",
|
|
32
|
+
"build/tools/debug.js.map",
|
|
33
|
+
"build/tools/debug.js",
|
|
12
34
|
"src/bin/install-pkmn-engine",
|
|
13
35
|
"src/lib/binding/node.zig",
|
|
14
36
|
"src/lib/binding/wasm.zig",
|
|
@@ -17,54 +39,41 @@
|
|
|
17
39
|
"src/lib/common/options.zig",
|
|
18
40
|
"src/lib/common/protocol.zig",
|
|
19
41
|
"src/lib/common/rng.zig",
|
|
42
|
+
"src/lib/gen1/data.zig",
|
|
20
43
|
"src/lib/gen1/data/moves.zig",
|
|
21
44
|
"src/lib/gen1/data/species.zig",
|
|
22
45
|
"src/lib/gen1/data/types.zig",
|
|
23
|
-
"src/lib/gen1/data.zig",
|
|
24
46
|
"src/lib/gen1/helpers.zig",
|
|
25
47
|
"src/lib/gen1/mechanics.zig",
|
|
48
|
+
"src/lib/gen2/data.zig",
|
|
26
49
|
"src/lib/gen2/data/items.zig",
|
|
27
50
|
"src/lib/gen2/data/moves.zig",
|
|
28
51
|
"src/lib/gen2/data/species.zig",
|
|
29
52
|
"src/lib/gen2/data/types.zig",
|
|
30
|
-
"src/lib/gen2/data.zig",
|
|
31
53
|
"src/lib/gen2/helpers.zig",
|
|
32
54
|
"src/lib/gen2/mechanics.zig",
|
|
33
55
|
"src/lib/pkmn.zig",
|
|
34
|
-
"
|
|
35
|
-
"build/pkg/data/layout.json",
|
|
36
|
-
"build/pkg/data/protocol.json",
|
|
37
|
-
"build/pkg/data.d.ts",
|
|
38
|
-
"build/pkg/data.js",
|
|
39
|
-
"build/pkg/data.js.map",
|
|
40
|
-
"build/pkg/gen1.d.ts",
|
|
41
|
-
"build/pkg/gen1.js",
|
|
42
|
-
"build/pkg/gen1.js.map",
|
|
43
|
-
"build/pkg/index.d.ts",
|
|
44
|
-
"build/pkg/index.js",
|
|
45
|
-
"build/pkg/index.js.map",
|
|
46
|
-
"build/pkg/protocol.d.ts",
|
|
47
|
-
"build/pkg/protocol.js",
|
|
48
|
-
"build/pkg/protocol.js.map"
|
|
56
|
+
"src/test/display/pkmn.html.tmpl"
|
|
49
57
|
],
|
|
50
58
|
"engines": {
|
|
51
59
|
"node": ">=16.0.0"
|
|
52
60
|
},
|
|
53
61
|
"bin": {
|
|
54
|
-
"install-pkmn-engine": "src/bin/install-pkmn-engine"
|
|
62
|
+
"install-pkmn-engine": "src/bin/install-pkmn-engine",
|
|
63
|
+
"pkmn-debug": "src/bin/pkmn-debug"
|
|
55
64
|
},
|
|
56
65
|
"publishConfig": {
|
|
57
66
|
"access": "public"
|
|
58
67
|
},
|
|
59
68
|
"dependencies": {
|
|
60
|
-
"@pkmn/data": "^0.7.
|
|
69
|
+
"@pkmn/data": "^0.7.36",
|
|
61
70
|
"@pkmn/protocol": "^0.6.11"
|
|
62
71
|
},
|
|
63
72
|
"devDependencies": {
|
|
64
73
|
"@pkmn/eslint-config": "^3.0.0",
|
|
65
74
|
"@pkmn/img": "^0.2.8",
|
|
66
75
|
"@pkmn/sets": "^5.0.4",
|
|
67
|
-
"@pkmn/sim": "0.7.
|
|
76
|
+
"@pkmn/sim": "0.7.36",
|
|
68
77
|
"@types/html-minifier": "^4.0.2",
|
|
69
78
|
"@types/jest": "^29.5.0",
|
|
70
79
|
"@types/minimist": "^1.2.2",
|
|
@@ -83,12 +92,13 @@
|
|
|
83
92
|
"vitest": "^0.29.8"
|
|
84
93
|
},
|
|
85
94
|
"scripts": {
|
|
86
|
-
"lint": "eslint --cache src --ext ts,js && eslint src/bin
|
|
87
|
-
"fix": "eslint --fix src --ext ts,js && eslint --fix src/bin
|
|
95
|
+
"lint": "eslint --cache src --ext ts,js && eslint src/bin/*",
|
|
96
|
+
"fix": "eslint --fix src --ext ts,js && eslint --fix src/bin/*",
|
|
88
97
|
"compile": "tsc -p .",
|
|
89
98
|
"build": "npm run compile",
|
|
90
99
|
"test": "vitest src/pkg",
|
|
91
100
|
"test:integration": "vitest src/test",
|
|
101
|
+
"debug": "npm run compile && node build/tools/debug",
|
|
92
102
|
"fuzz": "npm run compile && node build/test/fuzz",
|
|
93
103
|
"generate": "npm run compile && node build/tools/generate",
|
|
94
104
|
"release": "npm run compile && node build/tools/release",
|
|
@@ -15,8 +15,10 @@ const DEV = ZIG_VERSION.dev ? `-dev.${ZIG_VERSION.dev}` : '';
|
|
|
15
15
|
const VERSION = `${ZIG_VERSION.major}.${ZIG_VERSION.minor}.${ZIG_VERSION.patch}${DEV}`;
|
|
16
16
|
const INDEX = 'https://ziglang.org/download/index.json';
|
|
17
17
|
const ROOT = path.resolve(__dirname, '..', '..');
|
|
18
|
+
let SILENT = false;
|
|
18
19
|
|
|
19
20
|
const sh = (cmd, args) => execFileSync(cmd, args, {encoding: 'utf8', cwd: ROOT});
|
|
21
|
+
const log = (...args) => { if (!SILENT) console.log(...args); };
|
|
20
22
|
|
|
21
23
|
// This is hardly the browser fetch API, but it works for our primitive needs
|
|
22
24
|
const fetch = url => new Promise((resolve, reject) => {
|
|
@@ -61,7 +63,7 @@ const unpack7 = (input, output) => new Promise((resolve, reject) => {
|
|
|
61
63
|
require.resolve('7zip-min');
|
|
62
64
|
} catch (err) {
|
|
63
65
|
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
64
|
-
|
|
66
|
+
log('Installing 7zip-min package locally to unpack the archive...');
|
|
65
67
|
sh('npm', ['install', '7zip-min', '--no-audit', '--no-save']);
|
|
66
68
|
}
|
|
67
69
|
|
|
@@ -83,7 +85,7 @@ const unpack = async (input, output) => {
|
|
|
83
85
|
sh('tar', ['xf', input, '-C', output]);
|
|
84
86
|
return;
|
|
85
87
|
} catch {
|
|
86
|
-
|
|
88
|
+
log('tar command not found, falling back to 7z');
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
// Try to use a 7z binary if we can, and only install 7zip-min as a last resort
|
|
@@ -120,10 +122,10 @@ const find = zig => {
|
|
|
120
122
|
(!ZIG_VERSION.dev || (dev && dev >= ZIG_VERSION.dev)));
|
|
121
123
|
|
|
122
124
|
if (compatible) {
|
|
123
|
-
|
|
125
|
+
log('Found existing compatible Zig executable:', zig_exe);
|
|
124
126
|
return zig_exe;
|
|
125
127
|
} else {
|
|
126
|
-
|
|
128
|
+
log(`Existing Zig executable is not compatible (${version} < ${VERSION}):`, zig_exe);
|
|
127
129
|
}
|
|
128
130
|
} catch (err) {
|
|
129
131
|
if (err.code !== 'ENOENT') throw err;
|
|
@@ -168,11 +170,11 @@ const install = async () => {
|
|
|
168
170
|
let base = path.basename(url);
|
|
169
171
|
const archive = path.join(dir, path.basename(url));
|
|
170
172
|
|
|
171
|
-
|
|
173
|
+
log(`Downloading Zig tarball from ${url} to ${archive}`);
|
|
172
174
|
try {
|
|
173
175
|
await download(url, archive, release.shasum);
|
|
174
176
|
|
|
175
|
-
|
|
177
|
+
log(`Extracting tarball and installing to ${dir}`);
|
|
176
178
|
await unpack(archive, dir);
|
|
177
179
|
if (archive.endsWith('.zip')) {
|
|
178
180
|
base = base.slice(0, -4);
|
|
@@ -187,17 +189,17 @@ const install = async () => {
|
|
|
187
189
|
} catch (err) {
|
|
188
190
|
if (err.code !== 'ENOENT') throw err;
|
|
189
191
|
}
|
|
190
|
-
|
|
192
|
+
log(`Zig${version === 'master' ? '' : ` v${version}`} executable:`, zig,
|
|
191
193
|
'(this version of Zig will be retained to speed up future updates)');
|
|
192
194
|
const [pre, post] = tty.isatty(1) ? ['\x1b[1m\x1b[35m', '\x1b[0m'] : ['', ''];
|
|
193
|
-
|
|
195
|
+
log(
|
|
194
196
|
`${pre}Consider moving the ${path.dirname(zig)} directory and adding it to your PATH`,
|
|
195
197
|
`to use Zig on other projects, or delete this if you wish to save space.${post}`
|
|
196
198
|
);
|
|
197
199
|
|
|
198
200
|
return zig;
|
|
199
201
|
} finally {
|
|
200
|
-
|
|
202
|
+
log(`Cleaning up ${archive}`);
|
|
201
203
|
try { fs.rmSync(archive); } catch {}
|
|
202
204
|
}
|
|
203
205
|
};
|
|
@@ -206,17 +208,17 @@ const install = async () => {
|
|
|
206
208
|
const downloadHeaders = async () => {
|
|
207
209
|
const include = path.resolve(process.execPath, '..', '..', 'include', 'node');
|
|
208
210
|
if (fs.existsSync(path.join(include, 'node.h'))) {
|
|
209
|
-
|
|
211
|
+
log('Node headers:', include);
|
|
210
212
|
return include;
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
const dir = path.join(ROOT, 'build', 'include');
|
|
214
|
-
const dest = path.join(dir,
|
|
216
|
+
const dest = path.join(dir, 'node');
|
|
215
217
|
if (fs.existsSync(path.join(dest, 'node.h'))) {
|
|
216
|
-
|
|
218
|
+
log('Node headers:', dest);
|
|
217
219
|
return dest;
|
|
218
220
|
} else {
|
|
219
|
-
|
|
221
|
+
log(`Could not find Node headers at ${include}, downloading...`);
|
|
220
222
|
}
|
|
221
223
|
|
|
222
224
|
try {
|
|
@@ -227,20 +229,28 @@ const downloadHeaders = async () => {
|
|
|
227
229
|
|
|
228
230
|
const url = process.release.headersUrl;
|
|
229
231
|
const targz = path.join(dir, path.basename(url));
|
|
232
|
+
const unpacked = path.join(dir, `node-${process.version}`, 'include');
|
|
230
233
|
|
|
231
|
-
|
|
232
|
-
await download(url, targz);
|
|
233
|
-
await unpack(targz, dir);
|
|
234
|
+
log(`Downloading Node headers from ${url} to ${targz}`);
|
|
234
235
|
|
|
235
|
-
|
|
236
|
+
try {
|
|
237
|
+
await download(url, targz);
|
|
238
|
+
await unpack(targz, dir);
|
|
239
|
+
fs.renameSync(path.join(unpacked, 'node'), dest);
|
|
236
240
|
|
|
237
|
-
|
|
241
|
+
log('Node headers:', dest);
|
|
242
|
+
|
|
243
|
+
return dest;
|
|
244
|
+
} finally {
|
|
245
|
+
try { fs.rmSync(targz); } catch {}
|
|
246
|
+
try { fs.rmSync(unpacked, {recursive: true}); } catch {}
|
|
247
|
+
}
|
|
238
248
|
};
|
|
239
249
|
|
|
240
250
|
const downloadImportLib = async () => {
|
|
241
251
|
const library = path.resolve(process.execPath, '..', 'node.lib');
|
|
242
252
|
if (fs.existsSync(path.join(library, 'node.h'))) {
|
|
243
|
-
|
|
253
|
+
log('Node import library:', library);
|
|
244
254
|
return library;
|
|
245
255
|
}
|
|
246
256
|
|
|
@@ -248,10 +258,10 @@ const downloadImportLib = async () => {
|
|
|
248
258
|
const url = new URL('./win-x64/node.lib', process.release.headersUrl).href;
|
|
249
259
|
const dest = path.join(dir, path.basename(url));
|
|
250
260
|
if (fs.existsSync(path.join(dest))) {
|
|
251
|
-
|
|
261
|
+
log('Node import library:', dest);
|
|
252
262
|
return dest;
|
|
253
263
|
} else {
|
|
254
|
-
|
|
264
|
+
log(`Could not find Node import library at ${library}, downloading...`);
|
|
255
265
|
}
|
|
256
266
|
|
|
257
267
|
try {
|
|
@@ -260,24 +270,41 @@ const downloadImportLib = async () => {
|
|
|
260
270
|
if (err.code !== 'EEXIST') throw err;
|
|
261
271
|
}
|
|
262
272
|
|
|
263
|
-
|
|
273
|
+
log(`Downloading Node import library from ${url} to ${dest}`);
|
|
264
274
|
await download(url, dest);
|
|
265
|
-
|
|
275
|
+
log('Node import library:', dest);
|
|
266
276
|
|
|
267
277
|
return dest;
|
|
268
278
|
};
|
|
269
279
|
|
|
270
280
|
(async () => {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
281
|
+
let only = false;
|
|
282
|
+
let options = undefined;
|
|
283
|
+
let error = false;
|
|
284
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
285
|
+
switch (process.argv[i]) {
|
|
286
|
+
case "--silent": {
|
|
287
|
+
SILENT = true;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
case "--zig": {
|
|
291
|
+
if (options || only) error = true;
|
|
292
|
+
only = true;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
default: {
|
|
296
|
+
if (options || only || !process.argv[i].startsWith('--options=')) error = true;
|
|
297
|
+
options = process.argv[i].slice(10).split(' ');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (error) {
|
|
275
302
|
console.error('Invalid arguments');
|
|
276
303
|
process.exit(1);
|
|
277
304
|
}
|
|
278
305
|
|
|
279
306
|
const zig = await install();
|
|
280
|
-
if (
|
|
307
|
+
if (only) process.exit(0);
|
|
281
308
|
|
|
282
309
|
let tmp;
|
|
283
310
|
try {
|
|
@@ -347,7 +374,7 @@ const downloadImportLib = async () => {
|
|
|
347
374
|
opts.includes('-Dshowdown') || opts.includes('-Dshowdown=true') ? '-showdown' : '';
|
|
348
375
|
const flags = opts.length ? ` (${opts.join(' ')})` : '';
|
|
349
376
|
const artifact = path.join('build', 'lib', `pkmn${suffix}.${config.type}`);
|
|
350
|
-
|
|
377
|
+
log(`Building ${artifact}${flags}`);
|
|
351
378
|
sh(zig, args);
|
|
352
379
|
// TODO: ziglang/zig#14647 broke emit_to so we get to rename the file ourselves...
|
|
353
380
|
if (config.type === 'node') {
|
|
@@ -355,7 +382,7 @@ const downloadImportLib = async () => {
|
|
|
355
382
|
const ext = process.platform === 'darwin'
|
|
356
383
|
? 'dylib' : process.platform === 'win32' ? 'dll' : 'so';
|
|
357
384
|
const actual = path.join('build', 'lib', `${prefix}pkmn${suffix}.node.${ext}`);
|
|
358
|
-
fs.renameSync(actual, artifact);
|
|
385
|
+
fs.renameSync(path.join(ROOT, actual), path.join(ROOT, artifact));
|
|
359
386
|
}
|
|
360
387
|
}
|
|
361
388
|
})().catch(e => {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
try {
|
|
5
|
+
require('source-map-support').install();
|
|
6
|
+
} catch (err) {
|
|
7
|
+
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const {execFileSync} = require('child_process');
|
|
11
|
+
const {readFileSync} = require('fs');
|
|
12
|
+
const {join, resolve} = require('path');
|
|
13
|
+
|
|
14
|
+
const ROOT = resolve(__dirname, '..', '..');
|
|
15
|
+
const packageJSON = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
|
|
16
|
+
|
|
17
|
+
const missing = [];
|
|
18
|
+
for (const dep of ['@pkmn/img', 'html-minifier', 'mustache']) {
|
|
19
|
+
try {
|
|
20
|
+
require.resolve(dep);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
if (e.code !== 'MODULE_NOT_FOUND') throw e;
|
|
23
|
+
missing.push(`${dep}@${packageJSON.devDependencies[dep]}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let Dex = undefined;
|
|
28
|
+
try {
|
|
29
|
+
Dex = require('@pkmn/sim').Dex;
|
|
30
|
+
} catch (err) {
|
|
31
|
+
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
32
|
+
try {
|
|
33
|
+
Dex = require('@pkmn/dex').Dex;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
if (e.code !== 'MODULE_NOT_FOUND') throw e;
|
|
36
|
+
missing.push(`@pkmn/dex@${packageJSON.dependencies['@pkmn/data']}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (missing.length) {
|
|
41
|
+
if (+/^v(\d+)/.exec(process.version)[1] < 16) {
|
|
42
|
+
console.error('pkmn-debug requires Node v16+');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
execFileSync('npm', ['install', ...missing, '--global'], {encoding: 'utf8'});
|
|
47
|
+
|
|
48
|
+
const node_modules = execFileSync('npm', ['root', '--global'], {encoding: 'utf8'}).trim();
|
|
49
|
+
if (!process.env.NODE_PATH || !process.env.NODE_PATH.includes(node_modules)) {
|
|
50
|
+
console.error(`NODE_PATH must include npm's global node_modules directory: '${node_modules}'`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const {Generations} = require('@pkmn/data');
|
|
56
|
+
|
|
57
|
+
const {run} = require('../../build/tools/debug');
|
|
58
|
+
|
|
59
|
+
run(new Generations(Dex)).catch(err => {
|
|
60
|
+
console.error(err.message);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
});
|
package/src/lib/gen1/README.md
CHANGED
|
@@ -332,7 +332,9 @@ must be gleaned through the `Battle` state). Useful size and offset information
|
|
|
332
332
|
the [`layout.json`](../../data/layout.json) which exists to simplify writing driver code.
|
|
333
333
|
|
|
334
334
|
Documentation of the wire protocol used for logging traces when `-Dtrace` is enabled can be found in
|
|
335
|
-
[PROTOCOL.md](../../../docs/PROTOCOL.md).
|
|
335
|
+
[PROTOCOL.md](../../../docs/PROTOCOL.md). Note that the
|
|
336
|
+
[`pkmn-debug`](../../../README.md#pkmn-debug) tool exists to display the binary protocol and battle
|
|
337
|
+
data in the browser for ease of debugging.
|
|
336
338
|
|
|
337
339
|
### `Battle`
|
|
338
340
|
|
|
@@ -476,7 +478,8 @@ Smogon](https://www.smogon.com/forums/posts/5933177/show):
|
|
|
476
478
|
- Confusion self-hits use the wrong damage formula resulting in off-by-one errors (and also fail to
|
|
477
479
|
account for an opponent's Reflect). Furthermore, the confusion self-hit damage will erroneously be
|
|
478
480
|
inflicted to the confused user's substitute if the confused user had been attempting to use a
|
|
479
|
-
self-targeting move.
|
|
481
|
+
self-targeting move. Finally, Pokémon Showdown erroneously considers the *uncapped* self-hit
|
|
482
|
+
damage for the purposes of tracking the battle's last damage.
|
|
480
483
|
|
|
481
484
|
Beyond the general bugs listed above, several move effects are implemented incorrectly by Pokémon
|
|
482
485
|
Showdown. Some of these moves are [too fundamentally broken to be implemented](#unimplementable) by
|
|
@@ -486,16 +489,25 @@ the pkmn engine, but the following moves have their broken behavior preserved in
|
|
|
486
489
|
incorrectly still gets applied to Bide's user.
|
|
487
490
|
- **Leech Seed**: Leech Seed fails to heal its source side if a seeded target faints due to
|
|
488
491
|
recoil/crash damage on Pokémon Showdown. Pokémon Showdown also updates the battle's last damage
|
|
489
|
-
field with the uncapped Leech Seed damage
|
|
492
|
+
field with the uncapped Leech Seed damage if the residual damage is applied which has consequences
|
|
493
|
+
for Bide and Counter.
|
|
490
494
|
- **Pay Day**: Pay Day should still scatter coins if it hits (but doesn't break) and opponent's
|
|
491
495
|
Substitute but doesn't on Pokémon Showdown.
|
|
492
496
|
- **Flinch**: Flinching does not get cleared during move selection on Pokémon Showdown and is
|
|
493
497
|
instead cleared in Pokémon Showdown's "residual" phase, meaning the flinch status gets erroneously
|
|
494
498
|
preserved across fainting (as fainting triggers Pokémon Showdown's "instaswitch" behavior which
|
|
495
499
|
skips end-of-turn residuals).
|
|
500
|
+
- **Rage**: Rage boosts should still result in burn/paralysis [stat modification
|
|
501
|
+
errors](https://pkmn.cc/bulba-glitch-1#Stat_modification_errors) but don't on Pokémon Showdown.
|
|
502
|
+
Furthermore, Pokémon Showdown only builds Rage for Disable/Explosion (hit/miss) when attacking
|
|
503
|
+
into a Substitute instead of all moves. Pokémon Showdown also implements the [Rage and Thrash /
|
|
504
|
+
Petal Dance accuracy bug]( https://www.youtube.com/watch?v=NC5gbJeExbs) incorrectly, as the
|
|
505
|
+
accuracy only gets written when the volatile is present which can lead to incorrect accuracy
|
|
506
|
+
values on turns where Rage is used on the same turn as a move which modifies accuracy or evasion.
|
|
496
507
|
- **Thrash** / **Petal Dance**: once a Pokémon is locked into a thrashing move they will lose all
|
|
497
508
|
speed ties on Pokémon Showdown (due to the fact that if they win the speed tie their action will
|
|
498
|
-
then get "changed" and inserted back into the queue after their opponent's action).
|
|
509
|
+
then get "changed" and inserted back into the queue after their opponent's action). Pokémon
|
|
510
|
+
Showdown also implements accuracy incorrectly, as covered above.
|
|
499
511
|
- **Freeze** / **Sleep**: Pokémon Showdown requires a move to be selected when a Pokémon is frozen
|
|
500
512
|
or sleeping and uses that in the event that the status is removed while on the cartridge no
|
|
501
513
|
selection is possible and no turn exists for the thawed/woken Pokémon to act except in the case of
|
|
@@ -520,7 +532,8 @@ the pkmn engine, but the following moves have their broken behavior preserved in
|
|
|
520
532
|
Pokémon Showdown implementing the health check based on floating point division instead of integer
|
|
521
533
|
divison like on the cartridge (meaning the Substitute 1/4 glitch will only occur if the Pokémon's
|
|
522
534
|
maximum HP is evenly divisible by 4). Finally, Substitute also incorrectly blocks Dream Eater on
|
|
523
|
-
Pokémon Showdown
|
|
535
|
+
Pokémon Showdown and will incorrectly still heal 1 HP for any draining moves if the attack does 0
|
|
536
|
+
damage.
|
|
524
537
|
|
|
525
538
|
In addition to numerous cases where Pokémon Showdown uses the wrong type of message (e.g. `|-fail`
|
|
526
539
|
vs. `|-miss|` vs. `|-immune|`, e.g. in the case of Leech Seed) which are not documented here, Pokémon
|