@pkmn/engine 0.1.0-dev.979a393a → 0.1.0-dev.a02611a4

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 (49) hide show
  1. package/README.md +71 -60
  2. package/build/pkg/addon.d.ts +7 -0
  3. package/build/pkg/addon.js +112 -0
  4. package/build/pkg/addon.js.map +1 -0
  5. package/build/pkg/data/layout.json +25 -3
  6. package/build/pkg/gen1.d.ts +5 -4
  7. package/build/pkg/gen1.js +47 -31
  8. package/build/pkg/gen1.js.map +1 -1
  9. package/build/pkg/index.d.ts +15 -12
  10. package/build/pkg/index.js.map +1 -1
  11. package/build/pkg/protocol.d.ts +4 -2
  12. package/build/pkg/protocol.js +4 -2
  13. package/build/pkg/protocol.js.map +1 -1
  14. package/build/test/display.js +31 -13
  15. package/build/test/display.js.map +1 -1
  16. package/build.zig +95 -76
  17. package/package.json +29 -16
  18. package/src/bin/install-pkmn-engine +11 -6
  19. package/src/bin/pkmn-debug +29 -6
  20. package/src/lib/binding/node.zig +46 -141
  21. package/src/lib/binding/wasm.zig +6 -4
  22. package/src/lib/common/battle.zig +17 -0
  23. package/src/lib/common/data.zig +11 -11
  24. package/src/lib/common/debug.zig +21 -11
  25. package/src/lib/common/js.zig +118 -0
  26. package/src/lib/common/optional.zig +60 -0
  27. package/src/lib/common/options.zig +12 -3
  28. package/src/lib/common/protocol.zig +308 -182
  29. package/src/lib/common/rational.zig +353 -0
  30. package/src/lib/common/rng.zig +10 -10
  31. package/src/lib/common/util.zig +12 -0
  32. package/src/lib/gen1/README.md +262 -154
  33. package/src/lib/gen1/calc.zig +426 -0
  34. package/src/lib/gen1/chance.zig +1044 -0
  35. package/src/lib/gen1/data/moves.zig +10 -10
  36. package/src/lib/gen1/data/species.zig +2 -2
  37. package/src/lib/gen1/data/types.zig +3 -3
  38. package/src/lib/gen1/data.zig +69 -43
  39. package/src/lib/gen1/helpers.zig +592 -32
  40. package/src/lib/gen1/mechanics.zig +983 -626
  41. package/src/lib/gen2/README.md +12 -13
  42. package/src/lib/gen2/data/items.zig +3 -3
  43. package/src/lib/gen2/data/moves.zig +16 -3
  44. package/src/lib/gen2/data/species.zig +1 -1
  45. package/src/lib/gen2/data/types.zig +3 -3
  46. package/src/lib/gen2/data.zig +59 -41
  47. package/src/lib/gen2/helpers.zig +15 -16
  48. package/src/lib/gen2/mechanics.zig +354 -11
  49. package/src/lib/pkmn.zig +68 -9
@@ -54,13 +54,12 @@ The following information is required to simulate a Generation II Pokémon battl
54
54
  | | | |
55
55
  | `active.last_used_move` | `PlayerUsedMove` | `side.lastMove` |
56
56
 
57
- - Pokémon Showdown does not implement the correct Generation II RNG and as such its `seed` is
57
+ - Pokémon Showdown doesn't implement the correct Generation II RNG and as such its `seed` is
58
58
  different
59
- - `battle.turn` only needs to be tracked in order to be compatible with the Pokémon Showdown
60
- protocol
59
+ - `battle.turn` only needs to be tracked to be compatible with the Pokémon Showdown protocol
61
60
  - Battle results (win, lose, draw) or request states are communicated via the return code of
62
61
  `Battle.update`
63
- - Nicknames (`BattleMonNick`/`pokemon.name`) are not handled by the pkmn engine as they are expected
62
+ - Nicknames (`BattleMonNick`/`pokemon.name`) aren't handled by the pkmn engine as they're expected
64
63
  to be handled by driver code if required
65
64
 
66
65
  ==**TODO**==
@@ -77,9 +76,9 @@ between the data structure for the "active" Pokémon and its party members (see
77
76
 
78
77
  ### `Pokemon` / `ActivePokemon`
79
78
 
80
- Similar to the cartridge, in order to save space different information is stored depending on
81
- whether a [Pokémon](https://pkmn.cc/bulba/Pok%c3%a9mon_data_structure_%28Generation_II%29) is
82
- actively participating in battle vs. is switched out (pret's [`battle_struct` vs.
79
+ Similar to the cartridge, to save space different information is stored depending on whether a
80
+ [Pokémon](https://pkmn.cc/bulba/Pok%c3%a9mon_data_structure_%28Generation_II%29) is actively
81
+ participating in battle vs. is switched out (pret's [`battle_struct` vs.
83
82
  `party_struct`](https://pkmn.cc/pokecrystal/macros/ram.asm)). In Pokémon Showdown, all the Pokemon
84
83
  are represented by the same
85
84
  [`Pokemon`](https://github.com/smogon/pokemon-showdown/blob/master/sim/pokemon.ts) class, and static
@@ -93,14 +92,14 @@ A `MoveSlot` is a data-type for a `(move, current pp)` pair. A pared down versio
93
92
  Showdown's `Pokemon#moveSlot`, it also stores data from the cartridge's `battle_struct::Move` macro
94
93
  and can be used to replace the `PlayerMove*` data. Move PP is stored as a full byte instead of how
95
94
  the cartridge (`battle_struct::PP`) stores it (6 bits for current PP and the remaining 2 bits used
96
- to store the number of applied PP Ups). PP Up bits do not actually need to be stored on move slot
95
+ to store the number of applied PP Ups). PP Up bits don't actually need to be stored on move slot
97
96
  as max PP is never relevant in Generation II (the Mystery Berry always increases PP from 0 -> 5).
98
97
 
99
98
  #### `Status`
100
99
 
101
100
  Bitfield representation of a Pokémon's major [status
102
- condition](https://pkmn.cc/bulba/Status_condition), mirroring how it is stored on the cartridge. A
103
- value of `0x00` means that the Pokémon is not affected by any major status, otherwise the lower 3
101
+ condition](https://pkmn.cc/bulba/Status_condition), mirroring how it's stored on the cartridge. A
102
+ value of `0x00` means that the Pokémon isn't affected by any major status, otherwise the lower 3
104
103
  bits represent the remaining duration for Sleep. Other status are denoted by the presence of
105
104
  individual bits - at most one status should be set at any given time.
106
105
 
@@ -161,7 +160,7 @@ boosts should always range from `-6`...`6` instead of `1`...`13` as on the cartr
161
160
 
162
161
  `Moves` serves as an identifier for a unique [Pokémon move](https://pkmn.cc/bulba/Move) that can be
163
162
  used to retrieve a `Move` with information regarding base power, accuracy and type. As covered
164
- above, PP information isn't strictly necessary in Generation I, but fits neatly into the 4 bits of
163
+ earlier, PP information isn't strictly necessary in Generation I, but fits neatly into the 4 bits of
165
164
  padding after noticing that all PP values are multiples of 5. `Moves.None` exists as a special
166
165
  sentinel value to indicate `null`.
167
166
 
@@ -215,8 +214,8 @@ entropy](https://en.wikipedia.org/wiki/Entropy_(information_theory))) is as foll
215
214
  | **wrap** | 0...5 | 3 | | **perish song** | 0...4 | 3 |
216
215
  | **item** | TODO | 5? | | **gender** | 0..1 | 1 |
217
216
 
218
- From this we can determine the minimum bits required to store each data structure to determine how
219
- much overhead the representations above have after taking into consideration [alignment &
217
+ From this one can determine the minimum bits required to store each data structure to determine how
218
+ much overhead the preceding representations have after taking into consideration [alignment &
220
219
  padding](https://en.wikipedia.org/wiki/Data_structure_alignment) and
221
220
  [denormalization](https://en.wikipedia.org/wiki/Denormalization):
222
221
 
@@ -219,18 +219,18 @@ pub const Item = enum(u8) {
219
219
  pub inline fn boost(item: Item) ?Type {
220
220
  assert(item != .None);
221
221
  if (item == .PolkadotBow) return .Normal;
222
- return if (@enumToInt(item) <= 18) @intToEnum(Type, @enumToInt(item) - 1) else null;
222
+ return if (@intFromEnum(item) <= 18) @enumFromInt(@intFromEnum(item) - 1) else null;
223
223
  }
224
224
 
225
225
  /// Whether or not this item is a Berry.
226
226
  pub inline fn berry(item: Item) bool {
227
227
  assert(item != .None);
228
- return @enumToInt(item) > 30 and @enumToInt(item) <= 40;
228
+ return @intFromEnum(item) > 30 and @intFromEnum(item) <= 40;
229
229
  }
230
230
 
231
231
  /// Whether or not this item is Mail.
232
232
  pub inline fn mail(item: Item) bool {
233
233
  assert(item != .None);
234
- return @enumToInt(item) > 60 and @enumToInt(item) <= 70;
234
+ return @intFromEnum(item) > 60 and @intFromEnum(item) <= 70;
235
235
  }
236
236
  };
@@ -279,9 +279,11 @@ pub const Move = enum(u8) {
279
279
  target: Target,
280
280
  /// The chance of the move's secondary effect occurring.
281
281
  chance: u8 = 0,
282
+ /// The priority of the move
283
+ priority: u8 = 1,
282
284
 
283
285
  comptime {
284
- assert(@sizeOf(Data) == 7);
286
+ assert(@sizeOf(Data) == 8);
285
287
  }
286
288
  };
287
289
 
@@ -450,6 +452,7 @@ pub const Move = enum(u8) {
450
452
  .pp = 20,
451
453
  .accuracy = 100,
452
454
  .target = .Other,
455
+ .priority = 0,
453
456
  },
454
457
  // Fly
455
458
  .{
@@ -709,6 +712,7 @@ pub const Move = enum(u8) {
709
712
  .pp = 20,
710
713
  .accuracy = 100,
711
714
  .target = .Other,
715
+ .priority = 0,
712
716
  },
713
717
  // Sing
714
718
  .{
@@ -916,6 +920,7 @@ pub const Move = enum(u8) {
916
920
  .pp = 20,
917
921
  .accuracy = 100,
918
922
  .target = .Depends,
923
+ .priority = 0,
919
924
  },
920
925
  // SeismicToss
921
926
  .{
@@ -1191,6 +1196,7 @@ pub const Move = enum(u8) {
1191
1196
  .pp = 30,
1192
1197
  .accuracy = 100,
1193
1198
  .target = .Other,
1199
+ .priority = 2,
1194
1200
  },
1195
1201
  // Rage
1196
1202
  .{
@@ -1962,6 +1968,7 @@ pub const Move = enum(u8) {
1962
1968
  .pp = 10,
1963
1969
  .accuracy = 100,
1964
1970
  .target = .Self,
1971
+ .priority = 3,
1965
1972
  },
1966
1973
  // MachPunch
1967
1974
  .{
@@ -1971,6 +1978,7 @@ pub const Move = enum(u8) {
1971
1978
  .pp = 30,
1972
1979
  .accuracy = 100,
1973
1980
  .target = .Other,
1981
+ .priority = 2,
1974
1982
  },
1975
1983
  // ScaryFace
1976
1984
  .{
@@ -2102,6 +2110,7 @@ pub const Move = enum(u8) {
2102
2110
  .pp = 5,
2103
2111
  .accuracy = 100,
2104
2112
  .target = .Self,
2113
+ .priority = 3,
2105
2114
  },
2106
2115
  // BoneRush
2107
2116
  .{
@@ -2156,6 +2165,7 @@ pub const Move = enum(u8) {
2156
2165
  .pp = 10,
2157
2166
  .accuracy = 100,
2158
2167
  .target = .Self,
2168
+ .priority = 3,
2159
2169
  },
2160
2170
  // Charm
2161
2171
  .{
@@ -2433,6 +2443,7 @@ pub const Move = enum(u8) {
2433
2443
  .pp = 10,
2434
2444
  .accuracy = 100,
2435
2445
  .target = .Other,
2446
+ .priority = 0,
2436
2447
  },
2437
2448
  // MorningSun
2438
2449
  .{
@@ -2525,6 +2536,7 @@ pub const Move = enum(u8) {
2525
2536
  .pp = 20,
2526
2537
  .accuracy = 100,
2527
2538
  .target = .Depends,
2539
+ .priority = 0,
2528
2540
  },
2529
2541
  // PsychUp
2530
2542
  .{
@@ -2543,6 +2555,7 @@ pub const Move = enum(u8) {
2543
2555
  .pp = 5,
2544
2556
  .accuracy = 100,
2545
2557
  .target = .Other,
2558
+ .priority = 2,
2546
2559
  },
2547
2560
  // AncientPower
2548
2561
  .{
@@ -2770,7 +2783,7 @@ pub const Move = enum(u8) {
2770
2783
 
2771
2784
  comptime {
2772
2785
  assert(@sizeOf(Move) == 1);
2773
- assert(@sizeOf(@TypeOf(DATA)) == 1757);
2786
+ assert(@sizeOf(@TypeOf(DATA)) == 2008);
2774
2787
  }
2775
2788
 
2776
2789
  /// The number of Pokémon moves in this generation.
@@ -2779,6 +2792,6 @@ pub const Move = enum(u8) {
2779
2792
  /// Returns the `Data` corresponding to the move.
2780
2793
  pub inline fn get(id: Move) Data {
2781
2794
  assert(id != .None);
2782
- return DATA[@enumToInt(id) - 1];
2795
+ return DATA[@intFromEnum(id) - 1];
2783
2796
  }
2784
2797
  };
@@ -1793,6 +1793,6 @@ pub const Species = enum(u8) {
1793
1793
  /// Returns the `Data` corresponding to the species.
1794
1794
  pub fn get(id: Species) Data {
1795
1795
  assert(id != .None);
1796
- return DATA[@enumToInt(id) - 1];
1796
+ return DATA[@intFromEnum(id) - 1];
1797
1797
  }
1798
1798
  };
@@ -86,17 +86,17 @@ pub const Type = enum(u8) {
86
86
 
87
87
  /// Whether or not this type is considered to be special as opposed to physical.
88
88
  pub inline fn special(self: Type) bool {
89
- return @enumToInt(self) >= @enumToInt(Type.Fire);
89
+ return @intFromEnum(self) >= @intFromEnum(Type.Fire);
90
90
  }
91
91
 
92
92
  /// The `Effectiveness` of type `t2` vs. type `t1`.
93
93
  pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
94
- return CHART[@enumToInt(t1)][@enumToInt(t2)];
94
+ return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
95
95
  }
96
96
 
97
97
  /// The precedence order of type type.
98
98
  pub inline fn precedence(self: Type) u8 {
99
- return PRECEDENCE[@enumToInt(self)];
99
+ return PRECEDENCE[@intFromEnum(self)];
100
100
  }
101
101
  };
102
102
 
@@ -1,10 +1,13 @@
1
1
  const std = @import("std");
2
2
  const builtin = @import("builtin");
3
3
 
4
+ const pkmn = @import("../pkmn.zig");
5
+
4
6
  const data = @import("../common/data.zig");
5
7
  const DEBUG = @import("../common/debug.zig").print;
6
- const options = @import("../common/options.zig");
8
+ const protocol = @import("../common/protocol.zig");
7
9
  const rng = @import("../common/rng.zig");
10
+ const util = @import("../common/util.zig");
8
11
 
9
12
  const items = @import("data/items.zig");
10
13
  const moves = @import("data/moves.zig");
@@ -13,30 +16,37 @@ const types = @import("data/types.zig");
13
16
 
14
17
  const gen1 = @import("../gen1/data.zig");
15
18
 
19
+ const calc = @import("calc.zig");
20
+ const chance = @import("chance.zig");
16
21
  const mechanics = @import("mechanics.zig");
17
22
 
18
23
  const assert = std.debug.assert;
19
24
  const expectEqual = std.testing.expectEqual;
20
25
  const expect = std.testing.expect;
21
26
 
22
- pub const MAX_OPTIONS: usize = 9; // move 1..4, switch 2..6
27
+ pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
23
28
  pub const MAX_LOGS: usize = 180; // TODO
24
29
 
25
- pub const OPTIONS_SIZE = if (builtin.mode == .ReleaseSmall)
26
- MAX_OPTIONS
30
+ pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
31
+ MAX_CHOICES
27
32
  else
28
- std.math.ceilPowerOfTwo(usize, MAX_OPTIONS) catch unreachable;
33
+ std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
29
34
  pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
30
35
  MAX_LOGS
31
36
  else
32
37
  std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
33
38
 
39
+ /// Null object pattern implementation of pkmn.battle.Options for Generation II.
40
+ pub const NULL = pkmn.battle.options(protocol.NULL, chance.NULL, calc.NULL);
41
+
34
42
  const Choice = data.Choice;
35
43
  const ID = data.ID;
36
44
  const Player = data.Player;
37
45
  const Result = data.Result;
38
46
 
39
- const showdown = options.showdown;
47
+ const PointerType = util.PointerType;
48
+
49
+ const showdown = pkmn.options.showdown;
40
50
 
41
51
  pub const PRNG = rng.PRNG(2);
42
52
 
@@ -49,20 +59,22 @@ pub fn Battle(comptime RNG: anytype) type {
49
59
  turn: u16 = 0,
50
60
  field: Field = .{},
51
61
 
52
- pub inline fn side(self: *Self, player: Player) *Side {
53
- return &self.sides[@enumToInt(player)];
62
+ pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
63
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
64
+ return &self.sides[@intFromEnum(player)];
54
65
  }
55
66
 
56
- pub inline fn foe(self: *Self, player: Player) *Side {
57
- return &self.sides[@enumToInt(player.foe())];
67
+ pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
68
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
69
+ return &self.sides[@intFromEnum(player.foe())];
58
70
  }
59
71
 
60
- pub inline fn active(self: *Self, player: Player) ID {
61
- return player.ident(@intCast(u3, self.side(player).pokemon[0].position));
72
+ pub inline fn active(self: *const Self, player: Player) ID {
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 {
@@ -76,7 +88,7 @@ test Battle {
76
88
  try expectEqual(496, @sizeOf(Battle(PRNG)));
77
89
  }
78
90
 
79
- const Field = packed struct {
91
+ pub const Field = packed struct {
80
92
  weather: Weather = .None,
81
93
  weather_duration: u4 = 0,
82
94
 
@@ -85,19 +97,21 @@ const Field = packed struct {
85
97
  }
86
98
  };
87
99
 
88
- const Weather = enum(u4) {
100
+ pub const Weather = enum(u4) {
89
101
  None,
90
102
  Rain,
91
103
  Sun,
92
104
  Sandstorm,
93
105
  };
94
106
 
95
- const Side = extern struct {
107
+ pub const Side = extern struct {
96
108
  pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
97
109
  active: ActivePokemon = .{},
98
- conditions: Conditions,
99
- last_used_move: Move = .None,
100
- _: u8 = 0,
110
+ conditions: Conditions = .{},
111
+ // NB: wLastPlayerMove
112
+ last_move: Move = .None,
113
+ // NB: wLastPlayerCounterMove / wLastEnemyCounterMove
114
+ last_counter_move: Move = .None,
101
115
 
102
116
  const Conditions = packed struct {
103
117
  Spikes: bool = false,
@@ -118,41 +132,43 @@ const Side = extern struct {
118
132
  assert(@sizeOf(Side) == 240);
119
133
  }
120
134
 
121
- pub inline fn get(self: *const Side, slot: u8) *Pokemon {
135
+ pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
136
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
122
137
  assert(slot > 0 and slot <= 6);
123
138
  const id = self.pokemon[slot - 1].position;
124
139
  assert(id > 0 and id <= 6);
125
140
  return &self.pokemon[id - 1];
126
141
  }
127
142
 
128
- pub inline fn stored(self: *Side) *Pokemon {
143
+ pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
144
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
129
145
  return self.get(1);
130
146
  }
131
147
  };
132
148
 
133
149
  // NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
134
- const ActivePokemon = extern struct {
135
- volatiles: Volatile align(4) = .{},
150
+ pub const ActivePokemon = extern struct {
151
+ volatiles: Volatiles align(4) = .{},
136
152
  stats: Stats(u16) = .{},
137
153
  moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
138
154
  boosts: Boosts = .{},
139
155
  species: Species = .None,
140
156
  item: Item = .None,
141
- last_move: Move = .None,
142
- _: u8 = 0,
157
+ types: Types = .{},
143
158
 
144
159
  comptime {
145
160
  assert(@sizeOf(ActivePokemon) == 44);
146
161
  }
147
162
 
148
- pub inline fn move(self: *ActivePokemon, mslot: u8) *MoveSlot {
163
+ pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
164
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
149
165
  assert(mslot > 0 and mslot <= 4);
150
166
  assert(self.moves[mslot - 1].id != .None);
151
167
  return &self.moves[mslot - 1];
152
168
  }
153
169
  };
154
170
 
155
- const Pokemon = extern struct {
171
+ pub const Pokemon = extern struct {
156
172
  stats: Stats(u16) = .{},
157
173
  moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
158
174
  types: Types = .{},
@@ -169,7 +185,8 @@ const Pokemon = extern struct {
169
185
  assert(@sizeOf(Pokemon) == 32);
170
186
  }
171
187
 
172
- pub inline fn move(self: *Pokemon, mslot: u8) *MoveSlot {
188
+ pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
189
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
173
190
  assert(mslot > 0 and mslot <= 4);
174
191
  assert(self.moves[mslot - 1].id != .None);
175
192
  return &self.moves[mslot - 1];
@@ -182,7 +199,7 @@ pub const Gender = enum(u2) {
182
199
  Unknown,
183
200
  };
184
201
 
185
- const DVs = packed struct {
202
+ pub const DVs = packed struct {
186
203
  gender: Gender = .Male,
187
204
  pow: u6 = 40,
188
205
  type: Type = .Dark,
@@ -205,11 +222,11 @@ const DVs = packed struct {
205
222
  0xFF => .Unknown,
206
223
  else => if ((@as(u8, dvs.atk) << 4 | dvs.spe) < ratio) Gender.Female else Gender.Male,
207
224
  };
208
- const p = @intCast(u6, 1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
225
+ const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
209
226
  ((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
210
227
  var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
211
- if (t >= @enumToInt(Type.@"???")) t = t + 1;
212
- return DVs{ .gender = g, .pow = p, .type = @intToEnum(Type, t) };
228
+ if (t >= @intFromEnum(Type.@"???")) t = t + 1;
229
+ return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
213
230
  }
214
231
 
215
232
  comptime {
@@ -238,7 +255,7 @@ test DVs {
238
255
  try expectEqual(Gender.Female, dvs.gender);
239
256
  }
240
257
 
241
- const MoveSlot = packed struct {
258
+ pub const MoveSlot = packed struct {
242
259
  id: Move = .None,
243
260
  pp_ups: u2 = 3,
244
261
  pp: u6 = 0,
@@ -250,7 +267,7 @@ const MoveSlot = packed struct {
250
267
 
251
268
  pub const Status = gen1.Status;
252
269
 
253
- const Volatile = packed struct {
270
+ pub const Volatiles = packed struct {
254
271
  Bide: bool = false,
255
272
  Thrashing: bool = false,
256
273
  Flinch: bool = false,
@@ -312,7 +329,7 @@ const Volatile = packed struct {
312
329
  };
313
330
 
314
331
  comptime {
315
- assert(@sizeOf(Volatile) == 16);
332
+ assert(@sizeOf(Volatiles) == 16);
316
333
  }
317
334
  };
318
335
 
@@ -327,10 +344,11 @@ pub fn Stats(comptime T: type) type {
327
344
 
328
345
  pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
329
346
  assert(level > 0 and level <= 100);
330
- const evs = @min(255, @floatToInt(u16, @ceil(@sqrt(@intToFloat(f32, exp)))));
347
+ const float: f32 = @floatFromInt(exp);
348
+ const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
331
349
  const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
332
350
  const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
333
- return @intCast(T, core *% @as(u32, level) / 100 +% factor);
351
+ return @intCast(core *% @as(u32, level) / 100 +% factor);
334
352
  }
335
353
  };
336
354
  }
@@ -390,7 +408,7 @@ test Item {
390
408
  pub const Move = moves.Move;
391
409
 
392
410
  test Move {
393
- try expectEqual(251, @enumToInt(Move.BeatUp));
411
+ try expectEqual(251, @intFromEnum(Move.BeatUp));
394
412
  const move = Move.get(.DynamicPunch);
395
413
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
396
414
  try expectEqual(@as(u8, 50), move.accuracy);
@@ -400,7 +418,7 @@ test Move {
400
418
  pub const Species = species.Species;
401
419
 
402
420
  test Species {
403
- try expectEqual(152, @enumToInt(Species.Chikorita));
421
+ try expectEqual(152, @intFromEnum(Species.Chikorita));
404
422
  try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
405
423
  }
406
424
 
@@ -409,7 +427,7 @@ pub const Types = types.Types;
409
427
  pub const Effectiveness = gen1.Effectiveness;
410
428
 
411
429
  test Types {
412
- try expectEqual(13, @enumToInt(Type.Electric));
430
+ try expectEqual(13, @intFromEnum(Type.Electric));
413
431
 
414
432
  try expect(!Type.Steel.special());
415
433
  try expect(Type.Dark.special());
@@ -48,7 +48,7 @@ pub const Battle = struct {
48
48
  comptime rolls: anytype,
49
49
  p1: []const Pokemon,
50
50
  p2: []const Pokemon,
51
- ) data.Battle(rng.FixedRNG(1, rolls.len)) {
51
+ ) data.Battle(rng.FixedRNG(2, rolls.len)) {
52
52
  return .{
53
53
  .rng = .{ .rolls = rolls },
54
54
  .sides = .{ Side.init(p1), Side.init(p2) },
@@ -72,11 +72,9 @@ pub fn prng(rand: *PSRNG) data.PRNG {
72
72
  rand.newSeed()
73
73
  else
74
74
  .{
75
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
76
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
77
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
78
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
79
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
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),
80
78
  },
81
79
  },
82
80
  };
@@ -89,7 +87,7 @@ pub const Side = struct {
89
87
 
90
88
  for (0..ps.len) |i| {
91
89
  side.pokemon[i] = Pokemon.init(ps[i]);
92
- // FIXME side.order[i] = i + 1;
90
+ side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
93
91
  }
94
92
  return side;
95
93
  }
@@ -100,7 +98,7 @@ pub const Side = struct {
100
98
 
101
99
  for (0..n) |i| {
102
100
  side.pokemon[i] = Pokemon.random(rand, opt);
103
- side.pokemon[i].position = i + 1;
101
+ side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
104
102
  }
105
103
 
106
104
  return side;
@@ -141,7 +139,7 @@ pub const Pokemon = struct {
141
139
  .id = m,
142
140
  .pp_ups = 3,
143
141
  // NB: PP can be at most 61 legally (though can overflow to 63)
144
- .pp = @intCast(u8, @min(Move.pp(m) / 5 * 8, 61)),
142
+ .pp = @intCast(@min(Move.get(m).pp / 5 * 8, 61)),
145
143
  };
146
144
  }
147
145
  pokemon.item = p.item;
@@ -153,16 +151,16 @@ pub const Pokemon = struct {
153
151
  pokemon.status = p.status;
154
152
  pokemon.types = species.types;
155
153
  pokemon.level = p.level;
156
- pokemon.dvs = DVs.from(p.dvs);
154
+ pokemon.dvs = DVs.from(p.species, p.dvs);
157
155
  return pokemon;
158
156
  }
159
157
 
160
158
  pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
161
- const s = @intToEnum(Species, rand.range(u8, 1, Species.size + 1));
159
+ const s: Species = @enumFromInt(rand.range(u8, 1, Species.size + 1));
162
160
  const species = Species.get(s);
163
161
  const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
164
162
  // Pokémon Showdown does not support most items without in-battle held item effects
165
- const max_item = @enumToInt(if (showdown) Item.FlowerMail else Item.TM50);
163
+ const max_item = @intFromEnum(if (showdown) Item.FlowerMail else Item.TM50);
166
164
  const item = if (rand.chance(u8, 1, 10)) 0 else rand.range(u8, 1, max_item + 1);
167
165
 
168
166
  var stats: Stats(u16) = .{};
@@ -184,14 +182,15 @@ pub const Pokemon = struct {
184
182
  for (0..n) |i| {
185
183
  var m: Move = .None;
186
184
  sample: while (true) {
187
- m = @intToEnum(Move, rand.range(u8, 1, 164 + 1));
185
+ m = @enumFromInt(rand.range(u8, 1, 164 + 1));
188
186
  for (0..i) |j| if (ms[j].id == m) continue :sample;
189
187
  break;
190
188
  }
191
- const pp_ups =
189
+ const pp_ups: u8 =
192
190
  if (!opt.cleric and rand.chance(u8, 1, 10)) rand.range(u2, 0, 2 + 1) else 3;
193
191
  // NB: PP can be at most 61 legally (though can overflow to 63)
194
- const max_pp = @intCast(u8, Move.pp(m) + @as(u8, pp_ups) * @min(Move.pp(m) / 5, 7));
192
+ var base = Move.get(m).pp;
193
+ const max_pp: u8 = @intCast(base + pp_ups * @min(base / 5, 7));
195
194
  ms[i] = .{
196
195
  .id = m,
197
196
  .pp_ups = pp_ups,
@@ -201,7 +200,7 @@ pub const Pokemon = struct {
201
200
  return .{
202
201
  .species = s,
203
202
  .types = species.types,
204
- .dvs = DVs.from(dvs),
203
+ .dvs = DVs.from(s, dvs),
205
204
  .level = lvl,
206
205
  .stats = stats,
207
206
  .hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),