@pkmn/engine 0.1.0-dev.6c5fdce2 → 0.1.0-dev.6cc9e067
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/LICENSE +1 -3
- package/README.md +34 -11
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +4 -5
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +4 -4
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +2 -2
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +2 -3
- package/build/tools/debug.js.map +1 -1
- package/build.zig +57 -60
- package/package.json +29 -22
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/protocol.zig +16 -21
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/gen1/chance.zig +27 -26
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +18 -18
- package/src/lib/gen1/helpers.zig +35 -15
- package/src/lib/gen1/mechanics.zig +41 -14
- package/src/lib/gen2/chance.zig +38 -37
- package/src/lib/gen2/data.zig +8 -8
- package/src/lib/gen2/generated.zig +0 -2
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +112 -32
- package/src/lib/{binding/node.zig → node.zig} +3 -6
- package/src/lib/pkmn.zig +27 -12
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
|
@@ -999,7 +999,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
999
999
|
|
|
1000
1000
|
// Weather
|
|
1001
1001
|
if (battle.field.weather != .None) {
|
|
1002
|
-
assert(battle.field.weather_duration > 0);
|
|
1003
1002
|
battle.field.weather_duration -= 1;
|
|
1004
1003
|
|
|
1005
1004
|
if (battle.field.weather_duration == 0) {
|
|
@@ -1058,7 +1057,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1058
1057
|
var volatiles = &side.active.volatiles;
|
|
1059
1058
|
|
|
1060
1059
|
if (volatiles.PerishSong) {
|
|
1061
|
-
assert(volatiles.perish_song > 0);
|
|
1062
1060
|
volatiles.perish_song -= 1;
|
|
1063
1061
|
|
|
1064
1062
|
assert(volatiles.perish_song < 3);
|
|
@@ -1195,7 +1193,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1195
1193
|
var volatiles = &side.active.volatiles;
|
|
1196
1194
|
|
|
1197
1195
|
if (volatiles.Encore) {
|
|
1198
|
-
assert(volatiles.encore > 0);
|
|
1199
1196
|
volatiles.encore -= 1;
|
|
1200
1197
|
|
|
1201
1198
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
@@ -1217,7 +1214,6 @@ fn updateSideCondition(
|
|
|
1217
1214
|
) !void {
|
|
1218
1215
|
var side = battle.side(player);
|
|
1219
1216
|
if (@field(side.conditions, key)) {
|
|
1220
|
-
assert(@field(side.conditions, value) > 0);
|
|
1221
1217
|
@field(side.conditions, value) -= 1;
|
|
1222
1218
|
|
|
1223
1219
|
if (@field(side.conditions, value) == 0) {
|
|
@@ -1308,14 +1304,32 @@ pub const Effects = struct {
|
|
|
1308
1304
|
}
|
|
1309
1305
|
|
|
1310
1306
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1307
|
+
assert(!state.miss);
|
|
1308
|
+
var side = battle.side(player);
|
|
1309
|
+
|
|
1310
|
+
const ident = battle.active(player);
|
|
1311
|
+
|
|
1312
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1313
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1314
|
+
return options.log.fail(.{ ident, .None });
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1318
|
+
if (side.stored().hp <= hp) {
|
|
1319
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1320
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1321
|
+
return options.log.fail(.{ ident, .None });
|
|
1322
|
+
} else {
|
|
1323
|
+
side.stored().hp -= hp;
|
|
1324
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1325
|
+
|
|
1326
|
+
const before = side.active.boosts.atk;
|
|
1327
|
+
var i: usize = 0;
|
|
1328
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1329
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1330
|
+
}
|
|
1331
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1332
|
+
}
|
|
1319
1333
|
}
|
|
1320
1334
|
|
|
1321
1335
|
pub const bide = struct {
|
|
@@ -1327,7 +1341,6 @@ pub const Effects = struct {
|
|
|
1327
1341
|
|
|
1328
1342
|
if (!volatiles.Bide) return;
|
|
1329
1343
|
|
|
1330
|
-
assert(volatiles.attacks > 0);
|
|
1331
1344
|
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1332
1345
|
// if (!extend) volatiles.attacks = 0;
|
|
1333
1346
|
// } else {
|
|
@@ -1601,8 +1614,16 @@ pub const Effects = struct {
|
|
|
1601
1614
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1602
1615
|
}
|
|
1603
1616
|
|
|
1604
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1605
|
-
|
|
1617
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1618
|
+
var side = battle.side(player);
|
|
1619
|
+
var stored = side.stored();
|
|
1620
|
+
|
|
1621
|
+
stored.hp = 0;
|
|
1622
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1623
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1624
|
+
if (!showdown) stored.status = 0;
|
|
1625
|
+
side.active.volatiles.LeechSeed = false;
|
|
1626
|
+
side.active.volatiles.DestinyBond = false;
|
|
1606
1627
|
}
|
|
1607
1628
|
|
|
1608
1629
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1728,8 +1749,26 @@ pub const Effects = struct {
|
|
|
1728
1749
|
foe.active.volatiles.frozen = true;
|
|
1729
1750
|
}
|
|
1730
1751
|
|
|
1731
|
-
|
|
1732
|
-
|
|
1752
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1753
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1754
|
+
var side = battle.side(player);
|
|
1755
|
+
var volatiles = &side.active.volatiles;
|
|
1756
|
+
|
|
1757
|
+
if (state.miss) {
|
|
1758
|
+
volatiles.fury_cutter = 0;
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1763
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1764
|
+
volatiles.fury_cutter += 1;
|
|
1765
|
+
|
|
1766
|
+
if (showdown) {
|
|
1767
|
+
state.bp *= 2;
|
|
1768
|
+
assert(state.bp <= 160);
|
|
1769
|
+
} else {
|
|
1770
|
+
state.damage *|= 2;
|
|
1771
|
+
}
|
|
1733
1772
|
}
|
|
1734
1773
|
|
|
1735
1774
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1890,7 +1929,29 @@ pub const Effects = struct {
|
|
|
1890
1929
|
}
|
|
1891
1930
|
|
|
1892
1931
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1893
|
-
|
|
1932
|
+
var side = battle.side(player);
|
|
1933
|
+
|
|
1934
|
+
if (state.miss) {
|
|
1935
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1936
|
+
try options.log.lastmiss(.{});
|
|
1937
|
+
return options.log.miss(.{battle.active(player)});
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
const move = battle.foe(player).lastMove(false);
|
|
1941
|
+
if (move == .None or move == .Struggle) {
|
|
1942
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
var slot: u4 = 0;
|
|
1946
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1947
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1948
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1949
|
+
}
|
|
1950
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1951
|
+
|
|
1952
|
+
side.active.move(slot).id = move;
|
|
1953
|
+
side.active.move(slot).pp = 5;
|
|
1954
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1894
1955
|
}
|
|
1895
1956
|
|
|
1896
1957
|
pub fn mirrorMove(
|
|
@@ -2398,17 +2459,37 @@ pub const Effects = struct {
|
|
|
2398
2459
|
}
|
|
2399
2460
|
|
|
2400
2461
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2401
|
-
|
|
2402
|
-
// var foe = battle.foe(player);
|
|
2462
|
+
var foe = battle.foe(player);
|
|
2403
2463
|
|
|
2404
|
-
|
|
2464
|
+
const last = foe.lastMove(false);
|
|
2465
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2466
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2467
|
+
}
|
|
2405
2468
|
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
//
|
|
2411
|
-
|
|
2469
|
+
var slot: u4 = 0;
|
|
2470
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2471
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2472
|
+
}
|
|
2473
|
+
// If the move was called via Metronome it might not exist
|
|
2474
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2475
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2479
|
+
const pp = @min(
|
|
2480
|
+
foe.active.move(slot).pp,
|
|
2481
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2482
|
+
);
|
|
2483
|
+
foe.active.move(slot).pp -= pp;
|
|
2484
|
+
if (!foe.active.volatiles.Transform) {
|
|
2485
|
+
var move_slot = foe.stored().move(slot);
|
|
2486
|
+
assert(move_slot.pp > 0);
|
|
2487
|
+
|
|
2488
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2489
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2412
2493
|
}
|
|
2413
2494
|
|
|
2414
2495
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -3014,13 +3095,12 @@ pub const Rolls = struct {
|
|
|
3014
3095
|
return hits;
|
|
3015
3096
|
}
|
|
3016
3097
|
|
|
3017
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3018
|
-
|
|
3098
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3099
|
+
assert(!showdown);
|
|
3100
|
+
const pp: u3 = if (options.calc.overridden(player, .spite)) |val|
|
|
3019
3101
|
val + 1
|
|
3020
|
-
else if (showdown)
|
|
3021
|
-
battle.rng.range(u8, 2, 6)
|
|
3022
3102
|
else
|
|
3023
|
-
(battle.rng.next() & 3) + 2;
|
|
3103
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3024
3104
|
|
|
3025
3105
|
assert(pp >= 2 and pp <= 5);
|
|
3026
3106
|
try options.chance.spite(player, pp);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
-
const pkmn = @import("../pkmn.zig");
|
|
3
2
|
|
|
4
|
-
const
|
|
3
|
+
const pkmn = @import("pkmn.zig");
|
|
4
|
+
|
|
5
|
+
const js = @import("common/js.zig");
|
|
5
6
|
|
|
6
7
|
const assert = std.debug.assert;
|
|
7
8
|
|
|
@@ -72,7 +73,6 @@ fn update(gen: anytype) c.napi_callback {
|
|
|
72
73
|
var len: usize = 0;
|
|
73
74
|
assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
|
|
74
75
|
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
75
|
-
assert(data != null);
|
|
76
76
|
|
|
77
77
|
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
78
78
|
const c1: pkmn.Choice = @bitCast(js.Number.get(env, argv[1], u8));
|
|
@@ -85,7 +85,6 @@ fn update(gen: anytype) c.napi_callback {
|
|
|
85
85
|
else => result: {
|
|
86
86
|
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
87
87
|
assert(len == gen.LOGS_SIZE);
|
|
88
|
-
assert(data != null);
|
|
89
88
|
|
|
90
89
|
const buf = @as([*]u8, @ptrCast(data.?))[0..gen.LOGS_SIZE];
|
|
91
90
|
var stream: pkmn.protocol.ByteStream = .{ .buffer = buf };
|
|
@@ -116,7 +115,6 @@ fn choices(gen: anytype) c.napi_callback {
|
|
|
116
115
|
var len: usize = 0;
|
|
117
116
|
assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
|
|
118
117
|
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
119
|
-
assert(data != null);
|
|
120
118
|
|
|
121
119
|
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
122
120
|
const player: pkmn.Player = @enumFromInt(js.Number.get(env, argv[1], u8));
|
|
@@ -124,7 +122,6 @@ fn choices(gen: anytype) c.napi_callback {
|
|
|
124
122
|
|
|
125
123
|
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
126
124
|
assert(len == gen.CHOICES_SIZE);
|
|
127
|
-
assert(data != null);
|
|
128
125
|
|
|
129
126
|
const out = @as([*]pkmn.Choice, @ptrCast(data.?))[0..gen.CHOICES_SIZE];
|
|
130
127
|
const n = battle.choices(player, request, out);
|
package/src/lib/pkmn.zig
CHANGED
|
@@ -26,9 +26,7 @@ pub const Result = @import("common/data.zig").Result;
|
|
|
26
26
|
pub const PSRNG = @import("common/rng.zig").PSRNG;
|
|
27
27
|
|
|
28
28
|
/// Namespace for helpers related to protocol message logging.
|
|
29
|
-
pub const protocol = if (options.internal) struct {
|
|
30
|
-
pub usingnamespace @import("common/protocol.zig");
|
|
31
|
-
} else struct {
|
|
29
|
+
pub const protocol = if (options.internal) @import("common/protocol.zig") else struct {
|
|
32
30
|
/// Logs protocol information to its `Writer` during a battle update when
|
|
33
31
|
/// `options.log` is enabled.
|
|
34
32
|
pub const Log = @import("common/protocol.zig").Log;
|
|
@@ -82,16 +80,14 @@ pub const gen1 = struct {
|
|
|
82
80
|
/// via `overrides` and tracks `summaries` of information relevant to damage calculation.
|
|
83
81
|
pub const Calc = @import("gen1/calc.zig").Calc;
|
|
84
82
|
/// Namespace for types associated with supported Generation I Pokémon damage calc features.
|
|
85
|
-
pub const calc = if (options.internal) struct {
|
|
86
|
-
pub usingnamespace @import("gen1/calc.zig");
|
|
87
|
-
} else struct {
|
|
83
|
+
pub const calc = if (options.internal) @import("gen1/calc.zig") else struct {
|
|
88
84
|
/// TODO
|
|
89
85
|
pub const Overrides = @import("gen1/calc.zig").Overrides;
|
|
90
86
|
/// Information relevant to damage calculation that occured during a Generation I
|
|
91
87
|
/// battle `update`.
|
|
92
88
|
pub const Summaries = @import("gen1/calc.zig").Summaries;
|
|
93
89
|
/// Information relevant to damage calculation that occured during a Generation I
|
|
94
|
-
///
|
|
90
|
+
/// battle `update` for a single player.
|
|
95
91
|
pub const Summary = @import("gen1/calc.zig").Summary;
|
|
96
92
|
/// Null object pattern implementation of Generation I `Calc` which does nothing,
|
|
97
93
|
/// though damage calculator support should additionally be turned off
|
|
@@ -102,12 +98,31 @@ pub const gen1 = struct {
|
|
|
102
98
|
pub const helpers = @import("gen1/helpers.zig");
|
|
103
99
|
};
|
|
104
100
|
|
|
101
|
+
/// TODO
|
|
102
|
+
pub const gen2 = struct {
|
|
103
|
+
pub usingnamespace @import("gen2/data.zig");
|
|
104
|
+
pub const Chance = @import("gen2/chance.zig").Chance;
|
|
105
|
+
pub const chance = struct {
|
|
106
|
+
pub const Actions = @import("gen2/chance.zig").Actions;
|
|
107
|
+
pub const Action = @import("gen2/chance.zig").Action;
|
|
108
|
+
pub const Durations = @import("gen2/chance.zig").Durations;
|
|
109
|
+
pub const Duration = @import("gen2/chance.zig").Duration;
|
|
110
|
+
pub const NULL = @import("gen2/chance.zig").NULL;
|
|
111
|
+
};
|
|
112
|
+
pub const Calc = @import("gen2/calc.zig").Calc;
|
|
113
|
+
pub const calc = if (options.internal) @import("gen2/calc.zig") else struct {
|
|
114
|
+
pub const Overrides = @import("gen2/calc.zig").Overrides;
|
|
115
|
+
pub const Summaries = @import("gen2/calc.zig").Summaries;
|
|
116
|
+
pub const Summary = @import("gen2/calc.zig").Summary;
|
|
117
|
+
pub const NULL = @import("gen2/calc.zig").NULL;
|
|
118
|
+
};
|
|
119
|
+
pub const helpers = @import("gen2/helpers.zig");
|
|
120
|
+
};
|
|
121
|
+
|
|
105
122
|
// Internal APIs used by other pkmn libraries, not actually part of the public API.
|
|
106
123
|
// NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
|
|
107
|
-
pub const js = if (options.internal) struct {
|
|
108
|
-
pub usingnamespace @import("common/js.zig");
|
|
109
|
-
} else struct {};
|
|
124
|
+
pub const js = if (options.internal) @import("common/js.zig") else struct {};
|
|
110
125
|
pub const bindings = if (options.internal) struct {
|
|
111
|
-
pub const node = @import("
|
|
112
|
-
pub const wasm = @import("
|
|
126
|
+
pub const node = @import("node.zig");
|
|
127
|
+
pub const wasm = @import("wasm.zig");
|
|
113
128
|
} else struct {};
|