@pkmn/engine 0.1.0-dev.a0447192 → 0.1.0-dev.a15b6c17
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 -1
- package/README.md +22 -12
- package/build/pkg/addon/browser.d.ts +1 -1
- package/build/pkg/addon/browser.js +6 -2
- package/build/pkg/addon/browser.js.map +1 -1
- package/build/pkg/addon/node.js +22 -8
- package/build/pkg/addon/node.js.map +1 -1
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +25 -5
- package/build/pkg/addon.js +8 -17
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +15 -18
- package/build/pkg/gen1.js +28 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +37 -100
- package/build/pkg/index.js +23 -75
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +11 -2
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +55 -12
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +19 -94
- package/build/tools/display/debug.jsx.map +1 -1
- package/build/tools/display/index.js +35 -15
- package/build/tools/display/index.js.map +1 -1
- package/build/tools/display/ui.jsx +20 -9
- package/build/tools/display/ui.jsx.map +1 -1
- package/build/tools/display/util.js +140 -1
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +239 -217
- package/package.json +34 -130
- package/src/bin/install-pkmn-engine +25 -9
- package/src/lib/bindings/c.zig +200 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +50 -0
- package/src/lib/common/array.zig +5 -12
- package/src/lib/common/debug.zig +33 -59
- package/src/lib/common/js.zig +3 -9
- package/src/lib/common/optional.zig +11 -21
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +228 -197
- package/src/lib/common/rational.zig +275 -284
- package/src/lib/common/rng.zig +1 -3
- package/src/lib/common/util.zig +14 -31
- package/src/lib/gen1/calc.zig +159 -163
- package/src/lib/gen1/chance.zig +146 -93
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +1 -3
- package/src/lib/gen1/data/types.zig +3 -4
- package/src/lib/gen1/data.zig +12 -16
- package/src/lib/gen1/helpers.zig +12 -20
- package/src/lib/gen1/mechanics.zig +103 -78
- package/src/lib/gen2/calc.zig +12 -23
- package/src/lib/gen2/chance.zig +12 -18
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +7 -5
- package/src/lib/gen2/data.zig +19 -22
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +9 -18
- package/src/lib/gen2/mechanics.zig +58 -55
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +4 -121
- package/src/lib/pkmn.zig +98 -19
- package/src/lib/wasm.zig +9 -9
- package/src/tools/display/dom.ts +43 -31
- package/src/tools/display/ui.css +0 -5
package/src/lib/gen2/data.zig
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
|
-
const pkmn = @import("../pkmn.zig");
|
|
5
|
-
|
|
6
|
-
const data = @import("../common/data.zig");
|
|
7
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
8
|
-
const protocol = @import("../common/protocol.zig");
|
|
9
|
-
const rng = @import("../common/rng.zig");
|
|
10
|
-
const util = @import("../common/util.zig");
|
|
11
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const calc = @import("calc.zig");
|
|
5
|
+
const chance = @import("chance.zig");
|
|
6
|
+
const data = @import("../common/data.zig");
|
|
7
|
+
const gen1 = @import("../gen1/data.zig");
|
|
12
8
|
const items = @import("data/items.zig");
|
|
9
|
+
const mechanics = @import("mechanics.zig");
|
|
13
10
|
const moves = @import("data/moves.zig");
|
|
11
|
+
const pkmn = @import("../pkmn.zig");
|
|
12
|
+
const protocol = @import("../common/protocol.zig");
|
|
13
|
+
const rng = @import("../common/rng.zig");
|
|
14
14
|
const species = @import("data/species.zig");
|
|
15
|
+
const std = @import("std");
|
|
15
16
|
const types = @import("data/types.zig");
|
|
16
|
-
|
|
17
|
-
const gen1 = @import("../gen1/data.zig");
|
|
18
|
-
|
|
19
|
-
const calc = @import("calc.zig");
|
|
20
|
-
const chance = @import("chance.zig");
|
|
21
|
-
const mechanics = @import("mechanics.zig");
|
|
17
|
+
const util = @import("../common/util.zig");
|
|
22
18
|
|
|
23
19
|
const assert = std.debug.assert;
|
|
24
20
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -61,7 +57,7 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
61
57
|
turn: u16 = 0,
|
|
62
58
|
field: Field = .{},
|
|
63
59
|
|
|
64
|
-
_: [padding]u8 =
|
|
60
|
+
_: [padding]u8 = @splat(0),
|
|
65
61
|
|
|
66
62
|
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
67
63
|
assert(isPointerTo(self, Self));
|
|
@@ -104,13 +100,13 @@ pub const Weather = enum(u4) {
|
|
|
104
100
|
};
|
|
105
101
|
|
|
106
102
|
pub const Side = extern struct {
|
|
107
|
-
pokemon: [6]Pokemon =
|
|
103
|
+
pokemon: [6]Pokemon = @splat(.{}),
|
|
108
104
|
active: ActivePokemon align(8) = .{},
|
|
109
105
|
conditions: Conditions = .{},
|
|
110
106
|
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
111
107
|
last_used_move: Move = .None,
|
|
112
108
|
|
|
113
|
-
_: [7]u8 =
|
|
109
|
+
_: [7]u8 = @splat(0),
|
|
114
110
|
|
|
115
111
|
const Conditions = packed struct(u32) {
|
|
116
112
|
Spikes: bool = false,
|
|
@@ -165,7 +161,7 @@ pub const Side = extern struct {
|
|
|
165
161
|
pub const ActivePokemon = extern struct {
|
|
166
162
|
volatiles: Volatiles align(4) = .{},
|
|
167
163
|
stats: Stats(u16) = .{},
|
|
168
|
-
moves: [4]MoveSlot =
|
|
164
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
169
165
|
boosts: Boosts = .{},
|
|
170
166
|
types: Types = .{},
|
|
171
167
|
species: Species = .None,
|
|
@@ -186,7 +182,7 @@ pub const ActivePokemon = extern struct {
|
|
|
186
182
|
|
|
187
183
|
pub const Pokemon = extern struct {
|
|
188
184
|
stats: Stats(u16) = .{},
|
|
189
|
-
moves: [4]MoveSlot =
|
|
185
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
190
186
|
types: Types = .{},
|
|
191
187
|
dvs: DVs = .{},
|
|
192
188
|
hp: u16 = 0,
|
|
@@ -453,8 +449,9 @@ test Types {
|
|
|
453
449
|
try expect(!Type.Steel.special());
|
|
454
450
|
try expect(Type.Dark.special());
|
|
455
451
|
|
|
456
|
-
try expectEqual(@as(u8, 3), Type.Water.
|
|
457
|
-
try expect(Type.
|
|
452
|
+
try expectEqual(@as(u8, 3), Type.precedence(.Water, .Water));
|
|
453
|
+
try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
|
|
454
|
+
try expect(Type.precedence(.Ice, .Fire) > Type.precedence(.Ice, .Water));
|
|
458
455
|
|
|
459
456
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|
|
460
457
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Water, .Fire));
|
|
@@ -1,37 +1,32 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const common = @import("../common/data.zig");
|
|
6
|
-
|
|
7
4
|
const data = @import("data.zig");
|
|
8
5
|
const mechanics = @import("mechanics.zig");
|
|
6
|
+
const std = @import("std");
|
|
9
7
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const Effects = mechanics.Effects;
|
|
17
|
-
const State = mechanics.State;
|
|
18
|
-
|
|
19
|
-
const canMove = mechanics.canMove;
|
|
8
|
+
const adjustDamage = mechanics.adjustDamage;
|
|
9
|
+
const afterMove = mechanics.afterMove;
|
|
10
|
+
const applyDamage = mechanics.applyDamage;
|
|
11
|
+
const buildRage = mechanics.buildRage;
|
|
12
|
+
const calcDamage = mechanics.calcDamage;
|
|
20
13
|
const canCharge = mechanics.canCharge;
|
|
14
|
+
const canMove = mechanics.canMove;
|
|
15
|
+
const checkCriticalHit = mechanics.checkCriticalHit;
|
|
16
|
+
const checkHit = mechanics.checkHit;
|
|
21
17
|
const decrementPP = mechanics.decrementPP;
|
|
18
|
+
const destinyBond = mechanics.destinyBond;
|
|
22
19
|
const doMove = mechanics.doMove;
|
|
23
|
-
const checkHit = mechanics.checkHit;
|
|
24
|
-
const checkCriticalHit = mechanics.checkCriticalHit;
|
|
25
|
-
const calcDamage = mechanics.calcDamage;
|
|
26
|
-
const adjustDamage = mechanics.adjustDamage;
|
|
27
|
-
const randomizeDamage = mechanics.randomizeDamage;
|
|
28
|
-
const applyDamage = mechanics.applyDamage;
|
|
29
|
-
const reportOutcome = mechanics.reportOutcome;
|
|
30
20
|
const effectChance = mechanics.effectChance;
|
|
31
|
-
const
|
|
32
|
-
const
|
|
21
|
+
const Effectiveness = data.Effectiveness;
|
|
22
|
+
const Effects = mechanics.Effects;
|
|
33
23
|
const kingsRock = mechanics.kingsRock;
|
|
34
|
-
const
|
|
24
|
+
const Move = data.Move;
|
|
25
|
+
const Player = common.Player;
|
|
26
|
+
const randomizeDamage = mechanics.randomizeDamage;
|
|
27
|
+
const reportOutcome = mechanics.reportOutcome;
|
|
28
|
+
const Result = common.Result;
|
|
29
|
+
const State = mechanics.State;
|
|
35
30
|
|
|
36
31
|
const SECONDARY = true;
|
|
37
32
|
const KINGS = true;
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
1
|
const common = @import("../common/data.zig");
|
|
2
|
+
const data = @import("data.zig");
|
|
3
|
+
const gen1 = @import("../gen1/data.zig");
|
|
4
4
|
const options = @import("../common/options.zig");
|
|
5
5
|
const rng = @import("../common/rng.zig");
|
|
6
|
-
|
|
7
|
-
const gen1 = @import("../gen1/data.zig");
|
|
8
|
-
|
|
9
|
-
const data = @import("data.zig");
|
|
6
|
+
const std = @import("std");
|
|
10
7
|
|
|
11
8
|
const assert = std.debug.assert;
|
|
12
|
-
|
|
13
9
|
const Choice = common.Choice;
|
|
14
|
-
|
|
15
|
-
const showdown = options.showdown;
|
|
16
|
-
|
|
17
|
-
const PSRNG = rng.PSRNG;
|
|
18
|
-
|
|
19
10
|
const DVs = data.DVs;
|
|
20
11
|
const Item = data.Item;
|
|
21
12
|
const Move = data.Move;
|
|
22
13
|
const MoveSlot = data.MoveSlot;
|
|
14
|
+
const PSRNG = rng.PSRNG;
|
|
15
|
+
const showdown = options.showdown;
|
|
23
16
|
const Species = data.Species;
|
|
24
17
|
const Stats = data.Stats;
|
|
25
18
|
|
|
26
|
-
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
27
|
-
|
|
28
19
|
pub const Options = struct {
|
|
29
20
|
cleric: bool = showdown,
|
|
30
21
|
};
|
|
@@ -81,7 +72,7 @@ pub fn prng(rand: *PSRNG) data.PRNG {
|
|
|
81
72
|
var carry: u8 = 0;
|
|
82
73
|
var div: u8 = 0;
|
|
83
74
|
|
|
84
|
-
var i:
|
|
75
|
+
var i: usize = 0;
|
|
85
76
|
while (i < seed.len) {
|
|
86
77
|
div = @intCast(timer >> 8);
|
|
87
78
|
// This is effectively @addWithOverflow but with three operands
|
|
@@ -149,7 +140,7 @@ pub const Pokemon = struct {
|
|
|
149
140
|
var pokemon = data.Pokemon{};
|
|
150
141
|
pokemon.species = p.species;
|
|
151
142
|
const species = Species.get(p.species);
|
|
152
|
-
inline for (@
|
|
143
|
+
inline for (@typeInfo(@TypeOf(pokemon.stats)).@"struct".fields) |field| {
|
|
153
144
|
const hp = comptime std.mem.eql(u8, field.name, "hp");
|
|
154
145
|
const spc =
|
|
155
146
|
comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
@@ -190,7 +181,7 @@ pub const Pokemon = struct {
|
|
|
190
181
|
|
|
191
182
|
var stats: Stats(u16) = .{};
|
|
192
183
|
const dvs = gen1.DVs.random(rand);
|
|
193
|
-
inline for (@
|
|
184
|
+
inline for (@typeInfo(@TypeOf(stats)).@"struct".fields) |field| {
|
|
194
185
|
const hp = std.mem.eql(u8, field.name, "hp");
|
|
195
186
|
const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
196
187
|
@field(stats, field.name) = Stats(u16).calc(
|
|
@@ -202,7 +193,7 @@ pub const Pokemon = struct {
|
|
|
202
193
|
);
|
|
203
194
|
}
|
|
204
195
|
|
|
205
|
-
var ms
|
|
196
|
+
var ms: [4]MoveSlot = @splat(.{});
|
|
206
197
|
const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
|
|
207
198
|
for (0..n) |i| {
|
|
208
199
|
var m: Move = .None;
|
|
@@ -1,44 +1,37 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("../pkmn.zig");
|
|
4
|
-
|
|
5
|
-
const common = @import("../common/data.zig");
|
|
6
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
7
|
-
const protocol = @import("../common/protocol.zig");
|
|
8
|
-
const rng = @import("../common/rng.zig");
|
|
9
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const common = @import("../common/data.zig");
|
|
10
5
|
const data = @import("data.zig");
|
|
11
6
|
const generated = @import("generated.zig");
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
17
|
-
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
18
|
-
|
|
19
|
-
const showdown = pkmn.options.showdown;
|
|
20
|
-
|
|
21
|
-
const Choice = common.Choice;
|
|
22
|
-
const ID = common.ID;
|
|
23
|
-
const Player = common.Player;
|
|
24
|
-
const Result = common.Result;
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
8
|
+
const protocol = @import("../common/protocol.zig");
|
|
9
|
+
const rng = @import("../common/rng.zig");
|
|
10
|
+
const std = @import("std");
|
|
25
11
|
|
|
26
12
|
const Activate = protocol.Activate;
|
|
13
|
+
const ActivePokemon = data.ActivePokemon;
|
|
14
|
+
const assert = std.debug.assert;
|
|
27
15
|
const Boost = protocol.Boost;
|
|
16
|
+
const Boosts = data.Boosts;
|
|
17
|
+
const Choice = common.Choice;
|
|
28
18
|
const Damage = protocol.Damage;
|
|
19
|
+
const Effectiveness = data.Effectiveness;
|
|
29
20
|
const End = protocol.End;
|
|
21
|
+
const expectEqual = std.testing.expectEqual;
|
|
22
|
+
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
23
|
+
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
30
24
|
const Fail = protocol.Fail;
|
|
31
|
-
const Heal = protocol.Heal;
|
|
32
|
-
const Start = protocol.Start;
|
|
33
|
-
|
|
34
25
|
const Gen12 = rng.Gen12;
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const Boosts = data.Boosts;
|
|
38
|
-
const Effectiveness = data.Effectiveness;
|
|
26
|
+
const Heal = protocol.Heal;
|
|
27
|
+
const ID = common.ID;
|
|
39
28
|
const Item = data.Item;
|
|
40
29
|
const Move = data.Move;
|
|
30
|
+
const Player = common.Player;
|
|
31
|
+
const Result = common.Result;
|
|
32
|
+
const showdown = pkmn.options.showdown;
|
|
41
33
|
const Side = data.Side;
|
|
34
|
+
const Start = protocol.Start;
|
|
42
35
|
const Stats = data.Stats;
|
|
43
36
|
const Status = data.Status;
|
|
44
37
|
const TriAttack = data.TriAttack;
|
|
@@ -137,11 +130,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
137
130
|
|
|
138
131
|
const p1_slot = findFirstAlive(p1);
|
|
139
132
|
assert(!showdown or p1_slot == 1);
|
|
140
|
-
if (p1_slot == 0) return if (findFirstAlive(p2) == 0)
|
|
133
|
+
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) .Tie else .Lose;
|
|
141
134
|
|
|
142
135
|
const p2_slot = findFirstAlive(p2);
|
|
143
136
|
assert(!showdown or p2_slot == 1);
|
|
144
|
-
if (p2_slot == 0) return
|
|
137
|
+
if (p2_slot == 0) return .Win;
|
|
145
138
|
|
|
146
139
|
try switchIn(battle, .P1, p1_slot, .Initial, options);
|
|
147
140
|
try switchIn(battle, .P2, p2_slot, .Initial, options);
|
|
@@ -689,7 +682,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
689
682
|
|
|
690
683
|
// Type effectiveness matchup precedence only matters with (NVE, SE)
|
|
691
684
|
if (!showdown and (eff1 + eff2) == Effectiveness.mismatch and
|
|
692
|
-
types.type1.precedence(
|
|
685
|
+
Type.precedence(state.type, types.type1) > Type.precedence(state.type, types.type2))
|
|
693
686
|
{
|
|
694
687
|
assert(eff2 != neutral);
|
|
695
688
|
d = d *% eff2 / 10;
|
|
@@ -826,13 +819,13 @@ fn checkFaint(
|
|
|
826
819
|
|
|
827
820
|
if (player_out and foe_out) {
|
|
828
821
|
try options.log.tie(.{});
|
|
829
|
-
return
|
|
822
|
+
return .Tie;
|
|
830
823
|
} else if (player_out) {
|
|
831
824
|
try options.log.win(.{player.foe()});
|
|
832
|
-
return if (player == .P1)
|
|
825
|
+
return if (player == .P1) .Lose else .Win;
|
|
833
826
|
} else if (foe_out) {
|
|
834
827
|
try options.log.win(.{player});
|
|
835
|
-
return if (player == .P1)
|
|
828
|
+
return if (player == .P1) .Win else .Lose;
|
|
836
829
|
}
|
|
837
830
|
|
|
838
831
|
const foe_choice: Choice.Type = if (foe_fainted) .Switch else .Pass;
|
|
@@ -1200,16 +1193,16 @@ fn updateSideCondition(
|
|
|
1200
1193
|
fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
|
|
1201
1194
|
battle.turn += 1;
|
|
1202
1195
|
|
|
1203
|
-
if (
|
|
1196
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1204
1197
|
try options.log.tie(.{});
|
|
1205
|
-
return
|
|
1198
|
+
return .Tie;
|
|
1206
1199
|
} else if (battle.turn >= 65535) {
|
|
1207
|
-
return
|
|
1200
|
+
return .Error;
|
|
1208
1201
|
}
|
|
1209
1202
|
|
|
1210
1203
|
try options.log.turn(.{battle.turn});
|
|
1211
1204
|
|
|
1212
|
-
return
|
|
1205
|
+
return .Default;
|
|
1213
1206
|
}
|
|
1214
1207
|
|
|
1215
1208
|
pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1434,7 +1427,8 @@ pub const Effects = struct {
|
|
|
1434
1427
|
if (move.type.special() != (last == .MirrorCoat)) return;
|
|
1435
1428
|
if (state.damage == 0) return;
|
|
1436
1429
|
|
|
1437
|
-
|
|
1430
|
+
// NOTE: llvm/llvm-project#58557
|
|
1431
|
+
state.damage = smul(state.damage, 2);
|
|
1438
1432
|
state.miss = false;
|
|
1439
1433
|
}
|
|
1440
1434
|
|
|
@@ -1529,15 +1523,18 @@ pub const Effects = struct {
|
|
|
1529
1523
|
|
|
1530
1524
|
pub const double = struct {
|
|
1531
1525
|
pub fn flying(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1532
|
-
if (battle.foe(player).active.volatiles.Flying)
|
|
1526
|
+
if (!battle.foe(player).active.volatiles.Flying) return;
|
|
1527
|
+
state.damage = smul(state.damage, 2);
|
|
1533
1528
|
}
|
|
1534
1529
|
|
|
1535
1530
|
pub fn underground(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1536
|
-
if (battle.foe(player).active.volatiles.Underground)
|
|
1531
|
+
if (!battle.foe(player).active.volatiles.Underground) return;
|
|
1532
|
+
state.damage = smul(state.damage, 2);
|
|
1537
1533
|
}
|
|
1538
1534
|
|
|
1539
1535
|
pub fn minimize(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1540
|
-
if (battle.foe(player).active.volatiles.minimized)
|
|
1536
|
+
if (!battle.foe(player).active.volatiles.minimized) return;
|
|
1537
|
+
state.damage = smul(state.damage, 2);
|
|
1541
1538
|
}
|
|
1542
1539
|
};
|
|
1543
1540
|
|
|
@@ -1561,7 +1558,7 @@ pub const Effects = struct {
|
|
|
1561
1558
|
const last = battle.foe(player).lastMove(true);
|
|
1562
1559
|
const failed = volatiles.Encore or
|
|
1563
1560
|
(last == .None or last == .Struggle or
|
|
1564
|
-
|
|
1561
|
+
last == .Encore or .last == .MirrorMove);
|
|
1565
1562
|
if (failed) return options.log.fail(.{ foe_ident, .None });
|
|
1566
1563
|
|
|
1567
1564
|
var slot: u3 = 0;
|
|
@@ -1706,7 +1703,7 @@ pub const Effects = struct {
|
|
|
1706
1703
|
if (!state.proc) return;
|
|
1707
1704
|
if (foe.conditions.Safeguard) return;
|
|
1708
1705
|
// Freeze Clause Mod
|
|
1709
|
-
if (
|
|
1706
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1710
1707
|
|
|
1711
1708
|
foe_stored.status = Status.init(.FRZ);
|
|
1712
1709
|
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
@@ -1735,7 +1732,7 @@ pub const Effects = struct {
|
|
|
1735
1732
|
state.bp *= 2;
|
|
1736
1733
|
assert(state.bp <= 160);
|
|
1737
1734
|
} else {
|
|
1738
|
-
state.damage
|
|
1735
|
+
state.damage = smul(state.damage, 2);
|
|
1739
1736
|
}
|
|
1740
1737
|
}
|
|
1741
1738
|
|
|
@@ -2206,7 +2203,7 @@ pub const Effects = struct {
|
|
|
2206
2203
|
|
|
2207
2204
|
pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2208
2205
|
// TODO: remove switching variable and just look at Choice saved in Battle struct?
|
|
2209
|
-
if (battle.foe(player).active.volatiles.switching) state.damage
|
|
2206
|
+
if (battle.foe(player).active.volatiles.switching) state.damage = smul(state.damage, 2);
|
|
2210
2207
|
}
|
|
2211
2208
|
|
|
2212
2209
|
pub const rage = struct {
|
|
@@ -2219,7 +2216,7 @@ pub const Effects = struct {
|
|
|
2219
2216
|
const side = battle.side(player);
|
|
2220
2217
|
|
|
2221
2218
|
assert(side.active.volatiles.Rage);
|
|
2222
|
-
state.damage
|
|
2219
|
+
state.damage = smul(state.damage, (side.active.volatiles.rage +| 1)); // TODO: |+
|
|
2223
2220
|
}
|
|
2224
2221
|
};
|
|
2225
2222
|
|
|
@@ -2314,7 +2311,7 @@ pub const Effects = struct {
|
|
|
2314
2311
|
side.active.volatiles.attacks += 1;
|
|
2315
2312
|
side.active.volatiles.Rollout = side.active.volatiles.attacks != 5;
|
|
2316
2313
|
|
|
2317
|
-
if (side.active.volatiles.DefenseCurl) state.damage
|
|
2314
|
+
if (side.active.volatiles.DefenseCurl) state.damage = smul(state.damage, 2);
|
|
2318
2315
|
}
|
|
2319
2316
|
};
|
|
2320
2317
|
|
|
@@ -2384,7 +2381,7 @@ pub const Effects = struct {
|
|
|
2384
2381
|
}
|
|
2385
2382
|
|
|
2386
2383
|
// Sleep Clause Mod
|
|
2387
|
-
if (
|
|
2384
|
+
if (pkmn.options.mod) {
|
|
2388
2385
|
for (foe.pokemon) |p| {
|
|
2389
2386
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2390
2387
|
}
|
|
@@ -2842,6 +2839,12 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2842
2839
|
return t != .@"???" and !active.types.includes(t);
|
|
2843
2840
|
}
|
|
2844
2841
|
|
|
2842
|
+
// NOTE: llvm/llvm-project#58557
|
|
2843
|
+
fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
|
2844
|
+
if (!comptime builtin.target.cpu.arch.isWasm()) return a *| b;
|
|
2845
|
+
return @max(a *% b, std.math.maxInt(@TypeOf(a)));
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2845
2848
|
pub const Rolls = struct {
|
|
2846
2849
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2847
2850
|
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
@@ -3078,7 +3081,7 @@ pub const Rolls = struct {
|
|
|
3078
3081
|
}
|
|
3079
3082
|
|
|
3080
3083
|
// Conversion 2 can at most choose between 7 types (vs. a Grass-type attack)
|
|
3081
|
-
var CONVERSION_2: [7]Type =
|
|
3084
|
+
var CONVERSION_2: [7]Type = @splat(.@"???");
|
|
3082
3085
|
|
|
3083
3086
|
fn conversion2(battle: anytype, player: Player, mtype: Type, options: anytype) !Type {
|
|
3084
3087
|
var i: u8 = 0;
|
|
@@ -3503,11 +3506,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3503
3506
|
},
|
|
3504
3507
|
.Switch => {
|
|
3505
3508
|
const side = battle.side(player);
|
|
3506
|
-
var slot:
|
|
3509
|
+
var slot: usize = 2;
|
|
3507
3510
|
while (slot <= 6) : (slot += 1) {
|
|
3508
3511
|
const id = side.order[slot - 1];
|
|
3509
3512
|
if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
|
|
3510
|
-
out[n] = .{ .type = .Switch, .data = slot };
|
|
3513
|
+
out[n] = .{ .type = .Switch, .data = @intCast(slot) };
|
|
3511
3514
|
n += 1;
|
|
3512
3515
|
}
|
|
3513
3516
|
if (n == 0) {
|
|
@@ -3537,11 +3540,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3537
3540
|
return n;
|
|
3538
3541
|
}
|
|
3539
3542
|
|
|
3540
|
-
var slot:
|
|
3543
|
+
var slot: usize = 2;
|
|
3541
3544
|
while (!active.volatiles.Trapped and slot <= 6) : (slot += 1) {
|
|
3542
3545
|
const id = side.order[slot - 1];
|
|
3543
3546
|
if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
|
|
3544
|
-
out[n] = .{ .type = .Switch, .data = slot };
|
|
3547
|
+
out[n] = .{ .type = .Switch, .data = @intCast(slot) };
|
|
3545
3548
|
n += 1;
|
|
3546
3549
|
}
|
|
3547
3550
|
|
|
@@ -3574,7 +3577,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3574
3577
|
const struggle =
|
|
3575
3578
|
m.id == .Bide and (m.pp == 0 or active.volatiles.disable.move == slot);
|
|
3576
3579
|
const s = if (struggle) 0 else slot;
|
|
3577
|
-
out[n] = .{ .type = .Move, .data = s };
|
|
3580
|
+
out[n] = .{ .type = .Move, .data = @intCast(s) };
|
|
3578
3581
|
n += 1;
|
|
3579
3582
|
return n;
|
|
3580
3583
|
}
|
|
@@ -3588,7 +3591,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3588
3591
|
if (m.id == .None) break;
|
|
3589
3592
|
if (m.pp == 0) continue;
|
|
3590
3593
|
if (active.volatiles.disable.move == slot) continue;
|
|
3591
|
-
out[n] = .{ .type = .Move, .data = slot };
|
|
3594
|
+
out[n] = .{ .type = .Move, .data = @intCast(slot) };
|
|
3592
3595
|
n += 1;
|
|
3593
3596
|
}
|
|
3594
3597
|
// Struggle (Pokémon Showdown would use 'move 1' here)
|
package/src/lib/napi.h
ADDED
package/src/lib/node.zig
CHANGED
|
@@ -1,131 +1,14 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("pkmn.zig");
|
|
4
|
-
|
|
1
|
+
const c = @import("napi");
|
|
5
2
|
const js = @import("common/js.zig");
|
|
3
|
+
const node = @import("bindings/node.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
7
|
|
|
9
|
-
const c = @cImport({
|
|
10
|
-
@cDefine("NAPI_VERSION", "8");
|
|
11
|
-
@cInclude("node_api.h");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
8
|
export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
|
|
15
9
|
const properties = [_]c.napi_property_descriptor{
|
|
16
|
-
js.Property.init("engine", .{ .value = register(env) }),
|
|
10
|
+
js.Property.init("engine", .{ .value = node.register(env) }),
|
|
17
11
|
};
|
|
18
12
|
assert(c.napi_define_properties(env, exports, properties.len, &properties) == c.napi_ok);
|
|
19
13
|
return exports;
|
|
20
14
|
}
|
|
21
|
-
|
|
22
|
-
pub fn register(env: c.napi_env) c.napi_value {
|
|
23
|
-
const object = js.Object.init(env);
|
|
24
|
-
const properties = [_]c.napi_property_descriptor{
|
|
25
|
-
js.Property.init("options", .{ .value = options(env) }),
|
|
26
|
-
js.Property.init("bindings", .{ .value = bindings(env) }),
|
|
27
|
-
};
|
|
28
|
-
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
29
|
-
return object;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
fn options(env: c.napi_env) c.napi_value {
|
|
33
|
-
const object = js.Object.init(env);
|
|
34
|
-
const properties = [_]c.napi_property_descriptor{
|
|
35
|
-
js.Property.init("showdown", .{ .value = js.Boolean.init(env, pkmn.options.showdown) }),
|
|
36
|
-
js.Property.init("log", .{ .value = js.Boolean.init(env, pkmn.options.log) }),
|
|
37
|
-
js.Property.init("chance", .{ .value = js.Boolean.init(env, pkmn.options.chance) }),
|
|
38
|
-
js.Property.init("calc", .{ .value = js.Boolean.init(env, pkmn.options.calc) }),
|
|
39
|
-
};
|
|
40
|
-
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
41
|
-
return object;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
fn bindings(env: c.napi_env) c.napi_value {
|
|
45
|
-
const array = js.Array.init(env, .{ .length = 1 });
|
|
46
|
-
js.Array.set(env, array, 0, bind(env, pkmn.gen1));
|
|
47
|
-
return array;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
fn bind(env: c.napi_env, gen: anytype) c.napi_value {
|
|
51
|
-
const choices_size: u32 = @intCast(gen.CHOICES_SIZE);
|
|
52
|
-
const logs_size: u32 = @intCast(gen.LOGS_SIZE);
|
|
53
|
-
const object = js.Object.init(env);
|
|
54
|
-
const properties = [_]c.napi_property_descriptor{
|
|
55
|
-
js.Property.init("CHOICES_SIZE", .{ .value = js.Number.init(env, choices_size) }),
|
|
56
|
-
js.Property.init("LOGS_SIZE", .{ .value = js.Number.init(env, logs_size) }),
|
|
57
|
-
js.Property.init("update", .{ .method = update(gen) }),
|
|
58
|
-
js.Property.init("choices", .{ .method = choices(gen) }),
|
|
59
|
-
};
|
|
60
|
-
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
61
|
-
return object;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
fn update(gen: anytype) c.napi_callback {
|
|
65
|
-
return struct {
|
|
66
|
-
fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
|
|
67
|
-
var argc: usize = 4;
|
|
68
|
-
var argv: [4]c.napi_value = undefined;
|
|
69
|
-
assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
|
|
70
|
-
assert(argc == 4);
|
|
71
|
-
|
|
72
|
-
var data: ?*anyopaque = undefined;
|
|
73
|
-
var len: usize = 0;
|
|
74
|
-
assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
|
|
75
|
-
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
76
|
-
|
|
77
|
-
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
78
|
-
const c1: pkmn.Choice = @bitCast(js.Number.get(env, argv[1], u8));
|
|
79
|
-
const c2: pkmn.Choice = @bitCast(js.Number.get(env, argv[2], u8));
|
|
80
|
-
|
|
81
|
-
var vtype: c.napi_valuetype = undefined;
|
|
82
|
-
assert(c.napi_typeof(env, argv[3], &vtype) == c.napi_ok);
|
|
83
|
-
const result = switch (vtype) {
|
|
84
|
-
c.napi_undefined, c.napi_null => battle.update(c1, c2, &gen.NULL),
|
|
85
|
-
else => result: {
|
|
86
|
-
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
87
|
-
assert(len == gen.LOGS_SIZE);
|
|
88
|
-
|
|
89
|
-
const buf = @as([*]u8, @ptrCast(data.?))[0..gen.LOGS_SIZE];
|
|
90
|
-
var stream: pkmn.protocol.ByteStream = .{ .buffer = buf };
|
|
91
|
-
// TODO: extract out
|
|
92
|
-
var opts = pkmn.battle.options(
|
|
93
|
-
pkmn.protocol.FixedLog{ .writer = stream.writer() },
|
|
94
|
-
gen.chance.NULL,
|
|
95
|
-
gen.calc.NULL,
|
|
96
|
-
);
|
|
97
|
-
break :result battle.update(c1, c2, &opts);
|
|
98
|
-
},
|
|
99
|
-
} catch unreachable;
|
|
100
|
-
|
|
101
|
-
return js.Number.init(env, @as(u8, @bitCast(result)));
|
|
102
|
-
}
|
|
103
|
-
}.call;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
fn choices(gen: anytype) c.napi_callback {
|
|
107
|
-
return struct {
|
|
108
|
-
fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
|
|
109
|
-
var argc: usize = 4;
|
|
110
|
-
var argv: [4]c.napi_value = undefined;
|
|
111
|
-
assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
|
|
112
|
-
assert(argc == 4);
|
|
113
|
-
|
|
114
|
-
var data: ?*anyopaque = undefined;
|
|
115
|
-
var len: usize = 0;
|
|
116
|
-
assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
|
|
117
|
-
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
118
|
-
|
|
119
|
-
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
120
|
-
const player: pkmn.Player = @enumFromInt(js.Number.get(env, argv[1], u8));
|
|
121
|
-
const request: pkmn.Choice.Type = @enumFromInt(js.Number.get(env, argv[2], u8));
|
|
122
|
-
|
|
123
|
-
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
124
|
-
assert(len == gen.CHOICES_SIZE);
|
|
125
|
-
|
|
126
|
-
const out = @as([*]pkmn.Choice, @ptrCast(data.?))[0..gen.CHOICES_SIZE];
|
|
127
|
-
const n = battle.choices(player, request, out);
|
|
128
|
-
return js.Number.init(env, @as(u8, @bitCast(n)));
|
|
129
|
-
}
|
|
130
|
-
}.call;
|
|
131
|
-
}
|