@pkmn/engine 0.1.0-dev.736f7a8b → 0.1.0-dev.73fa6bda

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 (85) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +64 -29
  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/ids.json +12 -12
  16. package/build/pkg/data/layout.json +17 -4
  17. package/build/pkg/data/protocol.json +84 -13
  18. package/build/pkg/data.d.ts +27 -5
  19. package/build/pkg/data.js.map +1 -1
  20. package/build/pkg/gen1.d.ts +15 -18
  21. package/build/pkg/gen1.js +30 -21
  22. package/build/pkg/gen1.js.map +1 -1
  23. package/build/pkg/index.d.ts +42 -91
  24. package/build/pkg/index.js +23 -70
  25. package/build/pkg/index.js.map +1 -1
  26. package/build/pkg/protocol.d.ts +29 -1
  27. package/build/pkg/protocol.js +123 -102
  28. package/build/pkg/protocol.js.map +1 -1
  29. package/build/tools/debug.js +84 -73
  30. package/build/tools/debug.js.map +1 -1
  31. package/build/tools/display/debug.jsx +66 -0
  32. package/build/tools/display/debug.jsx.map +1 -0
  33. package/build/tools/display/index.js +152 -0
  34. package/build/tools/display/index.js.map +1 -0
  35. package/build/tools/display/ui.jsx +360 -0
  36. package/build/tools/display/ui.jsx.map +1 -0
  37. package/build/tools/display/util.js +157 -0
  38. package/build/tools/display/util.js.map +1 -0
  39. package/build.zig +275 -122
  40. package/package.json +64 -123
  41. package/src/bin/install-pkmn-engine +47 -21
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/bindings/c.zig +200 -0
  44. package/src/lib/{binding → bindings}/node.zig +15 -32
  45. package/src/lib/bindings/wasm.zig +50 -0
  46. package/src/lib/common/array.zig +70 -0
  47. package/src/lib/common/data.zig +12 -24
  48. package/src/lib/common/debug.zig +33 -28
  49. package/src/lib/common/js.zig +3 -7
  50. package/src/lib/common/optional.zig +19 -18
  51. package/src/lib/common/options.zig +6 -0
  52. package/src/lib/common/protocol.zig +898 -441
  53. package/src/lib/common/rational.zig +341 -254
  54. package/src/lib/common/rng.zig +22 -15
  55. package/src/lib/common/util.zig +33 -1
  56. package/src/lib/gen1/calc.zig +726 -177
  57. package/src/lib/gen1/chance.zig +484 -306
  58. package/src/lib/gen1/data/moves.zig +193 -183
  59. package/src/lib/gen1/data/species.zig +4 -6
  60. package/src/lib/gen1/data/types.zig +9 -10
  61. package/src/lib/gen1/data.zig +60 -75
  62. package/src/lib/gen1/helpers.zig +90 -563
  63. package/src/lib/gen1/mechanics.zig +696 -614
  64. package/src/lib/gen2/calc.zig +150 -0
  65. package/src/lib/gen2/chance.zig +896 -0
  66. package/src/lib/gen2/data/items.zig +13 -21
  67. package/src/lib/gen2/data/moves.zig +650 -571
  68. package/src/lib/gen2/data/species.zig +2 -4
  69. package/src/lib/gen2/data/types.zig +139 -34
  70. package/src/lib/gen2/data.zig +104 -94
  71. package/src/lib/gen2/generated.zig +801 -0
  72. package/src/lib/gen2/helpers.zig +49 -35
  73. package/src/lib/gen2/mechanics.zig +3324 -113
  74. package/src/lib/napi.h +2 -0
  75. package/src/lib/node.zig +14 -0
  76. package/src/lib/pkmn.zig +125 -29
  77. package/src/lib/wasm.zig +13 -0
  78. package/src/tools/display/dom.ts +108 -0
  79. package/src/tools/display/ui.css +354 -0
  80. package/build/test/display.js +0 -400
  81. package/build/test/display.js.map +0 -1
  82. package/src/lib/binding/wasm.zig +0 -12
  83. package/src/lib/gen1/README.md +0 -892
  84. package/src/lib/gen2/README.md +0 -383
  85. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -1,11 +1,9 @@
1
- //! Code generated by `tools/generate` - manual edits will be overwritten
2
-
3
- const std = @import("std");
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
4
2
 
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
- //! Code generated by `tools/generate` - manual edits will be overwritten
2
-
3
- const std = @import("std");
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
4
2
 
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;
@@ -14,15 +13,15 @@ const I = Effectiveness.Immune;
14
13
 
15
14
  /// Representation of a Generation II type in Pokémon.
16
15
  pub const Type = enum(u8) {
16
+ Ground,
17
+ Rock,
18
+ Steel,
17
19
  Normal,
18
20
  Fighting,
19
21
  Flying,
20
22
  Poison,
21
- Ground,
22
- Rock,
23
23
  Bug,
24
24
  Ghost,
25
- Steel,
26
25
  @"???",
27
26
  Fire,
28
27
  Water,
@@ -33,37 +32,37 @@ pub const Type = enum(u8) {
33
32
  Dragon,
34
33
  Dark,
35
34
 
36
- const CHART = [18][18]Effectiveness{
37
- [_]Effectiveness{ N, N, N, N, N, R, N, I, R, N, N, N, N, N, N, N, N, N }, // Normal
38
- [_]Effectiveness{ S, N, R, R, N, S, R, I, S, N, N, N, N, N, R, S, N, S }, // Fighting
39
- [_]Effectiveness{ N, S, N, N, N, R, S, N, R, N, N, N, S, R, N, N, N, N }, // Flying
40
- [_]Effectiveness{ N, N, N, R, R, R, N, R, I, N, N, N, S, N, N, N, N, N }, // Poison
41
- [_]Effectiveness{ N, N, I, S, N, S, R, N, S, N, S, N, R, S, N, N, N, N }, // Ground
42
- [_]Effectiveness{ N, R, S, N, R, N, S, N, R, N, S, N, N, N, N, S, N, N }, // Rock
43
- [_]Effectiveness{ N, R, R, R, N, N, N, R, R, N, R, N, S, N, S, N, N, S }, // Bug
44
- [_]Effectiveness{ I, N, N, N, N, N, N, S, R, N, N, N, N, N, S, N, N, R }, // Ghost
45
- [_]Effectiveness{ N, N, N, N, N, S, N, N, R, N, R, R, N, R, N, S, N, N }, // Steel
35
+ const CHART: [18][18]Effectiveness = .{
36
+ [_]Effectiveness{ N, S, S, N, N, I, S, R, N, N, S, N, R, S, N, N, N, N }, // Ground
37
+ [_]Effectiveness{ R, N, R, N, R, S, N, S, N, N, S, N, N, N, N, S, N, N }, // Rock
38
+ [_]Effectiveness{ N, S, R, N, N, N, N, N, N, N, R, R, N, R, N, S, N, N }, // Steel
39
+ [_]Effectiveness{ N, R, R, N, N, N, N, N, I, N, N, N, N, N, N, N, N, N }, // Normal
40
+ [_]Effectiveness{ N, S, S, S, N, R, R, R, I, N, N, N, N, N, R, S, N, S }, // Fighting
41
+ [_]Effectiveness{ N, R, R, N, S, N, N, S, N, N, N, N, S, R, N, N, N, N }, // Flying
42
+ [_]Effectiveness{ R, R, I, N, N, N, R, N, R, N, N, N, S, N, N, N, N, N }, // Poison
43
+ [_]Effectiveness{ N, N, R, N, R, R, R, N, R, N, R, N, S, N, S, N, N, S }, // Bug
44
+ [_]Effectiveness{ N, N, R, I, N, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Ghost
46
45
  [_]Effectiveness{ N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N }, // ???
47
- [_]Effectiveness{ N, N, N, N, N, R, S, N, S, N, R, R, S, N, N, S, R, N }, // Fire
48
- [_]Effectiveness{ N, N, N, N, S, S, N, N, N, N, S, R, R, N, N, N, R, N }, // Water
49
- [_]Effectiveness{ N, N, R, R, S, S, R, N, R, N, R, S, R, N, N, N, R, N }, // Grass
50
- [_]Effectiveness{ N, N, S, N, I, N, N, N, N, N, N, S, R, R, N, N, R, N }, // Electric
51
- [_]Effectiveness{ N, S, N, S, N, N, N, N, R, N, N, N, N, N, R, N, N, I }, // Psychic
52
- [_]Effectiveness{ N, N, S, N, S, N, N, N, R, N, R, R, S, N, N, R, S, N }, // Ice
53
- [_]Effectiveness{ N, N, N, N, N, N, N, N, R, N, N, N, N, N, N, N, S, N }, // Dragon
54
- [_]Effectiveness{ N, R, N, N, N, N, N, S, R, N, N, N, N, N, S, N, N, R }, // Dark
46
+ [_]Effectiveness{ N, R, S, N, N, N, N, S, N, N, R, R, S, N, N, S, R, N }, // Fire
47
+ [_]Effectiveness{ S, S, N, N, N, N, N, N, N, N, S, R, R, N, N, N, R, N }, // Water
48
+ [_]Effectiveness{ S, S, R, N, N, R, R, R, N, N, R, S, R, N, N, N, R, N }, // Grass
49
+ [_]Effectiveness{ I, N, N, N, N, S, N, N, N, N, N, S, R, R, N, N, R, N }, // Electric
50
+ [_]Effectiveness{ N, N, R, N, S, N, S, N, N, N, N, N, N, N, R, N, N, I }, // Psychic
51
+ [_]Effectiveness{ S, N, R, N, N, S, N, N, N, N, R, R, S, N, N, R, S, N }, // Ice
52
+ [_]Effectiveness{ N, N, R, N, N, N, N, N, N, N, N, N, N, N, N, N, S, N }, // Dragon
53
+ [_]Effectiveness{ N, N, R, N, R, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Dark
55
54
  };
56
55
 
57
56
  const PRECEDENCE = [_]u8{
57
+ 9, // Ground
58
+ 13, // Rock
59
+ 17, // Steel
58
60
  1, // Normal
59
61
  7, // Fighting
60
62
  10, // Flying
61
63
  8, // Poison
62
- 9, // Ground
63
- 13, // Rock
64
64
  12, // Bug
65
65
  14, // Ghost
66
- 17, // Steel
67
66
  0, // ???
68
67
  2, // Fire
69
68
  3, // Water
@@ -75,28 +74,128 @@ pub const Type = enum(u8) {
75
74
  16, // Dark
76
75
  };
77
76
 
77
+ const INDEXES = [_]u8{
78
+ 4, // Ground
79
+ 5, // Rock
80
+ 9, // Steel
81
+ 0, // Normal
82
+ 1, // Fighting
83
+ 2, // Flying
84
+ 3, // Poison
85
+ 7, // Bug
86
+ 8, // Ghost
87
+ 19, // ???
88
+ 20, // Fire
89
+ 21, // Water
90
+ 22, // Grass
91
+ 23, // Electric
92
+ 24, // Psychic
93
+ 25, // Ice
94
+ 26, // Dragon
95
+ 27, // Dark
96
+ };
97
+
98
+ const CONVERSION_2 = [_]Type{
99
+ .Normal,
100
+ .Fighting,
101
+ .Flying,
102
+ .Poison,
103
+ .Ground,
104
+ .Rock,
105
+ .@"???", // placeholder
106
+ .Bug,
107
+ .Ghost,
108
+ .Steel,
109
+ };
110
+
111
+ const HIDDEN_POWER = [_]Type{
112
+ .Fighting,
113
+ .Flying,
114
+ .Poison,
115
+ .Ground,
116
+ .Rock,
117
+ .Bug,
118
+ .Ghost,
119
+ .Steel,
120
+ .Fire,
121
+ .Water,
122
+ .Grass,
123
+ .Electric,
124
+ .Psychic,
125
+ .Ice,
126
+ .Dragon,
127
+ .Dark,
128
+ };
129
+
130
+ /// Order of Pokémon Showdown's types.
131
+ pub const SHOWDOWN = [_]Type{
132
+ .Fire,
133
+ .Ice,
134
+ .Steel,
135
+ .Electric,
136
+ .Ghost,
137
+ .Grass,
138
+ .Dark,
139
+ .Bug,
140
+ .Dragon,
141
+ .Fighting,
142
+ .Flying,
143
+ .Ground,
144
+ .Normal,
145
+ .Poison,
146
+ .Psychic,
147
+ .Rock,
148
+ .Water,
149
+ .@"???",
150
+ };
151
+
78
152
  comptime {
79
153
  assert(@bitSizeOf(Type) == 8);
80
154
  assert(@sizeOf(@TypeOf(CHART)) == 324);
81
155
  assert(@sizeOf(@TypeOf(PRECEDENCE)) == 18);
156
+ assert(@sizeOf(@TypeOf(INDEXES)) == 18);
157
+ assert(@sizeOf(@TypeOf(CONVERSION_2)) == 10);
158
+ assert(@sizeOf(@TypeOf(HIDDEN_POWER)) == 16);
159
+ assert(@sizeOf(@TypeOf(SHOWDOWN)) == 18);
82
160
  }
83
161
 
84
162
  /// The number of types in this generation.
85
163
  pub const size = 18;
86
164
 
87
165
  /// Whether or not this type is considered to be special as opposed to physical.
88
- pub inline fn special(self: Type) bool {
166
+ pub fn special(self: Type) bool {
89
167
  return @intFromEnum(self) >= @intFromEnum(Type.Fire);
90
168
  }
91
169
 
92
170
  /// The `Effectiveness` of type `t2` vs. type `t1`.
93
- pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
171
+ pub fn effectiveness(t1: Type, t2: Type) Effectiveness {
94
172
  return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
95
173
  }
96
174
 
97
- /// The precedence order of type type.
98
- pub inline fn precedence(self: Type) u8 {
99
- 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)];
181
+ }
182
+
183
+ /// The internal index of this Type used by Present.
184
+ pub fn present(self: Type) u8 {
185
+ return INDEXES[@intFromEnum(self)];
186
+ }
187
+
188
+ /// The Type corresponding to a random roll of `num` for Conversion 2.
189
+ pub fn conversion2(num: u8) Type {
190
+ assert(num != 6);
191
+ assert(num < 10 or num >= 20);
192
+ assert(num <= 27);
193
+ return if (num < 10) CONVERSION_2[num] else @enumFromInt(num - 10);
194
+ }
195
+
196
+ /// The Type corresponding to a Hidden Power `index`.
197
+ pub fn hiddenPower(index: u8) Type {
198
+ return HIDDEN_POWER[index];
100
199
  }
101
200
  };
102
201
 
@@ -112,12 +211,18 @@ pub const Types = extern struct {
112
211
  }
113
212
 
114
213
  /// Whether this typing is immune to type `t`.
115
- pub inline fn immune(self: Types, t: Type) bool {
214
+ pub fn immune(self: Types, t: Type) bool {
116
215
  return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
117
216
  }
118
217
 
119
218
  /// Whether this typing includes type `t`.
120
- pub inline fn includes(self: Types, t: Type) bool {
219
+ pub fn includes(self: Types, t: Type) bool {
121
220
  return self.type1 == t or self.type2 == t;
122
221
  }
222
+
223
+ /// Whether this typing is immune to damage from Sandstorm.
224
+ pub fn sandstormImmune(self: Types) bool {
225
+ return @intFromEnum(self.type1) <= @intFromEnum(Type.Steel) or
226
+ @intFromEnum(self.type2) <= @intFromEnum(Type.Steel);
227
+ }
123
228
  };
@@ -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,12 +41,14 @@ 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
 
51
48
  pub const PRNG = rng.PRNG(2);
52
49
 
53
50
  pub fn Battle(comptime RNG: anytype) type {
51
+ const padding = if (!showdown) 3 else 5;
54
52
  return extern struct {
55
53
  const Self = @This();
56
54
 
@@ -59,17 +57,19 @@ pub fn Battle(comptime RNG: anytype) type {
59
57
  turn: u16 = 0,
60
58
  field: Field = .{},
61
59
 
62
- pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
63
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
60
+ _: [padding]u8 = @splat(0),
61
+
62
+ pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
63
+ assert(isPointerTo(self, Self));
64
64
  return &self.sides[@intFromEnum(player)];
65
65
  }
66
66
 
67
- pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
68
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
67
+ pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
68
+ assert(isPointerTo(self, Self));
69
69
  return &self.sides[@intFromEnum(player.foe())];
70
70
  }
71
71
 
72
- pub inline fn active(self: *const Self, player: Player) ID {
72
+ pub fn active(self: *const Self, player: Player) ID {
73
73
  return player.ident(@intCast(self.side(player).pokemon[0].position));
74
74
  }
75
75
 
@@ -83,18 +83,13 @@ pub fn Battle(comptime RNG: anytype) type {
83
83
  };
84
84
  }
85
85
 
86
- // TODO
87
86
  test Battle {
88
- try expectEqual(496, @sizeOf(Battle(PRNG)));
87
+ try expectEqual(512, @sizeOf(Battle(PRNG)));
89
88
  }
90
89
 
91
- pub const Field = packed struct {
90
+ pub const Field = packed struct(u8) {
92
91
  weather: Weather = .None,
93
92
  weather_duration: u4 = 0,
94
-
95
- comptime {
96
- assert(@sizeOf(Field) == 1);
97
- }
98
93
  };
99
94
 
100
95
  pub const Weather = enum(u4) {
@@ -105,15 +100,15 @@ pub const Weather = enum(u4) {
105
100
  };
106
101
 
107
102
  pub const Side = extern struct {
108
- pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
109
- active: ActivePokemon = .{},
103
+ pokemon: [6]Pokemon = @splat(.{}),
104
+ active: ActivePokemon align(8) = .{},
110
105
  conditions: Conditions = .{},
111
- // NB: wCurPlayerMove / wCurEnemyMove
112
- last_selected_move: Move = .None,
113
106
  // NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
114
107
  last_used_move: Move = .None,
115
108
 
116
- const Conditions = packed struct {
109
+ _: [7]u8 = @splat(0),
110
+
111
+ const Conditions = packed struct(u32) {
117
112
  Spikes: bool = false,
118
113
  Safeguard: bool = false,
119
114
  LightScreen: bool = false,
@@ -123,51 +118,62 @@ pub const Side = extern struct {
123
118
  light_screen: u4 = 0,
124
119
  reflect: u4 = 0,
125
120
 
126
- comptime {
127
- assert(@sizeOf(Conditions) == 2);
128
- }
121
+ future_sight: FutureSight = .{},
122
+
123
+ const FutureSight = packed struct(u16) {
124
+ damage: u12 = 0,
125
+ count: u4 = 0,
126
+ };
129
127
  };
130
128
 
131
129
  comptime {
132
- assert(@sizeOf(Side) == 240);
130
+ assert(@sizeOf(Side) == 248);
133
131
  }
134
132
 
135
- pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
136
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
133
+ pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
134
+ assert(isPointerTo(self, Side));
137
135
  assert(slot > 0 and slot <= 6);
138
136
  const id = self.pokemon[slot - 1].position;
139
137
  assert(id > 0 and id <= 6);
140
138
  return &self.pokemon[id - 1];
141
139
  }
142
140
 
143
- pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
144
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
141
+ pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
142
+ assert(isPointerTo(self, Side));
145
143
  return self.get(1);
146
144
  }
147
145
 
148
- pub inline fn lastMove(self: anytype, encore: bool) Move {
146
+ pub fn lastMove(self: anytype, encore: bool) Move {
149
147
  assert(self.last_used_move != .None or !self.active.volatiles.dirty);
150
148
  if (encore) return self.last_used_move;
151
149
  return if (self.active.volatiles.dirty) .None else self.last_used_move;
152
150
  }
151
+
152
+ // TODO
153
+ // pub fn useMove(self: anytype) void {
154
+ // assert(side.last_selected_move != .None);
155
+ // self.last_used_move = side.last_selected_move;
156
+ // self.active.volatiles.dirty = false;
157
+ // }
153
158
  };
154
159
 
155
160
  // NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
156
161
  pub const ActivePokemon = extern struct {
157
162
  volatiles: Volatiles align(4) = .{},
158
163
  stats: Stats(u16) = .{},
159
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
164
+ moves: [4]MoveSlot = @splat(.{}),
160
165
  boosts: Boosts = .{},
161
166
  types: Types = .{},
162
167
  species: Species = .None,
163
- item: Item = .None,
168
+
169
+ _: u8 = 0,
164
170
 
165
171
  comptime {
166
172
  assert(@sizeOf(ActivePokemon) == 44);
167
173
  }
168
174
 
169
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
170
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
175
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
176
+ assert(isPointerTo(self, Pokemon));
171
177
  assert(mslot > 0 and mslot <= 4);
172
178
  assert(self.moves[mslot - 1].id != .None);
173
179
  return &self.moves[mslot - 1];
@@ -176,7 +182,7 @@ pub const ActivePokemon = extern struct {
176
182
 
177
183
  pub const Pokemon = extern struct {
178
184
  stats: Stats(u16) = .{},
179
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
185
+ moves: [4]MoveSlot = @splat(.{}),
180
186
  types: Types = .{},
181
187
  dvs: DVs = .{},
182
188
  hp: u16 = 0,
@@ -191,8 +197,8 @@ pub const Pokemon = extern struct {
191
197
  assert(@sizeOf(Pokemon) == 32);
192
198
  }
193
199
 
194
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
195
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
200
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
201
+ assert(isPointerTo(self, Pokemon));
196
202
  assert(mslot > 0 and mslot <= 4);
197
203
  assert(self.moves[mslot - 1].id != .None);
198
204
  return &self.moves[mslot - 1];
@@ -205,7 +211,7 @@ pub const Gender = enum(u2) {
205
211
  Unknown,
206
212
  };
207
213
 
208
- pub const DVs = packed struct {
214
+ pub const DVs = packed struct(u16) {
209
215
  gender: Gender = .Male,
210
216
  pow: u6 = 40,
211
217
  type: Type = .Dark,
@@ -213,10 +219,10 @@ pub const DVs = packed struct {
213
219
  pub fn init(g: Gender, p: u6, t: Type) DVs {
214
220
  assert(p >= 1 and p <= 40);
215
221
  assert(t != .Normal and t != .@"???");
216
- return DVs{ .gender = g, .pow = p, .type = t };
222
+ return .{ .gender = g, .pow = p, .type = t };
217
223
  }
218
224
 
219
- pub inline fn power(dvs: DVs) u8 {
225
+ pub fn power(dvs: DVs) u8 {
220
226
  return @as(u8, dvs.pow) + 30;
221
227
  }
222
228
 
@@ -230,13 +236,8 @@ pub const DVs = packed struct {
230
236
  };
231
237
  const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
232
238
  ((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
233
- var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
234
- if (t >= @intFromEnum(Type.@"???")) t = t + 1;
235
- return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
236
- }
237
-
238
- comptime {
239
- assert(@sizeOf(DVs) == 2);
239
+ const t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
240
+ return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
240
241
  }
241
242
  };
242
243
 
@@ -261,10 +262,12 @@ test DVs {
261
262
  try expectEqual(Gender.Female, dvs.gender);
262
263
  }
263
264
 
264
- pub const MoveSlot = packed struct {
265
+ /// Representation of a Generation II Pokémon's move slot in a battle.
266
+ pub const MoveSlot = extern struct {
267
+ /// The identifier for the move.
265
268
  id: Move = .None,
266
- pp_ups: u2 = 3,
267
- pp: u6 = 0,
269
+ /// The remaining PP of the move.
270
+ pp: u8 = 0,
268
271
 
269
272
  comptime {
270
273
  assert(@sizeOf(MoveSlot) == 2);
@@ -273,14 +276,19 @@ pub const MoveSlot = packed struct {
273
276
 
274
277
  pub const Status = gen1.Status;
275
278
 
276
- pub const Volatiles = packed struct {
279
+ /// TODO
280
+ pub const TriAttack = enum(u2) { PAR, FRZ, BRN };
281
+
282
+ pub const Volatiles = packed struct(u128) {
277
283
  Bide: bool = false,
278
284
  Thrashing: bool = false,
285
+ BeatUp: bool = false,
279
286
  Flinch: bool = false,
280
287
  Charging: bool = false,
281
288
  Underground: bool = false,
282
289
  Flying: bool = false,
283
290
  Confusion: bool = false,
291
+
284
292
  Mist: bool = false,
285
293
  FocusEnergy: bool = false,
286
294
  Substitute: bool = false,
@@ -291,53 +299,55 @@ pub const Volatiles = packed struct {
291
299
  Transform: bool = false,
292
300
 
293
301
  Nightmare: bool = false,
294
- Cure: bool = false,
302
+ Curse: bool = false,
295
303
  Protect: bool = false,
296
304
  Foresight: bool = false,
297
305
  PerishSong: bool = false,
298
306
  Endure: bool = false,
299
307
  Rollout: bool = false,
300
308
  Attract: bool = false,
309
+
301
310
  DefenseCurl: bool = false,
302
311
  Encore: bool = false,
303
312
  LockOn: bool = false,
304
313
  DestinyBond: bool = false,
305
- BeatUp: bool = false,
314
+ Trapped: bool = false,
306
315
 
307
- trapped: bool = false,
308
316
  switched: bool = false,
317
+ // TODO: replace with simply looking at Choice on Battle
309
318
  switching: bool = false,
319
+ // TODO: simply store both last moves on side
310
320
  dirty: bool = false,
311
321
 
312
- _: u4 = 0,
322
+ frozen: bool = false,
323
+ minimized: bool = false,
324
+ _: u10 = 0,
325
+ // TODO: is this needed for unmodified stats?
326
+ transform: u4 = 0,
313
327
 
314
- bind: u4 = 0,
315
- future_sight: FutureSight = .{},
316
328
  bide: u16 = 0,
317
- disabled: Disabled = .{},
329
+ bind: Bind = .{},
330
+ disable: Disable = .{},
318
331
  rage: u8 = 0,
319
332
  substitute: u8 = 0,
320
333
  toxic: u8 = 0,
321
334
  confusion: u4 = 0,
322
335
  encore: u4 = 0,
336
+ // TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
323
337
  fury_cutter: u4 = 0,
324
338
  perish_song: u4 = 0,
325
339
  protect: u4 = 0,
326
- rollout: u4 = 0,
340
+ attacks: u4 = 0, // bide/thrashing/rollout turns
327
341
 
328
- const Disabled = packed struct {
329
- move: u4 = 0,
342
+ const Bind = packed struct(u8) {
330
343
  duration: u4 = 0,
344
+ reason: u4 = 0,
331
345
  };
332
346
 
333
- const FutureSight = packed struct {
334
- damage: u12 = 0,
335
- count: u4 = 0,
347
+ const Disable = packed struct(u8) {
348
+ move: u4 = 0,
349
+ duration: u4 = 0,
336
350
  };
337
-
338
- comptime {
339
- assert(@sizeOf(Volatiles) == 16);
340
- }
341
351
  };
342
352
 
343
353
  pub fn Stats(comptime T: type) type {
@@ -372,7 +382,7 @@ test Stats {
372
382
  try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
373
383
  }
374
384
 
375
- pub const Boosts = packed struct {
385
+ pub const Boosts = packed struct(u32) {
376
386
  atk: i4 = 0,
377
387
  def: i4 = 0,
378
388
  spe: i4 = 0,
@@ -388,7 +398,7 @@ pub const Boosts = packed struct {
388
398
  };
389
399
 
390
400
  test Boosts {
391
- const boosts = Boosts{ .spd = -6 };
401
+ const boosts: Boosts = .{ .spd = -6 };
392
402
  try expectEqual(0, boosts.atk);
393
403
  try expectEqual(-6, boosts.spd);
394
404
  }
@@ -401,15 +411,10 @@ test Item {
401
411
  try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
402
412
  try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
403
413
 
404
- try expect(!Item.mail(.UpGrade));
405
- try expect(Item.mail(.FlowerMail));
406
- try expect(Item.mail(.MirageMail));
407
- try expect(!Item.mail(.TownMap));
408
-
409
- try expect(!Item.berry(.LightBall));
410
- try expect(Item.berry(.PSNCureBerry));
411
- try expect(Item.berry(.GoldBerry));
412
- try expect(!Item.berry(.MasterBall));
414
+ try expect(!Item.isMail(.UpGrade));
415
+ try expect(Item.isMail(.FlowerMail));
416
+ try expect(Item.isMail(.MirageMail));
417
+ try expect(!Item.isMail(.TownMap));
413
418
  }
414
419
 
415
420
  pub const Move = moves.Move;
@@ -418,9 +423,13 @@ test Move {
418
423
  try expectEqual(251, @intFromEnum(Move.BeatUp));
419
424
  const move = Move.get(.DynamicPunch);
420
425
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
421
- try expectEqual(@as(u8, 50), move.accuracy);
422
- try expectEqual(@as(u8, 5), move.pp);
426
+ try expectEqual(rng.Gen12.percent(50), move.accuracy);
427
+ try expectEqual(rng.Gen12.percent(100), move.chance);
428
+ try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
423
429
  try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
430
+ try expect(!move.effect.isHighCritical());
431
+ try expect(Move.get(.KarateChop).effect.isHighCritical());
432
+ try expect(Move.get(.RazorWind).effect.isHighCritical());
424
433
  }
425
434
 
426
435
  pub const Species = species.Species;
@@ -440,8 +449,9 @@ test Types {
440
449
  try expect(!Type.Steel.special());
441
450
  try expect(Type.Dark.special());
442
451
 
443
- try expectEqual(@as(u8, 3), Type.Water.precedence());
444
- 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));
445
455
 
446
456
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
447
457
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Water, .Fire));