@pkmn/engine 0.1.0-dev.c5fe6d07 → 0.1.0-dev.cdc837af

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