@pkmn/engine 0.1.0-dev.f3aab252 → 0.1.0-dev.f3b63b86
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 +35 -8
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +47 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +110 -0
- package/build/pkg/addon/node.js.map +1 -0
- 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 +26 -4
- package/build/pkg/addon.js +34 -65
- 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.d.ts +23 -1
- 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 +39 -89
- package/build/pkg/index.js +23 -70
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +105 -103
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +68 -74
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +152 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +359 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +33 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +235 -213
- package/package.json +59 -141
- package/src/bin/install-pkmn-engine +25 -9
- package/src/bin/pkmn-debug +1 -1
- 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 +30 -9
- package/src/lib/common/js.zig +3 -9
- package/src/lib/common/optional.zig +11 -21
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +180 -171
- package/src/lib/common/rational.zig +278 -285
- package/src/lib/common/rng.zig +1 -3
- package/src/lib/common/util.zig +14 -31
- package/src/lib/gen1/calc.zig +258 -106
- package/src/lib/gen1/chance.zig +57 -54
- 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 +13 -28
- package/src/lib/gen1/mechanics.zig +131 -101
- 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 +50 -47
- 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 +93 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -405
- package/build/test/display.js.map +0 -1
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen2/chance.zig
CHANGED
|
@@ -27,10 +27,6 @@ const Type = data.Type;
|
|
|
27
27
|
const Effectiveness = data.Effectiveness;
|
|
28
28
|
const TriAttack = data.TriAttack;
|
|
29
29
|
|
|
30
|
-
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
31
|
-
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
32
|
-
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
33
|
-
|
|
34
30
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
35
31
|
/// were observed during a Generation II battle `update`. This can additionally be provided as input
|
|
36
32
|
/// to the `update` call to override the normal behavior of the RNG in order to force specific
|
|
@@ -57,15 +53,15 @@ pub const Actions = extern struct {
|
|
|
57
53
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
58
54
|
/// set to the same value).
|
|
59
55
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
60
|
-
inline for (@
|
|
61
|
-
inline for (@
|
|
56
|
+
inline for (@typeInfo(Actions).@"struct".fields) |player| {
|
|
57
|
+
inline for (@typeInfo(Action).@"struct".fields) |field| {
|
|
62
58
|
const a_val = @field(@field(a, player.name), field.name);
|
|
63
59
|
const b_val = @field(@field(b, player.name), field.name);
|
|
64
60
|
|
|
65
61
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
66
|
-
|
|
62
|
+
.@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
67
63
|
return false,
|
|
68
|
-
|
|
64
|
+
.int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
69
65
|
else => unreachable,
|
|
70
66
|
}
|
|
71
67
|
}
|
|
@@ -73,7 +69,7 @@ pub const Actions = extern struct {
|
|
|
73
69
|
return true;
|
|
74
70
|
}
|
|
75
71
|
|
|
76
|
-
pub fn fmt(self: Actions, writer:
|
|
72
|
+
pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
|
|
77
73
|
try writer.writeAll("<P1 = ");
|
|
78
74
|
try self.p1.fmt(writer, shape);
|
|
79
75
|
try writer.writeAll(", P2 = ");
|
|
@@ -81,8 +77,7 @@ pub const Actions = extern struct {
|
|
|
81
77
|
try writer.writeAll(">");
|
|
82
78
|
}
|
|
83
79
|
|
|
84
|
-
pub fn format(a: Actions,
|
|
85
|
-
_ = .{ f, o };
|
|
80
|
+
pub fn format(a: Actions, w: *std.Io.Writer) !void {
|
|
86
81
|
try fmt(a, w, false);
|
|
87
82
|
}
|
|
88
83
|
};
|
|
@@ -186,18 +181,17 @@ pub const Action = packed struct(u128) {
|
|
|
186
181
|
|
|
187
182
|
pub const Field = std.meta.FieldEnum(Action);
|
|
188
183
|
|
|
189
|
-
pub fn format(a: Action,
|
|
190
|
-
_ = .{ f, o };
|
|
184
|
+
pub fn format(a: Action, w: *std.Io.Writer) !void {
|
|
191
185
|
try fmt(a, w, false);
|
|
192
186
|
}
|
|
193
187
|
|
|
194
|
-
pub fn fmt(self: Action, writer:
|
|
188
|
+
pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
|
|
195
189
|
try writer.writeByte('(');
|
|
196
190
|
var printed = false;
|
|
197
|
-
inline for (@
|
|
191
|
+
inline for (@typeInfo(Action).@"struct".fields) |field| {
|
|
198
192
|
const val = @field(self, field.name);
|
|
199
193
|
switch (@typeInfo(@TypeOf(val))) {
|
|
200
|
-
|
|
194
|
+
.@"enum" => if (val != .None) {
|
|
201
195
|
if (printed) try writer.writeAll(", ");
|
|
202
196
|
if (shape) {
|
|
203
197
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -211,7 +205,7 @@ pub const Action = packed struct(u128) {
|
|
|
211
205
|
}
|
|
212
206
|
printed = true;
|
|
213
207
|
},
|
|
214
|
-
|
|
208
|
+
.int => if (val != 0) {
|
|
215
209
|
if (printed) try writer.writeAll(", ");
|
|
216
210
|
if (shape) {
|
|
217
211
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -786,7 +780,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
|
786
780
|
|
|
787
781
|
r.reduce();
|
|
788
782
|
if (r.p != p or r.q != q) {
|
|
789
|
-
print("expected {d}/{d}, found {}\n", .{ p, q, r });
|
|
783
|
+
print("expected {d}/{d}, found {f}\n", .{ p, q, r });
|
|
790
784
|
return error.TestExpectedEqual;
|
|
791
785
|
}
|
|
792
786
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const data = @import("../../gen2/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Type = data.Type;
|
|
10
8
|
|
|
11
9
|
/// Representation of a held item in Generation II Pokémon.
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
|
-
const rng = @import("../../common/rng.zig");
|
|
6
3
|
const gen2 = @import("../../gen2/data.zig");
|
|
4
|
+
const rng = @import("../../common/rng.zig");
|
|
5
|
+
const std = @import("std");
|
|
7
6
|
|
|
8
7
|
const assert = std.debug.assert;
|
|
9
|
-
|
|
10
8
|
const percent = rng.Gen12.percent;
|
|
11
9
|
const Type = gen2.Type;
|
|
12
10
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen2 = @import("../../gen2/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Stats = gen2.Stats;
|
|
10
8
|
const Types = gen2.Types;
|
|
11
9
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
7
|
const Effectiveness = gen1.Effectiveness;
|
|
@@ -173,9 +172,12 @@ pub const Type = enum(u8) {
|
|
|
173
172
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
174
173
|
}
|
|
175
174
|
|
|
176
|
-
/// The precedence order of Type `
|
|
177
|
-
pub fn precedence(
|
|
178
|
-
return
|
|
175
|
+
/// The precedence order of Type `t1` vs. Type `t2`.
|
|
176
|
+
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
177
|
+
// The Ice vs. Fire matchup is out of order - return a higher
|
|
178
|
+
// number than anything else so that it sorts last correctly
|
|
179
|
+
if (t1 == .Ice and t2 == .Fire) return 18;
|
|
180
|
+
return PRECEDENCE[@intFromEnum(t2)];
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
/// The internal index of this Type used by Present.
|
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;
|
|
@@ -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;
|
|
@@ -1200,7 +1193,7 @@ 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
1198
|
return Result.Tie;
|
|
1206
1199
|
} else if (battle.turn >= 65535) {
|
|
@@ -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