@pkmn/engine 0.1.0-dev.8b83af6b → 0.1.0-dev.8cf7e679

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 (83) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +100 -61
  3. package/build/pkg/addon/browser.d.ts +3 -0
  4. package/build/pkg/addon/browser.js +44 -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 +97 -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 +44 -0
  11. package/build/pkg/addon/wasm.js.map +1 -0
  12. package/build/pkg/addon.d.ts +25 -3
  13. package/build/pkg/addon.js +39 -70
  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 +39 -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 +13 -17
  21. package/build/pkg/gen1.js +21 -28
  22. package/build/pkg/gen1.js.map +1 -1
  23. package/build/pkg/index.d.ts +40 -91
  24. package/build/pkg/index.js +6 -63
  25. package/build/pkg/index.js.map +1 -1
  26. package/build/pkg/protocol.d.ts +20 -1
  27. package/build/pkg/protocol.js +123 -102
  28. package/build/pkg/protocol.js.map +1 -1
  29. package/build/tools/debug.js +36 -69
  30. package/build/tools/debug.js.map +1 -1
  31. package/build/tools/display/debug.jsx +141 -0
  32. package/build/tools/display/debug.jsx.map +1 -0
  33. package/build/tools/display/index.js +141 -0
  34. package/build/tools/display/index.js.map +1 -0
  35. package/build/tools/display/ui.jsx +349 -0
  36. package/build/tools/display/ui.jsx.map +1 -0
  37. package/build/tools/display/util.js +18 -0
  38. package/build/tools/display/util.js.map +1 -0
  39. package/build.zig +332 -132
  40. package/package.json +66 -121
  41. package/src/bin/install-pkmn-engine +40 -25
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/common/array.zig +77 -0
  44. package/src/lib/common/battle.zig +17 -0
  45. package/src/lib/common/data.zig +12 -24
  46. package/src/lib/common/debug.zig +58 -18
  47. package/src/lib/common/js.zig +120 -0
  48. package/src/lib/common/optional.zig +71 -0
  49. package/src/lib/common/options.zig +15 -0
  50. package/src/lib/common/protocol.zig +851 -327
  51. package/src/lib/common/rational.zig +449 -0
  52. package/src/lib/common/rng.zig +26 -19
  53. package/src/lib/common/util.zig +61 -0
  54. package/src/lib/gen1/calc.zig +953 -0
  55. package/src/lib/gen1/chance.zig +1185 -0
  56. package/src/lib/gen1/data/moves.zig +193 -183
  57. package/src/lib/gen1/data/species.zig +4 -6
  58. package/src/lib/gen1/data/types.zig +8 -9
  59. package/src/lib/gen1/data.zig +79 -73
  60. package/src/lib/gen1/helpers.zig +130 -30
  61. package/src/lib/gen1/mechanics.zig +1137 -779
  62. package/src/lib/gen2/calc.zig +153 -0
  63. package/src/lib/gen2/chance.zig +902 -0
  64. package/src/lib/gen2/data/items.zig +13 -21
  65. package/src/lib/gen2/data/moves.zig +664 -572
  66. package/src/lib/gen2/data/species.zig +2 -4
  67. package/src/lib/gen2/data/types.zig +135 -33
  68. package/src/lib/gen2/data.zig +121 -87
  69. package/src/lib/gen2/generated.zig +801 -0
  70. package/src/lib/gen2/helpers.zig +54 -39
  71. package/src/lib/gen2/mechanics.zig +3582 -30
  72. package/src/lib/node.zig +129 -0
  73. package/src/lib/pkmn.zig +85 -5
  74. package/src/lib/wasm.zig +48 -0
  75. package/src/tools/display/dom.ts +96 -0
  76. package/src/tools/display/ui.css +354 -0
  77. package/build/test/display.js +0 -400
  78. package/build/test/display.js.map +0 -1
  79. package/src/lib/binding/node.zig +0 -226
  80. package/src/lib/binding/wasm.zig +0 -10
  81. package/src/lib/gen1/README.md +0 -814
  82. package/src/lib/gen2/README.md +0 -309
  83. 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,29 +74,126 @@ 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 {
175
+ /// The precedence order of Type `type`.
176
+ pub fn precedence(self: Type) u8 {
99
177
  return PRECEDENCE[@intFromEnum(self)];
100
178
  }
179
+
180
+ /// The internal index of this Type used by Present.
181
+ pub fn present(self: Type) u8 {
182
+ return INDEXES[@intFromEnum(self)];
183
+ }
184
+
185
+ /// The Type corresponding to a random roll of `num` for Conversion 2.
186
+ pub fn conversion2(num: u8) Type {
187
+ assert(num != 6);
188
+ assert(num < 10 or num >= 20);
189
+ assert(num <= 27);
190
+ return if (num < 10) CONVERSION_2[num] else @enumFromInt(num - 10);
191
+ }
192
+
193
+ /// The Type corresponding to a Hidden Power `index`.
194
+ pub fn hiddenPower(index: u8) Type {
195
+ return HIDDEN_POWER[index];
196
+ }
101
197
  };
102
198
 
103
199
  /// Representation of a Pokémon's typing.
@@ -112,12 +208,18 @@ pub const Types = extern struct {
112
208
  }
113
209
 
114
210
  /// Whether this typing is immune to type `t`.
115
- pub inline fn immune(self: Types, t: Type) bool {
211
+ pub fn immune(self: Types, t: Type) bool {
116
212
  return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
117
213
  }
118
214
 
119
215
  /// Whether this typing includes type `t`.
120
- pub inline fn includes(self: Types, t: Type) bool {
216
+ pub fn includes(self: Types, t: Type) bool {
121
217
  return self.type1 == t or self.type2 == t;
122
218
  }
219
+
220
+ /// Whether this typing is immune to damage from Sandstorm.
221
+ pub fn sandstormImmune(self: Types) bool {
222
+ return @intFromEnum(self.type1) <= @intFromEnum(Type.Steel) or
223
+ @intFromEnum(self.type2) <= @intFromEnum(Type.Steel);
224
+ }
123
225
  };
@@ -1,19 +1,20 @@
1
- const std = @import("std");
2
- const builtin = @import("builtin");
3
-
4
- const data = @import("../common/data.zig");
5
1
  const DEBUG = @import("../common/debug.zig").print;
6
- const options = @import("../common/options.zig");
7
- const rng = @import("../common/rng.zig");
8
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");
9
8
  const items = @import("data/items.zig");
9
+ const mechanics = @import("mechanics.zig");
10
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");
11
14
  const species = @import("data/species.zig");
15
+ const std = @import("std");
12
16
  const types = @import("data/types.zig");
13
-
14
- const gen1 = @import("../gen1/data.zig");
15
-
16
- const mechanics = @import("mechanics.zig");
17
+ const util = @import("../common/util.zig");
17
18
 
18
19
  const assert = std.debug.assert;
19
20
  const expectEqual = std.testing.expectEqual;
@@ -31,16 +32,23 @@ pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
31
32
  else
32
33
  std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
33
34
 
35
+ /// Null object pattern implementation of pkmn.battle.Options for Generation II.
36
+ pub const NULL = pkmn.battle.options(protocol.NULL, chance.NULL, calc.NULL);
37
+
34
38
  const Choice = data.Choice;
35
39
  const ID = data.ID;
36
40
  const Player = data.Player;
37
41
  const Result = data.Result;
38
42
 
39
- const showdown = options.showdown;
43
+ const PointerType = util.PointerType;
44
+ const isPointerTo = util.isPointerTo;
45
+
46
+ const showdown = pkmn.options.showdown;
40
47
 
41
48
  pub const PRNG = rng.PRNG(2);
42
49
 
43
50
  pub fn Battle(comptime RNG: anytype) type {
51
+ const padding = if (!showdown) 3 else 5;
44
52
  return extern struct {
45
53
  const Self = @This();
46
54
 
@@ -49,20 +57,24 @@ pub fn Battle(comptime RNG: anytype) type {
49
57
  turn: u16 = 0,
50
58
  field: Field = .{},
51
59
 
52
- pub inline fn side(self: *Self, player: Player) *Side {
60
+ _: [padding]u8 = .{0} ** padding,
61
+
62
+ pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
63
+ assert(isPointerTo(self, Self));
53
64
  return &self.sides[@intFromEnum(player)];
54
65
  }
55
66
 
56
- pub inline fn foe(self: *Self, player: Player) *Side {
67
+ pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
68
+ assert(isPointerTo(self, Self));
57
69
  return &self.sides[@intFromEnum(player.foe())];
58
70
  }
59
71
 
60
- pub inline fn active(self: *Self, player: Player) ID {
72
+ pub fn active(self: *const Self, player: Player) ID {
61
73
  return player.ident(@intCast(self.side(player).pokemon[0].position));
62
74
  }
63
75
 
64
- pub fn update(self: *Self, c1: Choice, c2: Choice, log: anytype) !Result {
65
- return mechanics.update(self, c1, c2, log);
76
+ pub fn update(self: *Self, c1: Choice, c2: Choice, options: anytype) !Result {
77
+ return mechanics.update(self, c1, c2, options);
66
78
  }
67
79
 
68
80
  pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
@@ -71,35 +83,32 @@ pub fn Battle(comptime RNG: anytype) type {
71
83
  };
72
84
  }
73
85
 
74
- // TODO
75
86
  test Battle {
76
- try expectEqual(496, @sizeOf(Battle(PRNG)));
87
+ try expectEqual(512, @sizeOf(Battle(PRNG)));
77
88
  }
78
89
 
79
- const Field = packed struct {
90
+ pub const Field = packed struct(u8) {
80
91
  weather: Weather = .None,
81
92
  weather_duration: u4 = 0,
82
-
83
- comptime {
84
- assert(@sizeOf(Field) == 1);
85
- }
86
93
  };
87
94
 
88
- const Weather = enum(u4) {
95
+ pub const Weather = enum(u4) {
89
96
  None,
90
97
  Rain,
91
98
  Sun,
92
99
  Sandstorm,
93
100
  };
94
101
 
95
- const Side = extern struct {
102
+ pub const Side = extern struct {
96
103
  pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
97
- active: ActivePokemon = .{},
98
- conditions: Conditions,
104
+ active: ActivePokemon align(8) = .{},
105
+ conditions: Conditions = .{},
106
+ // NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
99
107
  last_used_move: Move = .None,
100
- _: u8 = 0,
101
108
 
102
- const Conditions = packed struct {
109
+ _: [7]u8 = .{0} ** 7,
110
+
111
+ const Conditions = packed struct(u32) {
103
112
  Spikes: bool = false,
104
113
  Safeguard: bool = false,
105
114
  LightScreen: bool = false,
@@ -109,50 +118,69 @@ const Side = extern struct {
109
118
  light_screen: u4 = 0,
110
119
  reflect: u4 = 0,
111
120
 
112
- comptime {
113
- assert(@sizeOf(Conditions) == 2);
114
- }
121
+ future_sight: FutureSight = .{},
122
+
123
+ const FutureSight = packed struct(u16) {
124
+ damage: u12 = 0,
125
+ count: u4 = 0,
126
+ };
115
127
  };
116
128
 
117
129
  comptime {
118
- assert(@sizeOf(Side) == 240);
130
+ assert(@sizeOf(Side) == 248);
119
131
  }
120
132
 
121
- pub inline fn get(self: *const Side, slot: u8) *Pokemon {
133
+ pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
134
+ assert(isPointerTo(self, Side));
122
135
  assert(slot > 0 and slot <= 6);
123
136
  const id = self.pokemon[slot - 1].position;
124
137
  assert(id > 0 and id <= 6);
125
138
  return &self.pokemon[id - 1];
126
139
  }
127
140
 
128
- pub inline fn stored(self: *Side) *Pokemon {
141
+ pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
142
+ assert(isPointerTo(self, Side));
129
143
  return self.get(1);
130
144
  }
145
+
146
+ pub fn lastMove(self: anytype, encore: bool) Move {
147
+ assert(self.last_used_move != .None or !self.active.volatiles.dirty);
148
+ if (encore) return self.last_used_move;
149
+ return if (self.active.volatiles.dirty) .None else self.last_used_move;
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
+ // }
131
158
  };
132
159
 
133
160
  // NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
134
- const ActivePokemon = extern struct {
135
- volatiles: Volatile align(4) = .{},
161
+ pub const ActivePokemon = extern struct {
162
+ volatiles: Volatiles align(4) = .{},
136
163
  stats: Stats(u16) = .{},
137
164
  moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
138
165
  boosts: Boosts = .{},
166
+ types: Types = .{},
139
167
  species: Species = .None,
140
- item: Item = .None,
141
- last_move: Move = .None,
168
+
142
169
  _: u8 = 0,
143
170
 
144
171
  comptime {
145
172
  assert(@sizeOf(ActivePokemon) == 44);
146
173
  }
147
174
 
148
- pub inline fn move(self: *ActivePokemon, mslot: u8) *MoveSlot {
175
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
176
+ assert(isPointerTo(self, Pokemon));
149
177
  assert(mslot > 0 and mslot <= 4);
150
178
  assert(self.moves[mslot - 1].id != .None);
151
179
  return &self.moves[mslot - 1];
152
180
  }
153
181
  };
154
182
 
155
- const Pokemon = extern struct {
183
+ pub const Pokemon = extern struct {
156
184
  stats: Stats(u16) = .{},
157
185
  moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
158
186
  types: Types = .{},
@@ -169,7 +197,8 @@ const Pokemon = extern struct {
169
197
  assert(@sizeOf(Pokemon) == 32);
170
198
  }
171
199
 
172
- pub inline fn move(self: *Pokemon, mslot: u8) *MoveSlot {
200
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
201
+ assert(isPointerTo(self, Pokemon));
173
202
  assert(mslot > 0 and mslot <= 4);
174
203
  assert(self.moves[mslot - 1].id != .None);
175
204
  return &self.moves[mslot - 1];
@@ -182,7 +211,7 @@ pub const Gender = enum(u2) {
182
211
  Unknown,
183
212
  };
184
213
 
185
- const DVs = packed struct {
214
+ pub const DVs = packed struct(u16) {
186
215
  gender: Gender = .Male,
187
216
  pow: u6 = 40,
188
217
  type: Type = .Dark,
@@ -190,10 +219,10 @@ const DVs = packed struct {
190
219
  pub fn init(g: Gender, p: u6, t: Type) DVs {
191
220
  assert(p >= 1 and p <= 40);
192
221
  assert(t != .Normal and t != .@"???");
193
- return DVs{ .gender = g, .pow = p, .type = t };
222
+ return .{ .gender = g, .pow = p, .type = t };
194
223
  }
195
224
 
196
- pub inline fn power(dvs: DVs) u8 {
225
+ pub fn power(dvs: DVs) u8 {
197
226
  return @as(u8, dvs.pow) + 30;
198
227
  }
199
228
 
@@ -207,13 +236,8 @@ const DVs = packed struct {
207
236
  };
208
237
  const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
209
238
  ((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
210
- var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
211
- if (t >= @intFromEnum(Type.@"???")) t = t + 1;
212
- return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
213
- }
214
-
215
- comptime {
216
- 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) };
217
241
  }
218
242
  };
219
243
 
@@ -238,10 +262,12 @@ test DVs {
238
262
  try expectEqual(Gender.Female, dvs.gender);
239
263
  }
240
264
 
241
- 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.
242
268
  id: Move = .None,
243
- pp_ups: u2 = 3,
244
- pp: u6 = 0,
269
+ /// The remaining PP of the move.
270
+ pp: u8 = 0,
245
271
 
246
272
  comptime {
247
273
  assert(@sizeOf(MoveSlot) == 2);
@@ -250,14 +276,19 @@ const MoveSlot = packed struct {
250
276
 
251
277
  pub const Status = gen1.Status;
252
278
 
253
- const Volatile = packed struct {
279
+ /// TODO
280
+ pub const TriAttack = enum(u2) { PAR, FRZ, BRN };
281
+
282
+ pub const Volatiles = packed struct(u128) {
254
283
  Bide: bool = false,
255
284
  Thrashing: bool = false,
285
+ BeatUp: bool = false,
256
286
  Flinch: bool = false,
257
287
  Charging: bool = false,
258
288
  Underground: bool = false,
259
289
  Flying: bool = false,
260
290
  Confusion: bool = false,
291
+
261
292
  Mist: bool = false,
262
293
  FocusEnergy: bool = false,
263
294
  Substitute: bool = false,
@@ -268,52 +299,55 @@ const Volatile = packed struct {
268
299
  Transform: bool = false,
269
300
 
270
301
  Nightmare: bool = false,
271
- Cure: bool = false,
302
+ Curse: bool = false,
272
303
  Protect: bool = false,
273
304
  Foresight: bool = false,
274
305
  PerishSong: bool = false,
275
306
  Endure: bool = false,
276
307
  Rollout: bool = false,
277
308
  Attract: bool = false,
309
+
278
310
  DefenseCurl: bool = false,
279
311
  Encore: bool = false,
280
312
  LockOn: bool = false,
281
313
  DestinyBond: bool = false,
282
- BeatUp: bool = false,
314
+ Trapped: bool = false,
283
315
 
284
- trapped: bool = false,
285
316
  switched: bool = false,
317
+ // TODO: replace with simply looking at Choice on Battle
286
318
  switching: bool = false,
319
+ // TODO: simply store both last moves on side
320
+ dirty: bool = false,
287
321
 
288
- _: u5 = 0,
322
+ frozen: bool = false,
323
+ minimized: bool = false,
324
+ _: u10 = 0,
325
+ // TODO: is this needed for unmodified stats?
326
+ transform: u4 = 0,
289
327
 
290
- bind: u4 = 0,
291
- future_sight: FutureSight = .{},
292
328
  bide: u16 = 0,
293
- disabled: Disabled = .{},
329
+ bind: Bind = .{},
330
+ disable: Disable = .{},
294
331
  rage: u8 = 0,
295
332
  substitute: u8 = 0,
296
333
  toxic: u8 = 0,
297
334
  confusion: u4 = 0,
298
335
  encore: u4 = 0,
336
+ // TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
299
337
  fury_cutter: u4 = 0,
300
338
  perish_song: u4 = 0,
301
339
  protect: u4 = 0,
302
- rollout: u4 = 0,
340
+ attacks: u4 = 0, // bide/thrashing/rollout turns
303
341
 
304
- const Disabled = packed struct {
305
- move: u4 = 0,
342
+ const Bind = packed struct(u8) {
306
343
  duration: u4 = 0,
344
+ reason: u4 = 0,
307
345
  };
308
346
 
309
- const FutureSight = packed struct {
310
- damage: u12 = 0,
311
- count: u4 = 0,
347
+ const Disable = packed struct(u8) {
348
+ move: u4 = 0,
349
+ duration: u4 = 0,
312
350
  };
313
-
314
- comptime {
315
- assert(@sizeOf(Volatile) == 16);
316
- }
317
351
  };
318
352
 
319
353
  pub fn Stats(comptime T: type) type {
@@ -328,7 +362,7 @@ pub fn Stats(comptime T: type) type {
328
362
  pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
329
363
  assert(level > 0 and level <= 100);
330
364
  const float: f32 = @floatFromInt(exp);
331
- const evs = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
365
+ const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
332
366
  const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
333
367
  const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
334
368
  return @intCast(core *% @as(u32, level) / 100 +% factor);
@@ -348,7 +382,7 @@ test Stats {
348
382
  try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
349
383
  }
350
384
 
351
- pub const Boosts = packed struct {
385
+ pub const Boosts = packed struct(u32) {
352
386
  atk: i4 = 0,
353
387
  def: i4 = 0,
354
388
  spe: i4 = 0,
@@ -364,7 +398,7 @@ pub const Boosts = packed struct {
364
398
  };
365
399
 
366
400
  test Boosts {
367
- const boosts = Boosts{ .spd = -6 };
401
+ const boosts: Boosts = .{ .spd = -6 };
368
402
  try expectEqual(0, boosts.atk);
369
403
  try expectEqual(-6, boosts.spd);
370
404
  }
@@ -377,15 +411,10 @@ test Item {
377
411
  try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
378
412
  try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
379
413
 
380
- try expect(!Item.mail(.UpGrade));
381
- try expect(Item.mail(.FlowerMail));
382
- try expect(Item.mail(.MirageMail));
383
- try expect(!Item.mail(.TownMap));
384
-
385
- try expect(!Item.berry(.LightBall));
386
- try expect(Item.berry(.PSNCureBerry));
387
- try expect(Item.berry(.GoldBerry));
388
- 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));
389
418
  }
390
419
 
391
420
  pub const Move = moves.Move;
@@ -394,8 +423,13 @@ test Move {
394
423
  try expectEqual(251, @intFromEnum(Move.BeatUp));
395
424
  const move = Move.get(.DynamicPunch);
396
425
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
397
- try expectEqual(@as(u8, 50), move.accuracy);
398
- 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));
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());
399
433
  }
400
434
 
401
435
  pub const Species = species.Species;