@pkmn/engine 0.1.0-dev.1220fbc5 → 0.1.0-dev.130de754

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +46 -30
  3. package/build/pkg/addon/browser.d.ts +3 -0
  4. package/build/pkg/addon/browser.js +47 -0
  5. package/build/pkg/addon/browser.js.map +1 -0
  6. package/build/pkg/addon/node.d.ts +3 -0
  7. package/build/pkg/addon/node.js +110 -0
  8. package/build/pkg/addon/node.js.map +1 -0
  9. package/build/pkg/addon/wasm.d.ts +2 -0
  10. package/build/pkg/addon/wasm.js +53 -0
  11. package/build/pkg/addon/wasm.js.map +1 -0
  12. package/build/pkg/addon.d.ts +26 -4
  13. package/build/pkg/addon.js +39 -71
  14. package/build/pkg/addon.js.map +1 -1
  15. package/build/pkg/data/layout.json +15 -2
  16. package/build/pkg/data/protocol.json +23 -9
  17. package/build/pkg/data.d.ts +27 -5
  18. package/build/pkg/data.js.map +1 -1
  19. package/build/pkg/gen1.d.ts +15 -18
  20. package/build/pkg/gen1.js +28 -19
  21. package/build/pkg/gen1.js.map +1 -1
  22. package/build/pkg/index.d.ts +42 -91
  23. package/build/pkg/index.js +23 -70
  24. package/build/pkg/index.js.map +1 -1
  25. package/build/pkg/protocol.d.ts +29 -1
  26. package/build/pkg/protocol.js +109 -107
  27. package/build/pkg/protocol.js.map +1 -1
  28. package/build/tools/debug.js +84 -73
  29. package/build/tools/debug.js.map +1 -1
  30. package/build/tools/display/debug.jsx +66 -0
  31. package/build/tools/display/debug.jsx.map +1 -0
  32. package/build/tools/display/index.js +152 -0
  33. package/build/tools/display/index.js.map +1 -0
  34. package/build/tools/display/ui.jsx +360 -0
  35. package/build/tools/display/ui.jsx.map +1 -0
  36. package/build/tools/display/util.js +157 -0
  37. package/build/tools/display/util.js.map +1 -0
  38. package/build.zig +310 -203
  39. package/package.json +59 -132
  40. package/src/bin/install-pkmn-engine +70 -53
  41. package/src/bin/pkmn-debug +1 -1
  42. package/src/lib/bindings/c.zig +201 -0
  43. package/src/lib/{binding → bindings}/node.zig +15 -32
  44. package/src/lib/bindings/wasm.zig +51 -0
  45. package/src/lib/common/array.zig +70 -0
  46. package/src/lib/common/data.zig +4 -4
  47. package/src/lib/common/debug.zig +46 -30
  48. package/src/lib/common/js.zig +5 -9
  49. package/src/lib/common/optional.zig +21 -22
  50. package/src/lib/common/options.zig +6 -0
  51. package/src/lib/common/protocol.zig +573 -541
  52. package/src/lib/common/rational.zig +340 -254
  53. package/src/lib/common/rng.zig +11 -13
  54. package/src/lib/common/util.zig +53 -1
  55. package/src/lib/gen1/calc.zig +724 -175
  56. package/src/lib/gen1/chance.zig +494 -308
  57. package/src/lib/gen1/data/moves.zig +2 -4
  58. package/src/lib/gen1/data/species.zig +2 -4
  59. package/src/lib/gen1/data/types.zig +3 -4
  60. package/src/lib/gen1/data.zig +49 -49
  61. package/src/lib/gen1/helpers.zig +115 -562
  62. package/src/lib/gen1/mechanics.zig +603 -489
  63. package/src/lib/gen2/calc.zig +35 -28
  64. package/src/lib/gen2/chance.zig +122 -518
  65. package/src/lib/gen2/data/items.zig +1 -3
  66. package/src/lib/gen2/data/moves.zig +2 -4
  67. package/src/lib/gen2/data/species.zig +1 -3
  68. package/src/lib/gen2/data/types.zig +7 -5
  69. package/src/lib/gen2/data.zig +40 -39
  70. package/src/lib/gen2/generated.zig +30 -47
  71. package/src/lib/gen2/helpers.zig +68 -39
  72. package/src/lib/gen2/mechanics.zig +853 -533
  73. package/src/lib/napi.h +2 -0
  74. package/src/lib/node.zig +14 -0
  75. package/src/lib/pkmn.zig +123 -29
  76. package/src/lib/wasm.zig +12 -0
  77. package/src/tools/display/dom.ts +108 -0
  78. package/src/tools/display/ui.css +354 -0
  79. package/build/test/display.js +0 -400
  80. package/build/test/display.js.map +0 -1
  81. package/src/lib/binding/wasm.zig +0 -12
  82. package/src/lib/gen1/README.md +0 -894
  83. package/src/lib/gen2/README.md +0 -330
  84. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -1,11 +1,9 @@
1
1
  //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
- const std = @import("std");
4
-
5
3
  const data = @import("../../gen2/data.zig");
4
+ const std = @import("std");
6
5
 
7
6
  const assert = std.debug.assert;
8
-
9
7
  const Type = data.Type;
10
8
 
11
9
  /// Representation of a held item in Generation II Pokémon.
@@ -1,12 +1,10 @@
1
1
  //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
- const std = @import("std");
4
-
5
- const rng = @import("../../common/rng.zig");
6
3
  const gen2 = @import("../../gen2/data.zig");
4
+ const rng = @import("../../common/rng.zig");
5
+ const std = @import("std");
7
6
 
8
7
  const assert = std.debug.assert;
9
-
10
8
  const percent = rng.Gen12.percent;
11
9
  const Type = gen2.Type;
12
10
 
@@ -1,11 +1,9 @@
1
1
  //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
- const std = @import("std");
4
-
5
3
  const gen2 = @import("../../gen2/data.zig");
4
+ const std = @import("std");
6
5
 
7
6
  const assert = std.debug.assert;
8
-
9
7
  const Stats = gen2.Stats;
10
8
  const Types = gen2.Types;
11
9
 
@@ -1,8 +1,7 @@
1
1
  //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
- const std = @import("std");
4
-
5
3
  const gen1 = @import("../../gen1/data.zig");
4
+ const std = @import("std");
6
5
 
7
6
  const assert = std.debug.assert;
8
7
  const Effectiveness = gen1.Effectiveness;
@@ -173,9 +172,12 @@ pub const Type = enum(u8) {
173
172
  return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
174
173
  }
175
174
 
176
- /// The precedence order of Type `type`.
177
- pub fn precedence(self: Type) u8 {
178
- return PRECEDENCE[@intFromEnum(self)];
175
+ /// The precedence order of Type `t1` vs. Type `t2`.
176
+ pub fn precedence(t1: Type, t2: Type) u8 {
177
+ // The Ice vs. Fire matchup is out of order - return a higher
178
+ // number than anything else so that it sorts last correctly
179
+ if (t1 == .Ice and t2 == .Fire) return 18;
180
+ return PRECEDENCE[@intFromEnum(t2)];
179
181
  }
180
182
 
181
183
  /// The internal index of this Type used by Present.
@@ -1,24 +1,20 @@
1
- const std = @import("std");
2
- const builtin = @import("builtin");
3
-
4
- const pkmn = @import("../pkmn.zig");
5
-
6
- const data = @import("../common/data.zig");
7
1
  const DEBUG = @import("../common/debug.zig").print;
8
- const protocol = @import("../common/protocol.zig");
9
- const rng = @import("../common/rng.zig");
10
- const util = @import("../common/util.zig");
11
2
 
3
+ const builtin = @import("builtin");
4
+ const calc = @import("calc.zig");
5
+ const chance = @import("chance.zig");
6
+ const data = @import("../common/data.zig");
7
+ const gen1 = @import("../gen1/data.zig");
12
8
  const items = @import("data/items.zig");
9
+ const mechanics = @import("mechanics.zig");
13
10
  const moves = @import("data/moves.zig");
11
+ const pkmn = @import("../pkmn.zig");
12
+ const protocol = @import("../common/protocol.zig");
13
+ const rng = @import("../common/rng.zig");
14
14
  const species = @import("data/species.zig");
15
+ const std = @import("std");
15
16
  const types = @import("data/types.zig");
16
-
17
- const gen1 = @import("../gen1/data.zig");
18
-
19
- const calc = @import("calc.zig");
20
- const chance = @import("chance.zig");
21
- const mechanics = @import("mechanics.zig");
17
+ const util = @import("../common/util.zig");
22
18
 
23
19
  const assert = std.debug.assert;
24
20
  const expectEqual = std.testing.expectEqual;
@@ -27,11 +23,14 @@ const expect = std.testing.expect;
27
23
  pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
28
24
  pub const MAX_LOGS: usize = 180; // TODO
29
25
 
30
- pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
26
+ const ReleaseSmall: std.builtin.OptimizeMode =
27
+ if (@hasDecl(std.builtin.OptimizeMode, "ReleaseSmall")) .small else .ReleaseSmall;
28
+
29
+ pub const CHOICES_SIZE = if (builtin.mode == ReleaseSmall)
31
30
  MAX_CHOICES
32
31
  else
33
32
  std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
34
- pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
33
+ pub const LOGS_SIZE = if (builtin.mode == ReleaseSmall)
35
34
  MAX_LOGS
36
35
  else
37
36
  std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
@@ -45,6 +44,7 @@ const Player = data.Player;
45
44
  const Result = data.Result;
46
45
 
47
46
  const PointerType = util.PointerType;
47
+ const isPointerTo = util.isPointerTo;
48
48
 
49
49
  const showdown = pkmn.options.showdown;
50
50
 
@@ -60,15 +60,15 @@ pub fn Battle(comptime RNG: anytype) type {
60
60
  turn: u16 = 0,
61
61
  field: Field = .{},
62
62
 
63
- _: [padding]u8 = .{0} ** padding,
63
+ _: [padding]u8 = @splat(0),
64
64
 
65
65
  pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
66
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
66
+ assert(isPointerTo(self, Self));
67
67
  return &self.sides[@intFromEnum(player)];
68
68
  }
69
69
 
70
70
  pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
71
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
71
+ assert(isPointerTo(self, Self));
72
72
  return &self.sides[@intFromEnum(player.foe())];
73
73
  }
74
74
 
@@ -103,13 +103,13 @@ pub const Weather = enum(u4) {
103
103
  };
104
104
 
105
105
  pub const Side = extern struct {
106
- pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
106
+ pokemon: [6]Pokemon = @splat(.{}),
107
107
  active: ActivePokemon align(8) = .{},
108
108
  conditions: Conditions = .{},
109
109
  // NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
110
110
  last_used_move: Move = .None,
111
111
 
112
- _: [7]u8 = .{0} ** 7,
112
+ _: [7]u8 = @splat(0),
113
113
 
114
114
  const Conditions = packed struct(u32) {
115
115
  Spikes: bool = false,
@@ -134,7 +134,7 @@ pub const Side = extern struct {
134
134
  }
135
135
 
136
136
  pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
137
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
137
+ assert(isPointerTo(self, Side));
138
138
  assert(slot > 0 and slot <= 6);
139
139
  const id = self.pokemon[slot - 1].position;
140
140
  assert(id > 0 and id <= 6);
@@ -142,7 +142,7 @@ pub const Side = extern struct {
142
142
  }
143
143
 
144
144
  pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
145
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
145
+ assert(isPointerTo(self, Side));
146
146
  return self.get(1);
147
147
  }
148
148
 
@@ -164,7 +164,7 @@ pub const Side = extern struct {
164
164
  pub const ActivePokemon = extern struct {
165
165
  volatiles: Volatiles align(4) = .{},
166
166
  stats: Stats(u16) = .{},
167
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
167
+ moves: [4]MoveSlot = @splat(.{}),
168
168
  boosts: Boosts = .{},
169
169
  types: Types = .{},
170
170
  species: Species = .None,
@@ -176,7 +176,7 @@ pub const ActivePokemon = extern struct {
176
176
  }
177
177
 
178
178
  pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
179
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
179
+ assert(isPointerTo(self, ActivePokemon));
180
180
  assert(mslot > 0 and mslot <= 4);
181
181
  assert(self.moves[mslot - 1].id != .None);
182
182
  return &self.moves[mslot - 1];
@@ -185,7 +185,7 @@ pub const ActivePokemon = extern struct {
185
185
 
186
186
  pub const Pokemon = extern struct {
187
187
  stats: Stats(u16) = .{},
188
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
188
+ moves: [4]MoveSlot = @splat(.{}),
189
189
  types: Types = .{},
190
190
  dvs: DVs = .{},
191
191
  hp: u16 = 0,
@@ -201,7 +201,7 @@ pub const Pokemon = extern struct {
201
201
  }
202
202
 
203
203
  pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
204
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
204
+ assert(isPointerTo(self, Pokemon));
205
205
  assert(mslot > 0 and mslot <= 4);
206
206
  assert(self.moves[mslot - 1].id != .None);
207
207
  return &self.moves[mslot - 1];
@@ -239,7 +239,7 @@ pub const DVs = packed struct(u16) {
239
239
  };
240
240
  const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
241
241
  ((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
242
- var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
242
+ const t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
243
243
  return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
244
244
  }
245
245
  };
@@ -248,12 +248,12 @@ test DVs {
248
248
  var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
249
249
  try expectEqual(Gender.Unknown, dvs.gender);
250
250
  try expectEqual(Type.Ice, dvs.type);
251
- try expectEqual(@as(u8, 70), dvs.power());
251
+ try expectEqual(70, dvs.power());
252
252
 
253
253
  dvs = DVs.from(.Pikachu, .{ .spc = 14 });
254
254
  try expectEqual(Gender.Male, dvs.gender);
255
255
  try expectEqual(Type.Dark, dvs.type);
256
- try expectEqual(@as(u8, 69), dvs.power());
256
+ try expectEqual(69, dvs.power());
257
257
 
258
258
  dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
259
259
  try expectEqual(Gender.Female, dvs.gender);
@@ -314,8 +314,8 @@ pub const Volatiles = packed struct(u128) {
314
314
  Encore: bool = false,
315
315
  LockOn: bool = false,
316
316
  DestinyBond: bool = false,
317
+ Trapped: bool = false,
317
318
 
318
- trapped: bool = false,
319
319
  switched: bool = false,
320
320
  // TODO: replace with simply looking at Choice on Battle
321
321
  switching: bool = false,
@@ -380,9 +380,9 @@ test Stats {
380
380
  try expectEqual(0, stats.def);
381
381
 
382
382
  var base = Species.get(.Gyarados).stats;
383
- try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
383
+ try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
384
384
  base = Species.get(.Pidgeot).stats;
385
- try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
385
+ try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
386
386
  }
387
387
 
388
388
  pub const Boosts = packed struct(u32) {
@@ -428,8 +428,8 @@ test Move {
428
428
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
429
429
  try expectEqual(rng.Gen12.percent(50), move.accuracy);
430
430
  try expectEqual(rng.Gen12.percent(100), move.chance);
431
- try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
432
- try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
431
+ try expectEqual(5, Move.pp(.DynamicPunch));
432
+ try expectEqual(-1, Move.get(.Counter).priority);
433
433
  try expect(!move.effect.isHighCritical());
434
434
  try expect(Move.get(.KarateChop).effect.isHighCritical());
435
435
  try expect(Move.get(.RazorWind).effect.isHighCritical());
@@ -439,7 +439,7 @@ pub const Species = species.Species;
439
439
 
440
440
  test Species {
441
441
  try expectEqual(152, @intFromEnum(Species.Chikorita));
442
- try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
442
+ try expectEqual(100, Species.get(.Celebi).stats.spd);
443
443
  }
444
444
 
445
445
  pub const Type = types.Type;
@@ -452,8 +452,9 @@ test Types {
452
452
  try expect(!Type.Steel.special());
453
453
  try expect(Type.Dark.special());
454
454
 
455
- try expectEqual(@as(u8, 3), Type.Water.precedence());
456
- try expect(Type.Bug.precedence() > Type.Poison.precedence());
455
+ try expectEqual(3, Type.precedence(.Water, .Water));
456
+ try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
457
+ try expect(Type.precedence(.Ice, .Fire) > Type.precedence(.Ice, .Water));
457
458
 
458
459
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
459
460
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Water, .Fire));
@@ -1,39 +1,29 @@
1
1
  //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
- const std = @import("std");
4
-
5
3
  const common = @import("../common/data.zig");
6
-
7
4
  const data = @import("data.zig");
8
5
  const mechanics = @import("mechanics.zig");
9
6
 
10
- const assert = std.debug.assert;
11
-
12
- const Player = common.Player;
13
- const Result = common.Result;
14
-
15
- const Effectiveness = data.Effectiveness;
16
- const Move = data.Move;
17
-
18
- const Effects = mechanics.Effects;
19
- const State = mechanics.State;
20
-
21
- const canMove = mechanics.canMove;
7
+ const adjustDamage = mechanics.adjustDamage;
8
+ const afterMove = mechanics.afterMove;
9
+ const applyDamage = mechanics.applyDamage;
10
+ const calcDamage = mechanics.calcDamage;
22
11
  const canCharge = mechanics.canCharge;
12
+ const canMove = mechanics.canMove;
13
+ const checkCriticalHit = mechanics.checkCriticalHit;
14
+ const checkHit = mechanics.checkHit;
23
15
  const decrementPP = mechanics.decrementPP;
16
+ const destinyBond = mechanics.destinyBond;
24
17
  const doMove = mechanics.doMove;
25
- const checkHit = mechanics.checkHit;
26
- const checkCriticalHit = mechanics.checkCriticalHit;
27
- const calcDamage = mechanics.calcDamage;
28
- const adjustDamage = mechanics.adjustDamage;
29
- const randomizeDamage = mechanics.randomizeDamage;
30
- const applyDamage = mechanics.applyDamage;
31
- const reportOutcome = mechanics.reportOutcome;
32
18
  const effectChance = mechanics.effectChance;
33
- const afterMove = mechanics.afterMove;
34
- const buildRage = mechanics.buildRage;
19
+ const Effectiveness = data.Effectiveness;
20
+ const Effects = mechanics.Effects;
35
21
  const kingsRock = mechanics.kingsRock;
36
- const destinyBond = mechanics.destinyBond;
22
+ const Move = data.Move;
23
+ const Player = common.Player;
24
+ const randomizeDamage = mechanics.randomizeDamage;
25
+ const reportOutcome = mechanics.reportOutcome;
26
+ const State = mechanics.State;
37
27
 
38
28
  const SECONDARY = true;
39
29
  const KINGS = true;
@@ -42,7 +32,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
42
32
  var log = options.log;
43
33
 
44
34
  var side = battle.side(player);
45
- var volatiles = &side.active.volatiles;
35
+ const volatiles = &side.active.volatiles;
46
36
 
47
37
  const ident = battle.active(player);
48
38
  const foe_ident = battle.active(player.foe());
@@ -175,7 +165,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
175
165
  if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
176
166
  // TODO rampage
177
167
  // usedmovetext
178
- try log.move(ident, state.move, foe_ident); // FIXME self? from?
168
+ try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
179
169
  try checkHit(battle, player, state, options);
180
170
  try checkCriticalHit(battle, player, state, options);
181
171
  try calcDamage(battle, player, state, options);
@@ -260,7 +250,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
260
250
  try Effects.hyperBeam(battle, player, state, options);
261
251
  _ = try afterMove(!KINGS, battle, player, state, options);
262
252
  },
263
- .Counter => {
253
+ .MirrorCoat, .Counter => {
264
254
  if (!try canMove(battle, player, state, options)) return;
265
255
  try Effects.counter(battle, player, state, options);
266
256
  try applyDamage(battle, player, state, options);
@@ -344,13 +334,17 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
344
334
  if (!try canMove(battle, player, state, options)) return;
345
335
  try Effects.heal(battle, player, state, options);
346
336
  },
337
+ .LightScreen => {
338
+ if (!try canMove(battle, player, state, options)) return;
339
+ try Effects.lightScreen(battle, player, state, options);
340
+ },
347
341
  .Haze => {
348
342
  if (!try canMove(battle, player, state, options)) return;
349
343
  try Effects.haze(battle, player, state, options);
350
344
  },
351
- .LightScreen, .Reflect => {
345
+ .Reflect => {
352
346
  if (!try canMove(battle, player, state, options)) return;
353
- try Effects.screens(battle, player, state, options);
347
+ try Effects.reflect(battle, player, state, options);
354
348
  },
355
349
  .FocusEnergy => {
356
350
  if (!try canMove(battle, player, state, options)) return;
@@ -364,7 +358,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
364
358
  (volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
365
359
  if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
366
360
  // usedmovetext
367
- try log.move(ident, state.move, foe_ident); // FIXME self? from?
361
+ try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
368
362
  try Effects.bide.unleash(battle, player, state, options);
369
363
 
370
364
  if (state.immune()) {
@@ -544,19 +538,19 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
544
538
  try Effects.sandstorm(battle, player, state, options);
545
539
  },
546
540
  .Rollout => {
547
- // TODO checkcurl
541
+ try Effects.rollout.check(battle, player, state, options);
548
542
  // doturn
549
543
  const charging = false; // TODO
550
544
  const skip_pp = charging or state.move == .Struggle or
551
545
  (volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
552
546
  if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
553
547
  // usedmovetext
554
- try log.move(ident, state.move, foe_ident); // FIXME self? from?
548
+ try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
555
549
  try checkCriticalHit(battle, player, state, options);
556
550
  try calcDamage(battle, player, state, options);
557
551
  try adjustDamage(battle, player, state, options);
558
552
  try checkHit(battle, player, state, options);
559
- // TODO rolloutpower
553
+ try Effects.rollout.power(battle, player, state, options);
560
554
  try randomizeDamage(battle, player, state, options);
561
555
  try applyDamage(battle, player, state, options);
562
556
  try reportOutcome(battle, player, state, options);
@@ -577,12 +571,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
577
571
  .Swagger => {
578
572
  if (!try canMove(battle, player, state, options)) return;
579
573
  try checkHit(battle, player, state, options);
580
- // TODO switchturn
581
- // TODO attackup2
582
- // TODO switchturn
583
- // TODO failuretext
584
- // TODO switchturn
585
- // TODO switchturn
574
+ try Effects.boost(battle, player.foe(), state, options);
586
575
  try Effects.confusionChance(battle, player, state, options);
587
576
  },
588
577
  .FuryCutter => {
@@ -656,7 +645,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
656
645
  .Magnitude => {
657
646
  if (!try canMove(battle, player, state, options)) return;
658
647
  try checkCriticalHit(battle, player, state, options);
659
- // TODO getmagnitude
648
+ try Effects.magnitude(battle, player, state, options);
660
649
  try calcDamage(battle, player, state, options);
661
650
  try adjustDamage(battle, player, state, options);
662
651
  try randomizeDamage(battle, player, state, options);
@@ -731,12 +720,6 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
731
720
  if (!try canMove(battle, player, state, options)) return;
732
721
  try Effects.sunnyDay(battle, player, state, options);
733
722
  },
734
- .MirrorCoat => {
735
- if (!try canMove(battle, player, state, options)) return;
736
- try Effects.mirrorCoat(battle, player, state, options);
737
- try applyDamage(battle, player, state, options);
738
- _ = try afterMove(KINGS, battle, player, state, options);
739
- },
740
723
  .PsychUp => {
741
724
  if (!try canMove(battle, player, state, options)) return;
742
725
  try Effects.psychUp(battle, player, state, options);
@@ -1,31 +1,20 @@
1
- const std = @import("std");
2
-
3
1
  const common = @import("../common/data.zig");
2
+ const data = @import("data.zig");
3
+ const gen1 = @import("../gen1/data.zig");
4
4
  const options = @import("../common/options.zig");
5
- const protocol = @import("../common/protocol.zig");
6
5
  const rng = @import("../common/rng.zig");
7
-
8
- const gen1 = @import("../gen1/data.zig");
9
-
10
- const data = @import("data.zig");
6
+ const std = @import("std");
11
7
 
12
8
  const assert = std.debug.assert;
13
-
14
- const expectEqual = std.testing.expectEqual;
15
-
16
9
  const Choice = common.Choice;
17
-
18
- const showdown = options.showdown;
19
-
20
- const PSRNG = rng.PSRNG;
21
-
22
10
  const DVs = data.DVs;
23
11
  const Item = data.Item;
24
12
  const Move = data.Move;
25
13
  const MoveSlot = data.MoveSlot;
14
+ const PSRNG = rng.PSRNG;
15
+ const showdown = options.showdown;
26
16
  const Species = data.Species;
27
17
  const Stats = data.Stats;
28
- const Status = data.Status;
29
18
 
30
19
  pub const Options = struct {
31
20
  cleric: bool = showdown,
@@ -65,17 +54,47 @@ pub const Battle = struct {
65
54
  };
66
55
 
67
56
  pub fn prng(rand: *PSRNG) data.PRNG {
68
- // FIXME: what is max range in gen2?
69
57
  return .{
70
58
  .src = .{
71
59
  .seed = if (showdown)
72
60
  rand.newSeed()
73
- else
74
- .{
75
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
76
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
77
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
78
- },
61
+ else seed: {
62
+ // GLITCH: The initial RNG seed in Gen II link battles is heavily constrained
63
+ // https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
64
+ var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
65
+
66
+ // Bytes in seed can only range from 0-252, not 0-255
67
+ const max: u8 = 253;
68
+ // Instructions take multiples of 4 cycles to execute
69
+ var timer: u16 = 4 * rand.range(u16, 0, 16384);
70
+ var add: u8 = rand.range(u8, 0, 256);
71
+ var sub: u8 = rand.range(u8, 0, 256);
72
+ var carry: u8 = 0;
73
+ var div: u8 = 0;
74
+
75
+ var i: usize = 0;
76
+ while (i < seed.len) {
77
+ div = @intCast(timer >> 8);
78
+ // This is effectively @addWithOverflow but with three operands
79
+ const result = @as(u16, add) + @as(u16, div) + @as(u16, carry);
80
+ add = @intCast(result & 255);
81
+ carry = @intFromBool(result > 255);
82
+ timer +%= 44;
83
+ div = @intCast(timer >> 8);
84
+ sub -%= div +% carry;
85
+
86
+ if (sub < max) {
87
+ seed[i] = sub;
88
+ i += 1;
89
+ timer +%= 152;
90
+ carry = 1;
91
+ } else {
92
+ timer +%= 132;
93
+ carry = 0;
94
+ }
95
+ }
96
+ break :seed seed;
97
+ },
79
98
  },
80
99
  };
81
100
  }
@@ -121,15 +140,20 @@ pub const Pokemon = struct {
121
140
  var pokemon = data.Pokemon{};
122
141
  pokemon.species = p.species;
123
142
  const species = Species.get(p.species);
124
- inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
125
- const hp = comptime std.mem.eql(u8, field.name, "hp");
143
+ inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(pokemon.stats)).@"struct"), "fields"))
144
+ @typeInfo(@TypeOf(pokemon.stats)).@"struct".fields
145
+ else
146
+ @typeInfo(@TypeOf(pokemon.stats)).@"struct".field_names) |field|
147
+ {
148
+ const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
149
+ const hp = comptime std.mem.eql(u8, field_name, "hp");
126
150
  const spc =
127
- comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
128
- @field(pokemon.stats, field.name) = Stats(u16).calc(
129
- field.name,
130
- @field(species.stats, field.name),
131
- if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field.name),
132
- @field(p.stats, field.name),
151
+ comptime std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
152
+ @field(pokemon.stats, field_name) = Stats(u16).calc(
153
+ field_name,
154
+ @field(species.stats, field_name),
155
+ if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field_name),
156
+ @field(p.stats, field_name),
133
157
  p.level,
134
158
  );
135
159
  }
@@ -162,19 +186,24 @@ pub const Pokemon = struct {
162
186
 
163
187
  var stats: Stats(u16) = .{};
164
188
  const dvs = gen1.DVs.random(rand);
165
- inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
166
- const hp = std.mem.eql(u8, field.name, "hp");
167
- const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
168
- @field(stats, field.name) = Stats(u16).calc(
169
- field.name,
170
- @field(species.stats, field.name),
171
- if (hp) dvs.hp() else if (spc) dvs.spc else @field(dvs, field.name),
189
+ inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(stats)).@"struct"), "fields"))
190
+ @typeInfo(@TypeOf(stats)).@"struct".fields
191
+ else
192
+ @typeInfo(@TypeOf(stats)).@"struct".field_names) |field|
193
+ {
194
+ const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
195
+ const hp = std.mem.eql(u8, field_name, "hp");
196
+ const spc = std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
197
+ @field(stats, field_name) = Stats(u16).calc(
198
+ field_name,
199
+ @field(species.stats, field_name),
200
+ if (hp) dvs.hp() else if (spc) dvs.spc else @field(dvs, field_name),
172
201
  if (rand.chance(u8, 1, 20)) rand.range(u8, 0, 255 + 1) else 255,
173
202
  lvl,
174
203
  );
175
204
  }
176
205
 
177
- var ms = [_]MoveSlot{.{}} ** 4;
206
+ var ms: [4]MoveSlot = @splat(.{});
178
207
  const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
179
208
  for (0..n) |i| {
180
209
  var m: Move = .None;