@pkmn/engine 0.1.0-dev.d56bdadd → 0.1.0-dev.d73cd76c

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 (84) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +66 -32
  3. package/build/pkg/addon/browser.d.ts +3 -0
  4. package/build/pkg/addon/browser.js +47 -0
  5. package/build/pkg/addon/browser.js.map +1 -0
  6. package/build/pkg/addon/node.d.ts +3 -0
  7. package/build/pkg/addon/node.js +110 -0
  8. package/build/pkg/addon/node.js.map +1 -0
  9. package/build/pkg/addon/wasm.d.ts +2 -0
  10. package/build/pkg/addon/wasm.js +53 -0
  11. package/build/pkg/addon/wasm.js.map +1 -0
  12. package/build/pkg/addon.d.ts +26 -4
  13. package/build/pkg/addon.js +39 -71
  14. package/build/pkg/addon.js.map +1 -1
  15. package/build/pkg/data/layout.json +15 -2
  16. package/build/pkg/data/protocol.json +23 -9
  17. package/build/pkg/data.d.ts +27 -5
  18. package/build/pkg/data.js.map +1 -1
  19. package/build/pkg/gen1.d.ts +15 -18
  20. package/build/pkg/gen1.js +28 -19
  21. package/build/pkg/gen1.js.map +1 -1
  22. package/build/pkg/index.d.ts +42 -91
  23. package/build/pkg/index.js +23 -70
  24. package/build/pkg/index.js.map +1 -1
  25. package/build/pkg/protocol.d.ts +29 -1
  26. package/build/pkg/protocol.js +109 -107
  27. package/build/pkg/protocol.js.map +1 -1
  28. package/build/tools/debug.js +84 -73
  29. package/build/tools/debug.js.map +1 -1
  30. package/build/tools/display/debug.jsx +66 -0
  31. package/build/tools/display/debug.jsx.map +1 -0
  32. package/build/tools/display/index.js +152 -0
  33. package/build/tools/display/index.js.map +1 -0
  34. package/build/tools/display/ui.jsx +360 -0
  35. package/build/tools/display/ui.jsx.map +1 -0
  36. package/build/tools/display/util.js +157 -0
  37. package/build/tools/display/util.js.map +1 -0
  38. package/build.zig +357 -135
  39. package/package.json +61 -132
  40. package/src/bin/install-pkmn-engine +47 -21
  41. package/src/bin/pkmn-debug +1 -1
  42. package/src/lib/bindings/c.zig +201 -0
  43. package/src/lib/{binding → bindings}/node.zig +15 -32
  44. package/src/lib/bindings/wasm.zig +51 -0
  45. package/src/lib/common/array.zig +70 -0
  46. package/src/lib/common/data.zig +4 -4
  47. package/src/lib/common/debug.zig +45 -29
  48. package/src/lib/common/js.zig +3 -7
  49. package/src/lib/common/optional.zig +27 -23
  50. package/src/lib/common/options.zig +6 -0
  51. package/src/lib/common/protocol.zig +565 -552
  52. package/src/lib/common/rational.zig +340 -253
  53. package/src/lib/common/rng.zig +7 -9
  54. package/src/lib/common/util.zig +53 -1
  55. package/src/lib/gen1/calc.zig +724 -175
  56. package/src/lib/gen1/chance.zig +486 -300
  57. package/src/lib/gen1/data/moves.zig +2 -4
  58. package/src/lib/gen1/data/species.zig +2 -4
  59. package/src/lib/gen1/data/types.zig +3 -4
  60. package/src/lib/gen1/data.zig +29 -32
  61. package/src/lib/gen1/helpers.zig +113 -561
  62. package/src/lib/gen1/mechanics.zig +611 -488
  63. package/src/lib/gen2/calc.zig +35 -28
  64. package/src/lib/gen2/chance.zig +111 -511
  65. package/src/lib/gen2/data/items.zig +1 -3
  66. package/src/lib/gen2/data/moves.zig +2 -4
  67. package/src/lib/gen2/data/species.zig +1 -3
  68. package/src/lib/gen2/data/types.zig +7 -5
  69. package/src/lib/gen2/data.zig +27 -29
  70. package/src/lib/gen2/generated.zig +22 -29
  71. package/src/lib/gen2/helpers.zig +68 -39
  72. package/src/lib/gen2/mechanics.zig +504 -430
  73. package/src/lib/napi.h +2 -0
  74. package/src/lib/node.zig +14 -0
  75. package/src/lib/pkmn.zig +125 -29
  76. package/src/lib/wasm.zig +13 -0
  77. package/src/tools/display/dom.ts +108 -0
  78. package/src/tools/display/ui.css +354 -0
  79. package/build/test/display.js +0 -400
  80. package/build/test/display.js.map +0 -1
  81. package/src/lib/binding/wasm.zig +0 -12
  82. package/src/lib/gen1/README.md +0 -894
  83. package/src/lib/gen2/README.md +0 -329
  84. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -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;
@@ -45,6 +41,7 @@ const Player = data.Player;
45
41
  const Result = data.Result;
46
42
 
47
43
  const PointerType = util.PointerType;
44
+ const isPointerTo = util.isPointerTo;
48
45
 
49
46
  const showdown = pkmn.options.showdown;
50
47
 
@@ -60,15 +57,15 @@ pub fn Battle(comptime RNG: anytype) type {
60
57
  turn: u16 = 0,
61
58
  field: Field = .{},
62
59
 
63
- _: [padding]u8 = .{0} ** padding,
60
+ _: [padding]u8 = @splat(0),
64
61
 
65
62
  pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
66
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
63
+ assert(isPointerTo(self, Self));
67
64
  return &self.sides[@intFromEnum(player)];
68
65
  }
69
66
 
70
67
  pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
71
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
68
+ assert(isPointerTo(self, Self));
72
69
  return &self.sides[@intFromEnum(player.foe())];
73
70
  }
74
71
 
@@ -103,13 +100,13 @@ pub const Weather = enum(u4) {
103
100
  };
104
101
 
105
102
  pub const Side = extern struct {
106
- pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
103
+ pokemon: [6]Pokemon = @splat(.{}),
107
104
  active: ActivePokemon align(8) = .{},
108
105
  conditions: Conditions = .{},
109
106
  // NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
110
107
  last_used_move: Move = .None,
111
108
 
112
- _: [7]u8 = .{0} ** 7,
109
+ _: [7]u8 = @splat(0),
113
110
 
114
111
  const Conditions = packed struct(u32) {
115
112
  Spikes: bool = false,
@@ -134,7 +131,7 @@ pub const Side = extern struct {
134
131
  }
135
132
 
136
133
  pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
137
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
134
+ assert(isPointerTo(self, Side));
138
135
  assert(slot > 0 and slot <= 6);
139
136
  const id = self.pokemon[slot - 1].position;
140
137
  assert(id > 0 and id <= 6);
@@ -142,7 +139,7 @@ pub const Side = extern struct {
142
139
  }
143
140
 
144
141
  pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
145
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
142
+ assert(isPointerTo(self, Side));
146
143
  return self.get(1);
147
144
  }
148
145
 
@@ -164,7 +161,7 @@ pub const Side = extern struct {
164
161
  pub const ActivePokemon = extern struct {
165
162
  volatiles: Volatiles align(4) = .{},
166
163
  stats: Stats(u16) = .{},
167
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
164
+ moves: [4]MoveSlot = @splat(.{}),
168
165
  boosts: Boosts = .{},
169
166
  types: Types = .{},
170
167
  species: Species = .None,
@@ -176,7 +173,7 @@ pub const ActivePokemon = extern struct {
176
173
  }
177
174
 
178
175
  pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
179
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
176
+ assert(isPointerTo(self, Pokemon));
180
177
  assert(mslot > 0 and mslot <= 4);
181
178
  assert(self.moves[mslot - 1].id != .None);
182
179
  return &self.moves[mslot - 1];
@@ -185,7 +182,7 @@ pub const ActivePokemon = extern struct {
185
182
 
186
183
  pub const Pokemon = extern struct {
187
184
  stats: Stats(u16) = .{},
188
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
185
+ moves: [4]MoveSlot = @splat(.{}),
189
186
  types: Types = .{},
190
187
  dvs: DVs = .{},
191
188
  hp: u16 = 0,
@@ -201,7 +198,7 @@ pub const Pokemon = extern struct {
201
198
  }
202
199
 
203
200
  pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
204
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
201
+ assert(isPointerTo(self, Pokemon));
205
202
  assert(mslot > 0 and mslot <= 4);
206
203
  assert(self.moves[mslot - 1].id != .None);
207
204
  return &self.moves[mslot - 1];
@@ -239,7 +236,7 @@ pub const DVs = packed struct(u16) {
239
236
  };
240
237
  const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
241
238
  ((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
242
- var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
239
+ const t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
243
240
  return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
244
241
  }
245
242
  };
@@ -452,8 +449,9 @@ test Types {
452
449
  try expect(!Type.Steel.special());
453
450
  try expect(Type.Dark.special());
454
451
 
455
- try expectEqual(@as(u8, 3), Type.Water.precedence());
456
- 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));
457
455
 
458
456
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
459
457
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Water, .Fire));
@@ -1,39 +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 assert = std.debug.assert;
11
-
12
- const Player = common.Player;
13
- const Result = common.Result;
14
-
15
- const Effectiveness = data.Effectiveness;
16
- const Move = data.Move;
17
-
18
- const Effects = mechanics.Effects;
19
- const State = mechanics.State;
20
-
21
- 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;
22
13
  const canCharge = mechanics.canCharge;
14
+ const canMove = mechanics.canMove;
15
+ const checkCriticalHit = mechanics.checkCriticalHit;
16
+ const checkHit = mechanics.checkHit;
23
17
  const decrementPP = mechanics.decrementPP;
18
+ const destinyBond = mechanics.destinyBond;
24
19
  const doMove = mechanics.doMove;
25
- const checkHit = mechanics.checkHit;
26
- const checkCriticalHit = mechanics.checkCriticalHit;
27
- const calcDamage = mechanics.calcDamage;
28
- const adjustDamage = mechanics.adjustDamage;
29
- const randomizeDamage = mechanics.randomizeDamage;
30
- const applyDamage = mechanics.applyDamage;
31
- const reportOutcome = mechanics.reportOutcome;
32
20
  const effectChance = mechanics.effectChance;
33
- const afterMove = mechanics.afterMove;
34
- const buildRage = mechanics.buildRage;
21
+ const Effectiveness = data.Effectiveness;
22
+ const Effects = mechanics.Effects;
35
23
  const kingsRock = mechanics.kingsRock;
36
- 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;
37
30
 
38
31
  const SECONDARY = true;
39
32
  const KINGS = true;
@@ -42,7 +35,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
42
35
  var log = options.log;
43
36
 
44
37
  var side = battle.side(player);
45
- var volatiles = &side.active.volatiles;
38
+ const volatiles = &side.active.volatiles;
46
39
 
47
40
  const ident = battle.active(player);
48
41
  const foe_ident = battle.active(player.foe());
@@ -175,7 +168,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
175
168
  if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
176
169
  // TODO rampage
177
170
  // usedmovetext
178
- try log.move(ident, state.move, foe_ident); // FIXME self? from?
171
+ try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
179
172
  try checkHit(battle, player, state, options);
180
173
  try checkCriticalHit(battle, player, state, options);
181
174
  try calcDamage(battle, player, state, options);
@@ -368,7 +361,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
368
361
  (volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
369
362
  if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
370
363
  // usedmovetext
371
- try log.move(ident, state.move, foe_ident); // FIXME self? from?
364
+ try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
372
365
  try Effects.bide.unleash(battle, player, state, options);
373
366
 
374
367
  if (state.immune()) {
@@ -555,7 +548,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
555
548
  (volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
556
549
  if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
557
550
  // usedmovetext
558
- try log.move(ident, state.move, foe_ident); // FIXME self? from?
551
+ try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
559
552
  try checkCriticalHit(battle, player, state, options);
560
553
  try calcDamage(battle, player, state, options);
561
554
  try adjustDamage(battle, player, state, options);
@@ -1,31 +1,20 @@
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
- const protocol = @import("../common/protocol.zig");
6
5
  const rng = @import("../common/rng.zig");
7
-
8
- const gen1 = @import("../gen1/data.zig");
9
-
10
- const data = @import("data.zig");
6
+ const std = @import("std");
11
7
 
12
8
  const assert = std.debug.assert;
13
-
14
- const expectEqual = std.testing.expectEqual;
15
-
16
9
  const Choice = common.Choice;
17
-
18
- const showdown = options.showdown;
19
-
20
- const PSRNG = rng.PSRNG;
21
-
22
10
  const DVs = data.DVs;
23
11
  const Item = data.Item;
24
12
  const Move = data.Move;
25
13
  const MoveSlot = data.MoveSlot;
14
+ const PSRNG = rng.PSRNG;
15
+ const showdown = options.showdown;
26
16
  const Species = data.Species;
27
17
  const Stats = data.Stats;
28
- const Status = data.Status;
29
18
 
30
19
  pub const Options = struct {
31
20
  cleric: bool = showdown,
@@ -65,17 +54,47 @@ pub const Battle = struct {
65
54
  };
66
55
 
67
56
  pub fn prng(rand: *PSRNG) data.PRNG {
68
- // FIXME: what is max range in gen2?
69
57
  return .{
70
58
  .src = .{
71
59
  .seed = if (showdown)
72
60
  rand.newSeed()
73
- else
74
- .{
75
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
76
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
77
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
78
- },
61
+ else seed: {
62
+ // GLITCH: The initial RNG seed in Gen II link battles is heavily constrained
63
+ // https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
64
+ var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
65
+
66
+ // Bytes in seed can only range from 0-252, not 0-255
67
+ const max: u8 = 253;
68
+ // Instructions take multiples of 4 cycles to execute
69
+ var timer: u16 = 4 * rand.range(u16, 0, 16384);
70
+ var add: u8 = rand.range(u8, 0, 256);
71
+ var sub: u8 = rand.range(u8, 0, 256);
72
+ var carry: u8 = 0;
73
+ var div: u8 = 0;
74
+
75
+ var i: usize = 0;
76
+ while (i < seed.len) {
77
+ div = @intCast(timer >> 8);
78
+ // This is effectively @addWithOverflow but with three operands
79
+ const result = @as(u16, add) + @as(u16, div) + @as(u16, carry);
80
+ add = @intCast(result & 255);
81
+ carry = @intFromBool(result > 255);
82
+ timer +%= 44;
83
+ div = @intCast(timer >> 8);
84
+ sub -%= div +% carry;
85
+
86
+ if (sub < max) {
87
+ seed[i] = sub;
88
+ i += 1;
89
+ timer +%= 152;
90
+ carry = 1;
91
+ } else {
92
+ timer +%= 132;
93
+ carry = 0;
94
+ }
95
+ }
96
+ break :seed seed;
97
+ },
79
98
  },
80
99
  };
81
100
  }
@@ -121,15 +140,20 @@ pub const Pokemon = struct {
121
140
  var pokemon = data.Pokemon{};
122
141
  pokemon.species = p.species;
123
142
  const species = Species.get(p.species);
124
- inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
125
- 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");
126
150
  const spc =
127
- comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
128
- @field(pokemon.stats, field.name) = Stats(u16).calc(
129
- field.name,
130
- @field(species.stats, field.name),
131
- if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field.name),
132
- @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),
133
157
  p.level,
134
158
  );
135
159
  }
@@ -162,19 +186,24 @@ pub const Pokemon = struct {
162
186
 
163
187
  var stats: Stats(u16) = .{};
164
188
  const dvs = gen1.DVs.random(rand);
165
- inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
166
- const hp = std.mem.eql(u8, field.name, "hp");
167
- const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
168
- @field(stats, field.name) = Stats(u16).calc(
169
- field.name,
170
- @field(species.stats, field.name),
171
- 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),
172
201
  if (rand.chance(u8, 1, 20)) rand.range(u8, 0, 255 + 1) else 255,
173
202
  lvl,
174
203
  );
175
204
  }
176
205
 
177
- var ms = [_]MoveSlot{.{}} ** 4;
206
+ var ms: [4]MoveSlot = @splat(.{});
178
207
  const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
179
208
  for (0..n) |i| {
180
209
  var m: Move = .None;