@pkmn/engine 0.1.0-dev.ce4c0b2a → 0.1.0-dev.d127cb34

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 (73) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +23 -15
  3. package/build/pkg/addon/browser.d.ts +1 -1
  4. package/build/pkg/addon/browser.js +6 -2
  5. package/build/pkg/addon/browser.js.map +1 -1
  6. package/build/pkg/addon/node.js +22 -8
  7. package/build/pkg/addon/node.js.map +1 -1
  8. package/build/pkg/addon/wasm.d.ts +2 -0
  9. package/build/pkg/addon/wasm.js +53 -0
  10. package/build/pkg/addon/wasm.js.map +1 -0
  11. package/build/pkg/addon.d.ts +25 -5
  12. package/build/pkg/addon.js +8 -17
  13. package/build/pkg/addon.js.map +1 -1
  14. package/build/pkg/data/layout.json +15 -2
  15. package/build/pkg/data/protocol.json +23 -9
  16. package/build/pkg/data.js.map +1 -1
  17. package/build/pkg/gen1.d.ts +15 -18
  18. package/build/pkg/gen1.js +28 -19
  19. package/build/pkg/gen1.js.map +1 -1
  20. package/build/pkg/index.d.ts +37 -100
  21. package/build/pkg/index.js +23 -75
  22. package/build/pkg/index.js.map +1 -1
  23. package/build/pkg/protocol.d.ts +11 -2
  24. package/build/pkg/protocol.js.map +1 -1
  25. package/build/tools/debug.js +56 -13
  26. package/build/tools/debug.js.map +1 -1
  27. package/build/tools/display/debug.jsx +19 -81
  28. package/build/tools/display/debug.jsx.map +1 -1
  29. package/build/tools/display/index.js +35 -15
  30. package/build/tools/display/index.js.map +1 -1
  31. package/build/tools/display/ui.jsx +27 -16
  32. package/build/tools/display/ui.jsx.map +1 -1
  33. package/build/tools/display/util.js +141 -2
  34. package/build/tools/display/util.js.map +1 -1
  35. package/build.zig +330 -245
  36. package/package.json +36 -128
  37. package/src/bin/install-pkmn-engine +25 -9
  38. package/src/lib/bindings/c.zig +201 -0
  39. package/src/lib/bindings/node.zig +117 -0
  40. package/src/lib/bindings/wasm.zig +51 -0
  41. package/src/lib/common/array.zig +5 -12
  42. package/src/lib/common/debug.zig +45 -34
  43. package/src/lib/common/js.zig +3 -9
  44. package/src/lib/common/optional.zig +26 -26
  45. package/src/lib/common/options.zig +2 -0
  46. package/src/lib/common/protocol.zig +228 -197
  47. package/src/lib/common/rational.zig +278 -285
  48. package/src/lib/common/rng.zig +1 -3
  49. package/src/lib/common/util.zig +34 -31
  50. package/src/lib/gen1/calc.zig +220 -195
  51. package/src/lib/gen1/chance.zig +184 -108
  52. package/src/lib/gen1/data/moves.zig +2 -4
  53. package/src/lib/gen1/data/species.zig +1 -3
  54. package/src/lib/gen1/data/types.zig +3 -4
  55. package/src/lib/gen1/data.zig +12 -16
  56. package/src/lib/gen1/helpers.zig +35 -40
  57. package/src/lib/gen1/mechanics.zig +147 -127
  58. package/src/lib/gen2/calc.zig +12 -23
  59. package/src/lib/gen2/chance.zig +38 -26
  60. package/src/lib/gen2/data/items.zig +1 -3
  61. package/src/lib/gen2/data/moves.zig +2 -4
  62. package/src/lib/gen2/data/species.zig +1 -3
  63. package/src/lib/gen2/data/types.zig +7 -5
  64. package/src/lib/gen2/data.zig +19 -22
  65. package/src/lib/gen2/generated.zig +18 -23
  66. package/src/lib/gen2/helpers.zig +32 -31
  67. package/src/lib/gen2/mechanics.zig +58 -55
  68. package/src/lib/napi.h +2 -0
  69. package/src/lib/node.zig +4 -121
  70. package/src/lib/pkmn.zig +98 -19
  71. package/src/lib/wasm.zig +9 -9
  72. package/src/tools/display/dom.ts +43 -31
  73. package/src/tools/display/ui.css +12 -9
@@ -1,30 +1,19 @@
1
- const std = @import("std");
2
-
3
- const pkmn = @import("../pkmn.zig");
4
-
1
+ const chance = @import("chance.zig");
5
2
  const common = @import("../common/data.zig");
6
- const util = @import("../common/util.zig");
7
-
8
3
  const Optional = @import("../common/optional.zig").Optional;
4
+ const pkmn = @import("../pkmn.zig");
5
+ const std = @import("std");
6
+ const util = @import("../common/util.zig");
9
7
 
10
- const chance = @import("chance.zig");
11
-
8
+ const Action = chance.Action;
9
+ const Actions = chance.Actions;
12
10
  const assert = std.debug.assert;
13
-
11
+ const Criticals = chance.Criticals;
12
+ const Damages = chance.Damages;
14
13
  const enabled = pkmn.options.calc;
15
-
14
+ const isPointerTo = util.isPointerTo;
16
15
  const Player = common.Player;
17
-
18
16
  const PointerType = util.PointerType;
19
- const isPointerTo = util.isPointerTo;
20
-
21
- const Actions = chance.Actions;
22
- const Action = chance.Action;
23
- const Criticals = chance.Criticals;
24
- const Damages = chance.Damages;
25
-
26
- const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
27
- const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
28
17
 
29
18
  /// Information relevant to damage calculation that occured during a Generation II battle `update`.
30
19
  pub const Summaries = extern struct {
@@ -99,8 +88,8 @@ pub const Calc = struct {
99
88
  };
100
89
 
101
90
  return if (switch (@typeInfo(@TypeOf(val))) {
102
- Enum => val != .None,
103
- Int => val != 0,
91
+ .@"enum" => val != .None,
92
+ .int => val != 0,
104
93
  else => unreachable,
105
94
  }) val else null;
106
95
  }
@@ -156,6 +145,6 @@ fn ReturnType(comptime field: Action.Field) type {
156
145
  return ?(switch (field) {
157
146
  .damages => u8,
158
147
  .critical_hits => Optional(bool),
159
- else => std.meta.FieldType(Action, field),
148
+ else => @FieldType(Action, @tagName(field)),
160
149
  });
161
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,28 @@ 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| {
62
- const a_val = @field(@field(a, player.name), field.name);
63
- const b_val = @field(@field(b, player.name), field.name);
56
+ inline for (if (@hasField(@TypeOf(@typeInfo(Actions).@"struct"), "fields"))
57
+ @typeInfo(Actions).@"struct".fields
58
+ else
59
+ @typeInfo(Actions).@"struct".field_names) |player|
60
+ {
61
+ const player_name = if (@hasField(@TypeOf(player), "name"))
62
+ player.name
63
+ else
64
+ player;
65
+ inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
66
+ @typeInfo(Action).@"struct".fields
67
+ else
68
+ @typeInfo(Action).@"struct".field_names) |field|
69
+ {
70
+ const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
71
+ const a_val = @field(@field(a, player_name), field_name);
72
+ const b_val = @field(@field(b, player_name), field_name);
64
73
 
65
74
  switch (@typeInfo(@TypeOf(a_val))) {
66
- Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
75
+ .@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
67
76
  return false,
68
- Int => if ((a_val > 0) != (b_val > 0)) return false,
77
+ .int => if ((a_val > 0) != (b_val > 0)) return false,
69
78
  else => unreachable,
70
79
  }
71
80
  }
@@ -73,7 +82,7 @@ pub const Actions = extern struct {
73
82
  return true;
74
83
  }
75
84
 
76
- pub fn fmt(self: Actions, writer: anytype, shape: bool) !void {
85
+ pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
77
86
  try writer.writeAll("<P1 = ");
78
87
  try self.p1.fmt(writer, shape);
79
88
  try writer.writeAll(", P2 = ");
@@ -81,8 +90,7 @@ pub const Actions = extern struct {
81
90
  try writer.writeAll(">");
82
91
  }
83
92
 
84
- pub fn format(a: Actions, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
85
- _ = .{ f, o };
93
+ pub fn format(a: Actions, w: *std.Io.Writer) !void {
86
94
  try fmt(a, w, false);
87
95
  }
88
96
  };
@@ -186,37 +194,41 @@ pub const Action = packed struct(u128) {
186
194
 
187
195
  pub const Field = std.meta.FieldEnum(Action);
188
196
 
189
- pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
190
- _ = .{ f, o };
197
+ pub fn format(a: Action, w: *std.Io.Writer) !void {
191
198
  try fmt(a, w, false);
192
199
  }
193
200
 
194
- pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
201
+ pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
195
202
  try writer.writeByte('(');
196
203
  var printed = false;
197
- inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
198
- const val = @field(self, field.name);
204
+ inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
205
+ @typeInfo(Action).@"struct".fields
206
+ else
207
+ @typeInfo(Action).@"struct".field_names) |field|
208
+ {
209
+ const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
210
+ const val = @field(self, field_name);
199
211
  switch (@typeInfo(@TypeOf(val))) {
200
- Enum => if (val != .None) {
212
+ .@"enum" => if (val != .None) {
201
213
  if (printed) try writer.writeAll(", ");
202
214
  if (shape) {
203
- try writer.print("{s}:?", .{field.name});
215
+ try writer.print("{s}:?", .{field_name});
204
216
  } else if (@TypeOf(val) == Optional(bool)) {
205
217
  try writer.print("{s}{s}", .{
206
218
  if (val == .false) "!" else "",
207
- field.name,
219
+ field_name,
208
220
  });
209
221
  } else {
210
- try writer.print("{s}:{s}", .{ field.name, @tagName(val) });
222
+ try writer.print("{s}:{s}", .{ field_name, @tagName(val) });
211
223
  }
212
224
  printed = true;
213
225
  },
214
- Int => if (val != 0) {
226
+ .int => if (val != 0) {
215
227
  if (printed) try writer.writeAll(", ");
216
228
  if (shape) {
217
- try writer.print("{s}:?", .{field.name});
229
+ try writer.print("{s}:?", .{field_name});
218
230
  } else {
219
- try writer.print("{s}:{d}", .{ field.name, val });
231
+ try writer.print("{s}:{d}", .{ field_name, val });
220
232
  }
221
233
  printed = true;
222
234
  },
@@ -786,7 +798,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
786
798
 
787
799
  r.reduce();
788
800
  if (r.p != p or r.q != q) {
789
- print("expected {d}/{d}, found {}\n", .{ p, q, r });
801
+ print("expected {d}/{d}, found {f}\n", .{ p, q, r });
790
802
  return error.TestExpectedEqual;
791
803
  }
792
804
  }
@@ -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 `type`.
177
- pub fn precedence(self: Type) u8 {
178
- return PRECEDENCE[@intFromEnum(self)];
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.
@@ -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 = .{0} ** padding,
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 = [_]Pokemon{.{}} ** 6,
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 = .{0} ** 7,
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 = [_]MoveSlot{.{}} ** 4,
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 = [_]MoveSlot{.{}} ** 4,
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.precedence());
457
- try expect(Type.Bug.precedence() > Type.Poison.precedence());
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 Player = common.Player;
11
- const Result = common.Result;
12
-
13
- const Effectiveness = data.Effectiveness;
14
- const Move = data.Move;
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 afterMove = mechanics.afterMove;
32
- const buildRage = mechanics.buildRage;
21
+ const Effectiveness = data.Effectiveness;
22
+ const Effects = mechanics.Effects;
33
23
  const kingsRock = mechanics.kingsRock;
34
- const destinyBond = mechanics.destinyBond;
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;
@@ -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: u8 = 0;
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,15 +140,20 @@ 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 (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
153
- const hp = comptime std.mem.eql(u8, field.name, "hp");
143
+ inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(pokemon.stats)).@"struct"), "fields"))
144
+ @typeInfo(@TypeOf(pokemon.stats)).@"struct".fields
145
+ else
146
+ @typeInfo(@TypeOf(pokemon.stats)).@"struct".field_names) |field|
147
+ {
148
+ const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
149
+ const hp = comptime std.mem.eql(u8, field_name, "hp");
154
150
  const spc =
155
- comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
156
- @field(pokemon.stats, field.name) = Stats(u16).calc(
157
- field.name,
158
- @field(species.stats, field.name),
159
- if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field.name),
160
- @field(p.stats, field.name),
151
+ comptime std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
152
+ @field(pokemon.stats, field_name) = Stats(u16).calc(
153
+ field_name,
154
+ @field(species.stats, field_name),
155
+ if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field_name),
156
+ @field(p.stats, field_name),
161
157
  p.level,
162
158
  );
163
159
  }
@@ -190,19 +186,24 @@ pub const Pokemon = struct {
190
186
 
191
187
  var stats: Stats(u16) = .{};
192
188
  const dvs = gen1.DVs.random(rand);
193
- inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
194
- const hp = std.mem.eql(u8, field.name, "hp");
195
- const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
196
- @field(stats, field.name) = Stats(u16).calc(
197
- field.name,
198
- @field(species.stats, field.name),
199
- if (hp) dvs.hp() else if (spc) dvs.spc else @field(dvs, field.name),
189
+ inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(stats)).@"struct"), "fields"))
190
+ @typeInfo(@TypeOf(stats)).@"struct".fields
191
+ else
192
+ @typeInfo(@TypeOf(stats)).@"struct".field_names) |field|
193
+ {
194
+ const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
195
+ const hp = std.mem.eql(u8, field_name, "hp");
196
+ const spc = std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
197
+ @field(stats, field_name) = Stats(u16).calc(
198
+ field_name,
199
+ @field(species.stats, field_name),
200
+ if (hp) dvs.hp() else if (spc) dvs.spc else @field(dvs, field_name),
200
201
  if (rand.chance(u8, 1, 20)) rand.range(u8, 0, 255 + 1) else 255,
201
202
  lvl,
202
203
  );
203
204
  }
204
205
 
205
- var ms = [_]MoveSlot{.{}} ** 4;
206
+ var ms: [4]MoveSlot = @splat(.{});
206
207
  const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
207
208
  for (0..n) |i| {
208
209
  var m: Move = .None;