@pkmn/engine 0.1.0-dev.6195e056 → 0.1.0-dev.63cef311
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 +1 -1
- package/build/pkg/index.d.ts +1 -1
- package/build/pkg/index.js +1 -1
- package/build/pkg/index.js.map +1 -1
- package/package.json +4 -3
- package/src/lib/common/debug.zig +64 -0
- package/src/lib/gen1/README.md +2 -1
- package/src/lib/gen1/data.zig +1 -1
- package/src/lib/gen1/mechanics.zig +33 -16
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ implementation of both Pokémon battles as defined by the original game code, an
|
|
|
22
22
|
Showdown](https://pokemonshowdown.com/)[^1] simulator which represents Pokémon battling as practically
|
|
23
23
|
interpreted online.
|
|
24
24
|
|
|
25
|
-
The pkmn engine is
|
|
25
|
+
The pkmn engine is [**more than 1000× faster**](docs/TESTING.md#results) than the
|
|
26
26
|
[patched](docs/TESTING.md#patches) [Pokémon Showdown simulator
|
|
27
27
|
code](https://github.com/smogon/pokemon-showdown) when playing out supported formats in
|
|
28
28
|
compatibility mode and is extensively [tested](docs/TESTING.md) and [documented](docs). Note,
|
package/build/pkg/index.d.ts
CHANGED
|
@@ -362,7 +362,7 @@ export declare class Choice {
|
|
|
362
362
|
* Only numeric choice data is supported.
|
|
363
363
|
*/
|
|
364
364
|
static parse(choice: string): Choice;
|
|
365
|
-
/** Formats a `Choice`
|
|
365
|
+
/** Formats a `Choice` into a Pokémon Showdown compatible choice string. */
|
|
366
366
|
static format(choice: Choice): string;
|
|
367
367
|
/** Returns the "pass" `Choice`. */
|
|
368
368
|
static pass(): Choice;
|
package/build/pkg/index.js
CHANGED
|
@@ -80,7 +80,7 @@ class Choice {
|
|
|
80
80
|
const data = +(m[4] ?? m[7] ?? 0);
|
|
81
81
|
return { type, data };
|
|
82
82
|
}
|
|
83
|
-
/** Formats a `Choice`
|
|
83
|
+
/** Formats a `Choice` into a Pokémon Showdown compatible choice string. */
|
|
84
84
|
static format(choice) {
|
|
85
85
|
return choice.type === 'pass' ? choice.type : `${choice.type} ${choice.data}`;
|
|
86
86
|
}
|
package/build/pkg/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pkg/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pkg/index.ts"],"names":[],"mappings":"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"}
|
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.63cef311",
|
|
4
4
|
"description": "A minimal, complete, Pokémon battle simulation engine optimized for performance",
|
|
5
5
|
"repository": "github:pkmn/engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"src/lib/binding/node.zig",
|
|
14
14
|
"src/lib/binding/wasm.zig",
|
|
15
15
|
"src/lib/common/data.zig",
|
|
16
|
+
"src/lib/common/debug.zig",
|
|
16
17
|
"src/lib/common/options.zig",
|
|
17
18
|
"src/lib/common/protocol.zig",
|
|
18
19
|
"src/lib/common/rng.zig",
|
|
@@ -56,14 +57,14 @@
|
|
|
56
57
|
"access": "public"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
|
-
"@pkmn/data": "^0.7.
|
|
60
|
+
"@pkmn/data": "^0.7.34",
|
|
60
61
|
"@pkmn/protocol": "^0.6.11"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@pkmn/eslint-config": "^3.0.0-alpha.0",
|
|
64
65
|
"@pkmn/img": "^0.2.8",
|
|
65
66
|
"@pkmn/sets": "^5.0.4",
|
|
66
|
-
"@pkmn/sim": "0.7.
|
|
67
|
+
"@pkmn/sim": "0.7.34",
|
|
67
68
|
"@types/html-minifier": "^4.0.2",
|
|
68
69
|
"@types/jest": "^29.5.0",
|
|
69
70
|
"@types/minimist": "^1.2.2",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const debug = std.debug;
|
|
4
|
+
const io = std.io;
|
|
5
|
+
|
|
6
|
+
pub fn print(value: anytype) void {
|
|
7
|
+
debug.getStderrMutex().lock();
|
|
8
|
+
defer debug.getStderrMutex().unlock();
|
|
9
|
+
const stderr = io.getStdErr().writer();
|
|
10
|
+
|
|
11
|
+
nosuspend {
|
|
12
|
+
stderr.writeAll("\x1b[41m") catch return;
|
|
13
|
+
switch (@typeInfo(@TypeOf(value))) {
|
|
14
|
+
.Struct => |info| {
|
|
15
|
+
if (info.is_tuple) {
|
|
16
|
+
inline for (info.fields, 0..) |f, i| {
|
|
17
|
+
inspect(@field(value, f.name));
|
|
18
|
+
if (i < info.fields.len - 1) stderr.writeAll(" ") catch return;
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
inspect(value);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
else => inspect(value),
|
|
25
|
+
}
|
|
26
|
+
stderr.writeAll("\x1b[K\x1b[0m\n") catch return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn inspect(value: anytype) void {
|
|
31
|
+
const stderr = io.getStdErr().writer();
|
|
32
|
+
|
|
33
|
+
nosuspend {
|
|
34
|
+
const err = "Unable to format type '" ++ @typeName(@TypeOf(value)) ++ "'";
|
|
35
|
+
switch (@typeInfo(@TypeOf(value))) {
|
|
36
|
+
.Array => |info| {
|
|
37
|
+
if (info.child == u8) return stderr.print("{s}", .{value}) catch return;
|
|
38
|
+
@compileError(err);
|
|
39
|
+
},
|
|
40
|
+
.Pointer => |ptr_info| switch (ptr_info.size) {
|
|
41
|
+
.One => switch (@typeInfo(ptr_info.child)) {
|
|
42
|
+
.Array => |info| {
|
|
43
|
+
if (info.child == u8) return stderr.print("{s}", .{value}) catch return;
|
|
44
|
+
@compileError(err);
|
|
45
|
+
},
|
|
46
|
+
.Enum, .Union, .Struct => return inspect(value.*),
|
|
47
|
+
else => @compileError(err),
|
|
48
|
+
},
|
|
49
|
+
.Many, .C => {
|
|
50
|
+
if (ptr_info.sentinel) |_| return inspect(std.mem.span(value));
|
|
51
|
+
if (ptr_info.child == u8) {
|
|
52
|
+
return stderr.print("{s}", .{std.mem.span(value)}) catch return;
|
|
53
|
+
}
|
|
54
|
+
@compileError(err);
|
|
55
|
+
},
|
|
56
|
+
.Slice => {
|
|
57
|
+
if (ptr_info.child == u8) return stderr.print("{s}", .{value}) catch return;
|
|
58
|
+
@compileError(err);
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
else => stderr.print("{}", .{value}) catch return,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/lib/gen1/README.md
CHANGED
|
@@ -515,7 +515,8 @@ the pkmn engine, but the following moves have their broken behavior preserved in
|
|
|
515
515
|
glitch](https://glitchcity.wiki/Substitute_%C2%BC_HP_glitch) also fails in many cases due to
|
|
516
516
|
Pokémon Showdown implementing the health check based on floating point division instead of integer
|
|
517
517
|
divison like on the cartridge (meaning the Substitute 1/4 glitch will only occur if the Pokémon's
|
|
518
|
-
maximum HP is evenly divisible by 4).
|
|
518
|
+
maximum HP is evenly divisible by 4). Finally, Substitute also incorrectly blocks Dream Eater on
|
|
519
|
+
Pokémon Showdown.
|
|
519
520
|
|
|
520
521
|
In addition to numerous cases where Pokémon Showdown uses the wrong type of message (e.g. `|-fail`
|
|
521
522
|
vs. `|-miss|` vs. `|-immune|`, e.g. in the case of Leech Seed) which are not documented here, Pokémon
|
package/src/lib/gen1/data.zig
CHANGED
|
@@ -272,7 +272,7 @@ pub const Status = enum(u8) {
|
|
|
272
272
|
return num > 0;
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
///
|
|
275
|
+
/// Returns a human-readable representation of the status `num`.
|
|
276
276
|
pub fn name(num: u8) []const u8 {
|
|
277
277
|
if (Status.is(num, .EXT)) return if (num == Status.TOX) "TOX" else "SLF";
|
|
278
278
|
if (Status.is(num, .SLP)) return "SLP";
|
|
@@ -1278,7 +1278,9 @@ fn moveHit(battle: anytype, player: Player, move: Move.Data, immune: *bool, mist
|
|
|
1278
1278
|
if (move.effect == .Swift) return true;
|
|
1279
1279
|
if (foe.active.volatiles.Invulnerable) break :miss true;
|
|
1280
1280
|
}
|
|
1281
|
-
if (move.effect == .DreamEater and !Status.is(foe.stored().status, .SLP)
|
|
1281
|
+
if (move.effect == .DreamEater and (!Status.is(foe.stored().status, .SLP) or
|
|
1282
|
+
(showdown and foe.active.volatiles.Substitute)))
|
|
1283
|
+
{
|
|
1282
1284
|
immune.* = true;
|
|
1283
1285
|
if (showdown) return false;
|
|
1284
1286
|
break :miss true;
|
|
@@ -1480,16 +1482,20 @@ fn endTurn(battle: anytype, log: anytype) @TypeOf(log).Error!Result {
|
|
|
1480
1482
|
|
|
1481
1483
|
fn checkEBC(battle: anytype) bool {
|
|
1482
1484
|
for (battle.sides, 0..) |side, i| {
|
|
1485
|
+
const foe = battle.sides[~@intCast(u1, i)];
|
|
1486
|
+
|
|
1483
1487
|
var foe_all_ghosts = true;
|
|
1484
1488
|
var foe_all_transform = true;
|
|
1489
|
+
for (foe.order, 0..) |id, j| {
|
|
1490
|
+
if (id == 0) break;
|
|
1491
|
+
const active = j == 0;
|
|
1492
|
+
const pokemon = foe.pokemon[id - 1];
|
|
1485
1493
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
const ghost = pokemon.hp == 0 or pokemon.types.includes(.Ghost);
|
|
1494
|
+
const ghost = pokemon.hp == 0 or
|
|
1495
|
+
(if (active) foe.active.types else pokemon.types).includes(.Ghost);
|
|
1490
1496
|
foe_all_ghosts = foe_all_ghosts and ghost;
|
|
1491
1497
|
foe_all_transform = foe_all_transform and pokemon.hp == 0 or transform: {
|
|
1492
|
-
for (pokemon.moves) |m| {
|
|
1498
|
+
for (if (active) foe.active.moves else pokemon.moves) |m| {
|
|
1493
1499
|
if (m.id == .None) break :transform true;
|
|
1494
1500
|
if (m.id != .Transform) break :transform false;
|
|
1495
1501
|
}
|
|
@@ -1497,10 +1503,14 @@ fn checkEBC(battle: anytype) bool {
|
|
|
1497
1503
|
};
|
|
1498
1504
|
}
|
|
1499
1505
|
|
|
1500
|
-
for (side.
|
|
1506
|
+
for (side.order, 0..) |id, j| {
|
|
1507
|
+
if (id == 0) break;
|
|
1508
|
+
const active = j == 0;
|
|
1509
|
+
const pokemon = side.pokemon[id - 1];
|
|
1510
|
+
|
|
1501
1511
|
if (pokemon.hp == 0 or Status.is(pokemon.status, .FRZ)) continue;
|
|
1502
1512
|
const transform = foe_all_transform and transform: {
|
|
1503
|
-
for (pokemon.moves) |m| {
|
|
1513
|
+
for (if (active) side.active.moves else pokemon.moves) |m| {
|
|
1504
1514
|
if (m.id == .None) break :transform true;
|
|
1505
1515
|
if (m.id != .Transform) break :transform false;
|
|
1506
1516
|
}
|
|
@@ -1508,7 +1518,7 @@ fn checkEBC(battle: anytype) bool {
|
|
|
1508
1518
|
};
|
|
1509
1519
|
if (transform) continue;
|
|
1510
1520
|
const no_pp = foe_all_ghosts and no_pp: {
|
|
1511
|
-
for (pokemon.moves) |m| {
|
|
1521
|
+
for (if (active) side.active.moves else pokemon.moves) |m| {
|
|
1512
1522
|
if (m.pp != 0) break :no_pp false;
|
|
1513
1523
|
}
|
|
1514
1524
|
break :no_pp true;
|
|
@@ -1753,6 +1763,11 @@ pub const Effects = struct {
|
|
|
1753
1763
|
var side = battle.side(player);
|
|
1754
1764
|
var stored = side.stored();
|
|
1755
1765
|
|
|
1766
|
+
if (battle.last_damage == 0) {
|
|
1767
|
+
assert(showdown);
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1756
1771
|
const drain = @max(battle.last_damage / 2, 1);
|
|
1757
1772
|
battle.last_damage = drain;
|
|
1758
1773
|
|
|
@@ -2063,7 +2078,7 @@ pub const Effects = struct {
|
|
|
2063
2078
|
return log.fail(foe_ident, if (Status.is(foe_stored.status, .PSN))
|
|
2064
2079
|
if (!showdown)
|
|
2065
2080
|
.Poison
|
|
2066
|
-
else if (toxic ==
|
|
2081
|
+
else if (toxic == (foe_stored.status == Status.TOX))
|
|
2067
2082
|
if (toxic) .Toxic else .Poison
|
|
2068
2083
|
else
|
|
2069
2084
|
.None
|
|
@@ -2514,13 +2529,9 @@ fn clearVolatiles(battle: anytype, who: Player, log: anytype) !void {
|
|
|
2514
2529
|
volatiles.LeechSeed = false;
|
|
2515
2530
|
try log.end(ident, .LeechSeed);
|
|
2516
2531
|
}
|
|
2517
|
-
if (volatiles.Toxic) {
|
|
2532
|
+
if (!showdown and volatiles.Toxic) {
|
|
2518
2533
|
volatiles.Toxic = false;
|
|
2519
|
-
// volatiles.toxic is left unchanged, except on Pokémon Showdown which clears it
|
|
2520
|
-
if (showdown) {
|
|
2521
|
-
volatiles.toxic = 0;
|
|
2522
|
-
try log.end(ident, .Toxic);
|
|
2523
|
-
}
|
|
2534
|
+
// volatiles.toxic is left unchanged, except on Pokémon Showdown which clears it (below)
|
|
2524
2535
|
}
|
|
2525
2536
|
if (volatiles.LightScreen) {
|
|
2526
2537
|
volatiles.LightScreen = false;
|
|
@@ -2530,6 +2541,12 @@ fn clearVolatiles(battle: anytype, who: Player, log: anytype) !void {
|
|
|
2530
2541
|
volatiles.Reflect = false;
|
|
2531
2542
|
try log.end(ident, .Reflect);
|
|
2532
2543
|
}
|
|
2544
|
+
if (showdown and volatiles.Toxic) {
|
|
2545
|
+
volatiles.Toxic = false;
|
|
2546
|
+
// Pokémon Showdown erroneously clears the toxic counter
|
|
2547
|
+
volatiles.toxic = 0;
|
|
2548
|
+
try log.end(ident, .Toxic);
|
|
2549
|
+
}
|
|
2533
2550
|
}
|
|
2534
2551
|
|
|
2535
2552
|
fn disabled(side: *Side, ident: ID, log: anytype) !bool {
|