@pkmn/engine 0.1.0-dev.b3197aa3 → 0.1.0-dev.b396399c

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.
Files changed (47) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +17 -53
  3. package/build/pkg/addon/wasm.js.map +1 -1
  4. package/build/pkg/addon.d.ts +4 -4
  5. package/build/pkg/addon.js.map +1 -1
  6. package/build/pkg/data.js.map +1 -1
  7. package/build/pkg/gen1.js.map +1 -1
  8. package/build/pkg/protocol.d.ts +11 -2
  9. package/build/pkg/protocol.js.map +1 -1
  10. package/build/tools/debug.js +37 -3
  11. package/build/tools/debug.js.map +1 -1
  12. package/build/tools/display/debug.jsx +1 -87
  13. package/build/tools/display/debug.jsx.map +1 -1
  14. package/build/tools/display/ui.jsx +3 -2
  15. package/build/tools/display/ui.jsx.map +1 -1
  16. package/build/tools/display/util.js +125 -1
  17. package/build/tools/display/util.js.map +1 -1
  18. package/build.zig +141 -232
  19. package/package.json +22 -21
  20. package/src/bin/install-pkmn-engine +22 -7
  21. package/src/lib/bindings/c.zig +27 -82
  22. package/src/lib/bindings/node.zig +7 -7
  23. package/src/lib/bindings/wasm.zig +6 -15
  24. package/src/lib/common/array.zig +5 -12
  25. package/src/lib/common/debug.zig +33 -59
  26. package/src/lib/common/js.zig +3 -5
  27. package/src/lib/common/optional.zig +11 -21
  28. package/src/lib/common/protocol.zig +224 -189
  29. package/src/lib/common/rational.zig +18 -38
  30. package/src/lib/common/util.zig +14 -46
  31. package/src/lib/gen1/calc.zig +121 -108
  32. package/src/lib/gen1/chance.zig +112 -75
  33. package/src/lib/gen1/data/types.zig +1 -1
  34. package/src/lib/gen1/data.zig +5 -5
  35. package/src/lib/gen1/helpers.zig +8 -10
  36. package/src/lib/gen1/mechanics.zig +31 -31
  37. package/src/lib/gen2/calc.zig +3 -6
  38. package/src/lib/gen2/chance.zig +12 -18
  39. package/src/lib/gen2/data.zig +5 -5
  40. package/src/lib/gen2/helpers.zig +4 -6
  41. package/src/lib/gen2/mechanics.zig +16 -16
  42. package/src/lib/napi.h +2 -0
  43. package/src/lib/node.zig +1 -1
  44. package/src/lib/pkmn.zig +91 -17
  45. package/src/lib/wasm.zig +2 -6
  46. package/src/tools/display/dom.ts +40 -25
  47. package/src/lib/common/napi.zig +0 -4
@@ -84,11 +84,11 @@ fn start(battle: anytype, options: anytype) !Result {
84
84
 
85
85
  const p1_slot = findFirstAlive(p1);
86
86
  assert(!showdown or p1_slot == 1);
87
- if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
87
+ if (p1_slot == 0) return if (findFirstAlive(p2) == 0) .Tie else .Lose;
88
88
 
89
89
  const p2_slot = findFirstAlive(p2);
90
90
  assert(!showdown or p2_slot == 1);
91
- if (p2_slot == 0) return Result.Win;
91
+ if (p2_slot == 0) return .Win;
92
92
 
93
93
  try switchIn(battle, .P1, p1_slot, true, options);
94
94
  try switchIn(battle, .P2, p2_slot, true, options);
@@ -167,7 +167,7 @@ fn selectMove(
167
167
  const last = side.active.move(battle.lastMove(player).index);
168
168
  if (last.id == .Metronome) side.last_selected_move = last.id;
169
169
  if (last.id == .MirrorMove) {
170
- if (!pkmn.options.mod) return Result.Error;
170
+ if (!pkmn.options.mod) return .Error;
171
171
  side.last_selected_move = last.id;
172
172
  }
173
173
  }
@@ -432,7 +432,7 @@ fn executeMove(
432
432
  // technically be from this Pokémon because it must be exactly 1 to not desync and
433
433
  // every Pokémon must have at least one move
434
434
  if (mslot != 1 or side.active.move(mslot).id != side.last_selected_move) {
435
- return Result.Error;
435
+ return .Error;
436
436
  } else {
437
437
  auto = true;
438
438
  }
@@ -450,7 +450,7 @@ fn executeMove(
450
450
  .skip_can => skip_can = true,
451
451
  .skip_pp => skip_pp = true,
452
452
  .ok => {},
453
- .err => return @as(?Result, Result.Error),
453
+ .err => return .Error,
454
454
  }
455
455
 
456
456
  // Pokémon Showdown incorrectly implements PP deduction when handling the Hyper Beam automatic
@@ -878,7 +878,7 @@ fn doMove(
878
878
  var miss = showdown and miss: {
879
879
  immune = move.target != .Self and !status and !counter and
880
880
  (@intFromEnum(move.type.effectiveness(foe.active.types.type1)) == 0 or
881
- @intFromEnum(move.type.effectiveness(foe.active.types.type2)) == 0);
881
+ @intFromEnum(move.type.effectiveness(foe.active.types.type2)) == 0);
882
882
  if (immune and move.effect != .Binding) break :miss true;
883
883
  if (move.effect == .OHKO and side.active.stats.spe < foe.active.stats.spe) {
884
884
  battle.last_damage = 0;
@@ -916,7 +916,7 @@ fn doMove(
916
916
  if (showdown) break :damage; // skip adjustDamage / randomizeDamage
917
917
  } else if (!calcDamage(battle, player, player.foe(), move, crit, options)) {
918
918
  if (!showdown) try options.chance.commit(player, .err);
919
- return @as(?Result, Result.Error);
919
+ return .Error;
920
920
  }
921
921
  if (battle.last_damage == 0) {
922
922
  immune = true;
@@ -1019,7 +1019,7 @@ fn doMove(
1019
1019
  const sub = showdown and foe.active.volatiles.Substitute;
1020
1020
 
1021
1021
  var nullified = false;
1022
- var hit: u4 = 0;
1022
+ var hit: usize = 0;
1023
1023
  while (hit < hits) {
1024
1024
  if (hit == 0) {
1025
1025
  if (crit) try log.crit(.{battle.active(player.foe())});
@@ -1047,7 +1047,7 @@ fn doMove(
1047
1047
  if (showdown and move.effect == .Twineedle and !nullified and foe.stored().hp > 0) {
1048
1048
  try Effects.poison(battle, player, Move.get(.PoisonSting), options);
1049
1049
  }
1050
- try log.hitcount(.{ battle.active(player.foe()), hit });
1050
+ try log.hitcount(.{ battle.active(player.foe()), @as(u4, @intCast(hit)) });
1051
1051
  } else if (showdown and move.effect == .Binding) {
1052
1052
  // This should be handled much earlier but Pokémon Showdown does it here... ¯\_(ツ)_/¯
1053
1053
  try Effects.binding(battle, player, rewrap, options);
@@ -1248,13 +1248,13 @@ fn specialDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
1248
1248
  .Psywave => power: {
1249
1249
  const max: u8 = @intCast(@as(u16, side.stored().level) * 3 / 2);
1250
1250
  // GLITCH: Psywave infinite glitch loop
1251
- if (!pkmn.options.mod and max <= 1) return Result.Error;
1251
+ if (!pkmn.options.mod and max <= 1) return .Error;
1252
1252
  break :power try Rolls.psywave(battle, player, max, options);
1253
1253
  },
1254
1254
  else => unreachable,
1255
1255
  };
1256
1256
 
1257
- if (battle.last_damage == 0) return if (pkmn.options.mod) null else Result.Error;
1257
+ if (battle.last_damage == 0) return if (pkmn.options.mod) null else .Error;
1258
1258
 
1259
1259
  const sub = showdown and foe.active.volatiles.Substitute;
1260
1260
  _ = try applyDamage(battle, player.foe(), player.foe(), .None, options);
@@ -1274,15 +1274,15 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
1274
1274
  // Pretend Splash was used as a stand-in when no move has been used to fail below with 0 BP
1275
1275
  const foe_last_selected_move =
1276
1276
  Move.get(if (foe.last_selected_move == .None or foe.last_selected_move == .SKIP_TURN)
1277
- .Splash
1278
- else
1279
- foe.last_selected_move);
1277
+ .Splash
1278
+ else
1279
+ foe.last_selected_move);
1280
1280
 
1281
1281
  const used = battle.lastMove(player.foe()).counterable != 0;
1282
1282
  const selected = foe_last_selected_move.bp > 0 and
1283
1283
  foe.last_selected_move != .Counter and
1284
1284
  (foe_last_selected_move.type == .Normal or
1285
- foe_last_selected_move.type == .Fighting);
1285
+ foe_last_selected_move.type == .Fighting);
1286
1286
 
1287
1287
  if (!used and !selected) {
1288
1288
  try options.log.fail(.{ battle.active(player), .None });
@@ -1291,7 +1291,7 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
1291
1291
 
1292
1292
  if (!used or !selected) {
1293
1293
  // GLITCH: Counter desync (covered by Desync Clause Mod on Pokémon Showdown)
1294
- if (!pkmn.options.mod) return Result.Error;
1294
+ if (!pkmn.options.mod) return .Error;
1295
1295
  try options.log.fail(.{ battle.active(player), .None });
1296
1296
  return null;
1297
1297
  }
@@ -1528,13 +1528,13 @@ fn checkFaint(
1528
1528
 
1529
1529
  if (player_out and foe_out) {
1530
1530
  try options.log.tie(.{});
1531
- return Result.Tie;
1531
+ return .Tie;
1532
1532
  } else if (player_out) {
1533
1533
  try options.log.win(.{player.foe()});
1534
- return if (player == .P1) Result.Lose else Result.Win;
1534
+ return if (player == .P1) .Lose else .Win;
1535
1535
  } else if (foe_out) {
1536
1536
  try options.log.win(.{player});
1537
- return if (player == .P1) Result.Win else Result.Lose;
1537
+ return if (player == .P1) .Win else .Lose;
1538
1538
  }
1539
1539
 
1540
1540
  const foe_choice: Choice.Type = if (foe_fainted) .Switch else .Pass;
@@ -1552,7 +1552,7 @@ fn faint(battle: anytype, player: Player, done: bool, cap: bool, options: anytyp
1552
1552
  if (foe_volatiles.Bide) {
1553
1553
  assert(!foe_volatiles.Thrashing and !foe_volatiles.Rage);
1554
1554
  foe_volatiles.state = if (pkmn.options.mod) 0 else foe_volatiles.state & 255;
1555
- if (foe_volatiles.state != 0) return Result.Error;
1555
+ if (foe_volatiles.state != 0) return .Error;
1556
1556
  }
1557
1557
 
1558
1558
  // Clearing these is not strictly necessary as provided the battle hasn't ended the side that
@@ -1644,19 +1644,19 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
1644
1644
 
1645
1645
  if (pkmn.options.mod and pkmn.options.ebc and checkEBC(battle)) {
1646
1646
  try options.log.tie(.{});
1647
- return Result.Tie;
1647
+ return .Tie;
1648
1648
  }
1649
1649
 
1650
1650
  if (pkmn.options.mod and battle.turn >= 1000) {
1651
1651
  try options.log.tie(.{});
1652
- return Result.Tie;
1652
+ return .Tie;
1653
1653
  } else if (battle.turn >= 65535) {
1654
- return Result.Error;
1654
+ return .Error;
1655
1655
  }
1656
1656
 
1657
1657
  try options.log.turn(.{battle.turn});
1658
1658
 
1659
- return Result.Default;
1659
+ return .Default;
1660
1660
  }
1661
1661
 
1662
1662
  fn checkEBC(battle: anytype) bool {
@@ -1947,7 +1947,7 @@ pub const Effects = struct {
1947
1947
  return null;
1948
1948
  } else if (err) {
1949
1949
  // GLITCH: Transform + Mirror Move / Metronome PP softlock
1950
- return Result.Error;
1950
+ return .Error;
1951
1951
  }
1952
1952
 
1953
1953
  volatiles.disable_move =
@@ -3145,11 +3145,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3145
3145
  },
3146
3146
  .Switch => {
3147
3147
  const side = battle.side(player);
3148
- var slot: u4 = 2;
3148
+ var slot: usize = 2;
3149
3149
  while (slot <= 6) : (slot += 1) {
3150
3150
  const id = side.order[slot - 1];
3151
3151
  if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
3152
- out[n] = .{ .type = .Switch, .data = slot };
3152
+ out[n] = .{ .type = .Switch, .data = @intCast(slot) };
3153
3153
  n += 1;
3154
3154
  }
3155
3155
  if (n == 0) {
@@ -3180,11 +3180,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3180
3180
  return n;
3181
3181
  }
3182
3182
 
3183
- var slot: u4 = 2;
3183
+ var slot: usize = 2;
3184
3184
  while (slot <= 6) : (slot += 1) {
3185
3185
  const id = side.order[slot - 1];
3186
3186
  if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
3187
- out[n] = .{ .type = .Switch, .data = slot };
3187
+ out[n] = .{ .type = .Switch, .data = @intCast(slot) };
3188
3188
  n += 1;
3189
3189
  }
3190
3190
 
@@ -3217,7 +3217,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3217
3217
  const struggle =
3218
3218
  m.id == .Bide and (m.pp == 0 or active.volatiles.disable_move == slot);
3219
3219
  const s = if (struggle) 0 else slot;
3220
- out[n] = .{ .type = .Move, .data = s };
3220
+ out[n] = .{ .type = .Move, .data = @intCast(s) };
3221
3221
  n += 1;
3222
3222
  return n;
3223
3223
  }
@@ -3231,7 +3231,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3231
3231
  if (m.id == .None) break;
3232
3232
  if (m.pp == 0) continue;
3233
3233
  if (active.volatiles.disable_move == slot) continue;
3234
- out[n] = .{ .type = .Move, .data = slot };
3234
+ out[n] = .{ .type = .Move, .data = @intCast(slot) };
3235
3235
  n += 1;
3236
3236
  }
3237
3237
  // Struggle (Pokémon Showdown would use 'move 1' here)
@@ -15,9 +15,6 @@ const isPointerTo = util.isPointerTo;
15
15
  const Player = common.Player;
16
16
  const PointerType = util.PointerType;
17
17
 
18
- const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
19
- const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
20
-
21
18
  /// Information relevant to damage calculation that occured during a Generation II battle `update`.
22
19
  pub const Summaries = extern struct {
23
20
  /// Relevant information for Player 1.
@@ -91,8 +88,8 @@ pub const Calc = struct {
91
88
  };
92
89
 
93
90
  return if (switch (@typeInfo(@TypeOf(val))) {
94
- Enum => val != .None,
95
- Int => val != 0,
91
+ .@"enum" => val != .None,
92
+ .int => val != 0,
96
93
  else => unreachable,
97
94
  }) val else null;
98
95
  }
@@ -148,6 +145,6 @@ fn ReturnType(comptime field: Action.Field) type {
148
145
  return ?(switch (field) {
149
146
  .damages => u8,
150
147
  .critical_hits => Optional(bool),
151
- else => util.FieldType(Action, field),
148
+ else => @FieldType(Action, @tagName(field)),
152
149
  });
153
150
  }
@@ -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 (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
61
- inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
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
- Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
62
+ .@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
67
63
  return false,
68
- Int => if ((a_val > 0) != (b_val > 0)) return false,
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: anytype, shape: bool) !void {
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, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
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, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
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: anytype, shape: bool) !void {
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 (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
191
+ inline for (@typeInfo(Action).@"struct".fields) |field| {
198
192
  const val = @field(self, field.name);
199
193
  switch (@typeInfo(@TypeOf(val))) {
200
- Enum => if (val != .None) {
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
- Int => if (val != 0) {
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
  }
@@ -57,7 +57,7 @@ pub fn Battle(comptime RNG: anytype) type {
57
57
  turn: u16 = 0,
58
58
  field: Field = .{},
59
59
 
60
- _: [padding]u8 = .{0} ** padding,
60
+ _: [padding]u8 = @splat(0),
61
61
 
62
62
  pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
63
63
  assert(isPointerTo(self, Self));
@@ -100,13 +100,13 @@ pub const Weather = enum(u4) {
100
100
  };
101
101
 
102
102
  pub const Side = extern struct {
103
- pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
103
+ pokemon: [6]Pokemon = @splat(.{}),
104
104
  active: ActivePokemon align(8) = .{},
105
105
  conditions: Conditions = .{},
106
106
  // NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
107
107
  last_used_move: Move = .None,
108
108
 
109
- _: [7]u8 = .{0} ** 7,
109
+ _: [7]u8 = @splat(0),
110
110
 
111
111
  const Conditions = packed struct(u32) {
112
112
  Spikes: bool = false,
@@ -161,7 +161,7 @@ pub const Side = extern struct {
161
161
  pub const ActivePokemon = extern struct {
162
162
  volatiles: Volatiles align(4) = .{},
163
163
  stats: Stats(u16) = .{},
164
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
164
+ moves: [4]MoveSlot = @splat(.{}),
165
165
  boosts: Boosts = .{},
166
166
  types: Types = .{},
167
167
  species: Species = .None,
@@ -182,7 +182,7 @@ pub const ActivePokemon = extern struct {
182
182
 
183
183
  pub const Pokemon = extern struct {
184
184
  stats: Stats(u16) = .{},
185
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
185
+ moves: [4]MoveSlot = @splat(.{}),
186
186
  types: Types = .{},
187
187
  dvs: DVs = .{},
188
188
  hp: u16 = 0,
@@ -16,8 +16,6 @@ const showdown = options.showdown;
16
16
  const Species = data.Species;
17
17
  const Stats = data.Stats;
18
18
 
19
- const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
20
-
21
19
  pub const Options = struct {
22
20
  cleric: bool = showdown,
23
21
  };
@@ -74,7 +72,7 @@ pub fn prng(rand: *PSRNG) data.PRNG {
74
72
  var carry: u8 = 0;
75
73
  var div: u8 = 0;
76
74
 
77
- var i: u8 = 0;
75
+ var i: usize = 0;
78
76
  while (i < seed.len) {
79
77
  div = @intCast(timer >> 8);
80
78
  // This is effectively @addWithOverflow but with three operands
@@ -142,7 +140,7 @@ pub const Pokemon = struct {
142
140
  var pokemon = data.Pokemon{};
143
141
  pokemon.species = p.species;
144
142
  const species = Species.get(p.species);
145
- inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
143
+ inline for (@typeInfo(@TypeOf(pokemon.stats)).@"struct".fields) |field| {
146
144
  const hp = comptime std.mem.eql(u8, field.name, "hp");
147
145
  const spc =
148
146
  comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
@@ -183,7 +181,7 @@ pub const Pokemon = struct {
183
181
 
184
182
  var stats: Stats(u16) = .{};
185
183
  const dvs = gen1.DVs.random(rand);
186
- inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
184
+ inline for (@typeInfo(@TypeOf(stats)).@"struct".fields) |field| {
187
185
  const hp = std.mem.eql(u8, field.name, "hp");
188
186
  const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
189
187
  @field(stats, field.name) = Stats(u16).calc(
@@ -195,7 +193,7 @@ pub const Pokemon = struct {
195
193
  );
196
194
  }
197
195
 
198
- var ms = [_]MoveSlot{.{}} ** 4;
196
+ var ms: [4]MoveSlot = @splat(.{});
199
197
  const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
200
198
  for (0..n) |i| {
201
199
  var m: Move = .None;
@@ -130,11 +130,11 @@ fn start(battle: anytype, options: anytype) !Result {
130
130
 
131
131
  const p1_slot = findFirstAlive(p1);
132
132
  assert(!showdown or p1_slot == 1);
133
- if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
133
+ if (p1_slot == 0) return if (findFirstAlive(p2) == 0) .Tie else .Lose;
134
134
 
135
135
  const p2_slot = findFirstAlive(p2);
136
136
  assert(!showdown or p2_slot == 1);
137
- if (p2_slot == 0) return Result.Win;
137
+ if (p2_slot == 0) return .Win;
138
138
 
139
139
  try switchIn(battle, .P1, p1_slot, .Initial, options);
140
140
  try switchIn(battle, .P2, p2_slot, .Initial, options);
@@ -819,13 +819,13 @@ fn checkFaint(
819
819
 
820
820
  if (player_out and foe_out) {
821
821
  try options.log.tie(.{});
822
- return Result.Tie;
822
+ return .Tie;
823
823
  } else if (player_out) {
824
824
  try options.log.win(.{player.foe()});
825
- return if (player == .P1) Result.Lose else Result.Win;
825
+ return if (player == .P1) .Lose else .Win;
826
826
  } else if (foe_out) {
827
827
  try options.log.win(.{player});
828
- return if (player == .P1) Result.Win else Result.Lose;
828
+ return if (player == .P1) .Win else .Lose;
829
829
  }
830
830
 
831
831
  const foe_choice: Choice.Type = if (foe_fainted) .Switch else .Pass;
@@ -1195,14 +1195,14 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
1195
1195
 
1196
1196
  if (pkmn.options.mod and battle.turn >= 1000) {
1197
1197
  try options.log.tie(.{});
1198
- return Result.Tie;
1198
+ return .Tie;
1199
1199
  } else if (battle.turn >= 65535) {
1200
- return Result.Error;
1200
+ return .Error;
1201
1201
  }
1202
1202
 
1203
1203
  try options.log.turn(.{battle.turn});
1204
1204
 
1205
- return Result.Default;
1205
+ return .Default;
1206
1206
  }
1207
1207
 
1208
1208
  pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytype) !void {
@@ -1558,7 +1558,7 @@ pub const Effects = struct {
1558
1558
  const last = battle.foe(player).lastMove(true);
1559
1559
  const failed = volatiles.Encore or
1560
1560
  (last == .None or last == .Struggle or
1561
- last == .Encore or .last == .MirrorMove);
1561
+ last == .Encore or .last == .MirrorMove);
1562
1562
  if (failed) return options.log.fail(.{ foe_ident, .None });
1563
1563
 
1564
1564
  var slot: u3 = 0;
@@ -3081,7 +3081,7 @@ pub const Rolls = struct {
3081
3081
  }
3082
3082
 
3083
3083
  // Conversion 2 can at most choose between 7 types (vs. a Grass-type attack)
3084
- var CONVERSION_2: [7]Type = [_]Type{.@"???"} ** 7;
3084
+ var CONVERSION_2: [7]Type = @splat(.@"???");
3085
3085
 
3086
3086
  fn conversion2(battle: anytype, player: Player, mtype: Type, options: anytype) !Type {
3087
3087
  var i: u8 = 0;
@@ -3506,11 +3506,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3506
3506
  },
3507
3507
  .Switch => {
3508
3508
  const side = battle.side(player);
3509
- var slot: u4 = 2;
3509
+ var slot: usize = 2;
3510
3510
  while (slot <= 6) : (slot += 1) {
3511
3511
  const id = side.order[slot - 1];
3512
3512
  if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
3513
- out[n] = .{ .type = .Switch, .data = slot };
3513
+ out[n] = .{ .type = .Switch, .data = @intCast(slot) };
3514
3514
  n += 1;
3515
3515
  }
3516
3516
  if (n == 0) {
@@ -3540,11 +3540,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3540
3540
  return n;
3541
3541
  }
3542
3542
 
3543
- var slot: u4 = 2;
3543
+ var slot: usize = 2;
3544
3544
  while (!active.volatiles.Trapped and slot <= 6) : (slot += 1) {
3545
3545
  const id = side.order[slot - 1];
3546
3546
  if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
3547
- out[n] = .{ .type = .Switch, .data = slot };
3547
+ out[n] = .{ .type = .Switch, .data = @intCast(slot) };
3548
3548
  n += 1;
3549
3549
  }
3550
3550
 
@@ -3577,7 +3577,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3577
3577
  const struggle =
3578
3578
  m.id == .Bide and (m.pp == 0 or active.volatiles.disable.move == slot);
3579
3579
  const s = if (struggle) 0 else slot;
3580
- out[n] = .{ .type = .Move, .data = s };
3580
+ out[n] = .{ .type = .Move, .data = @intCast(s) };
3581
3581
  n += 1;
3582
3582
  return n;
3583
3583
  }
@@ -3591,7 +3591,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3591
3591
  if (m.id == .None) break;
3592
3592
  if (m.pp == 0) continue;
3593
3593
  if (active.volatiles.disable.move == slot) continue;
3594
- out[n] = .{ .type = .Move, .data = slot };
3594
+ out[n] = .{ .type = .Move, .data = @intCast(slot) };
3595
3595
  n += 1;
3596
3596
  }
3597
3597
  // Struggle (Pokémon Showdown would use 'move 1' here)
package/src/lib/napi.h ADDED
@@ -0,0 +1,2 @@
1
+ #define NAPI_VERSION 8
2
+ #include <node_api.h>
package/src/lib/node.zig CHANGED
@@ -1,4 +1,4 @@
1
- const c = @import("common/napi.zig");
1
+ const c = @import("napi");
2
2
  const js = @import("common/js.zig");
3
3
  const node = @import("bindings/node.zig");
4
4
  const std = @import("std");