@pkmn/engine 0.1.0-dev.9f75c7ae → 0.1.0-dev.a0167a9d

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 (42) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +44 -19
  3. package/build/pkg/addon.js +6 -7
  4. package/build/pkg/addon.js.map +1 -1
  5. package/build/pkg/data.d.ts +4 -4
  6. package/build/pkg/data.js.map +1 -1
  7. package/build/pkg/gen1.js.map +1 -1
  8. package/build/pkg/index.d.ts +2 -1
  9. package/build/pkg/index.js.map +1 -1
  10. package/build/pkg/protocol.js +5 -5
  11. package/build/pkg/protocol.js.map +1 -1
  12. package/build/test/display.js +2 -2
  13. package/build/test/display.js.map +1 -1
  14. package/build/tools/debug.js +5 -4
  15. package/build/tools/debug.js.map +1 -1
  16. package/build.zig +82 -90
  17. package/package.json +31 -23
  18. package/src/bin/install-pkmn-engine +26 -18
  19. package/src/lib/common/data.zig +4 -4
  20. package/src/lib/common/debug.zig +2 -2
  21. package/src/lib/common/optional.zig +1 -1
  22. package/src/lib/common/options.zig +2 -0
  23. package/src/lib/common/protocol.zig +393 -396
  24. package/src/lib/common/rational.zig +109 -15
  25. package/src/lib/common/rng.zig +11 -11
  26. package/src/lib/gen1/calc.zig +269 -97
  27. package/src/lib/gen1/chance.zig +126 -160
  28. package/src/lib/gen1/data/species.zig +1 -1
  29. package/src/lib/gen1/data.zig +21 -21
  30. package/src/lib/gen1/helpers.zig +191 -206
  31. package/src/lib/gen1/mechanics.zig +308 -285
  32. package/src/lib/gen2/chance.zig +211 -121
  33. package/src/lib/gen2/data.zig +13 -11
  34. package/src/lib/gen2/generated.zig +15 -24
  35. package/src/lib/gen2/helpers.zig +37 -7
  36. package/src/lib/gen2/mechanics.zig +855 -413
  37. package/src/lib/{binding/node.zig → node.zig} +9 -12
  38. package/src/lib/pkmn.zig +27 -12
  39. package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
  40. package/src/test/display/pkmn.html.tmpl +5 -5
  41. package/src/lib/gen1/README.md +0 -894
  42. package/src/lib/gen2/README.md +0 -330
@@ -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,16 +314,18 @@ 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
+ // TODO: replace with simply looking at Choice on Battle
320
321
  switching: bool = false,
322
+ // TODO: simply store both last moves on side
321
323
  dirty: bool = false,
322
324
 
323
325
  frozen: bool = false,
324
326
  minimized: bool = false,
325
327
  _: u10 = 0,
326
- // TODO?
328
+ // TODO: is this needed for unmodified stats?
327
329
  transform: u4 = 0,
328
330
 
329
331
  bide: u16 = 0,
@@ -378,9 +380,9 @@ test Stats {
378
380
  try expectEqual(0, stats.def);
379
381
 
380
382
  var base = Species.get(.Gyarados).stats;
381
- 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));
382
384
  base = Species.get(.Pidgeot).stats;
383
- 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));
384
386
  }
385
387
 
386
388
  pub const Boosts = packed struct(u32) {
@@ -426,8 +428,8 @@ test Move {
426
428
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
427
429
  try expectEqual(rng.Gen12.percent(50), move.accuracy);
428
430
  try expectEqual(rng.Gen12.percent(100), move.chance);
429
- try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
430
- try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
431
+ try expectEqual(5, Move.pp(.DynamicPunch));
432
+ try expectEqual(-1, Move.get(.Counter).priority);
431
433
  try expect(!move.effect.isHighCritical());
432
434
  try expect(Move.get(.KarateChop).effect.isHighCritical());
433
435
  try expect(Move.get(.RazorWind).effect.isHighCritical());
@@ -437,7 +439,7 @@ pub const Species = species.Species;
437
439
 
438
440
  test Species {
439
441
  try expectEqual(152, @intFromEnum(Species.Chikorita));
440
- try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
442
+ try expectEqual(100, Species.get(.Celebi).stats.spd);
441
443
  }
442
444
 
443
445
  pub const Type = types.Type;
@@ -450,7 +452,7 @@ test Types {
450
452
  try expect(!Type.Steel.special());
451
453
  try expect(Type.Dark.special());
452
454
 
453
- try expectEqual(@as(u8, 3), Type.Water.precedence());
455
+ try expectEqual(3, Type.Water.precedence());
454
456
  try expect(Type.Bug.precedence() > Type.Poison.precedence());
455
457
 
456
458
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
@@ -7,8 +7,6 @@ const common = @import("../common/data.zig");
7
7
  const data = @import("data.zig");
8
8
  const mechanics = @import("mechanics.zig");
9
9
 
10
- const assert = std.debug.assert;
11
-
12
10
  const Player = common.Player;
13
11
  const Result = common.Result;
14
12
 
@@ -42,7 +40,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
42
40
  var log = options.log;
43
41
 
44
42
  var side = battle.side(player);
45
- var volatiles = &side.active.volatiles;
43
+ const volatiles = &side.active.volatiles;
46
44
 
47
45
  const ident = battle.active(player);
48
46
  const foe_ident = battle.active(player.foe());
@@ -175,7 +173,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
175
173
  if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
176
174
  // TODO rampage
177
175
  // usedmovetext
178
- try log.move(ident, state.move, foe_ident); // FIXME self? from?
176
+ try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
179
177
  try checkHit(battle, player, state, options);
180
178
  try checkCriticalHit(battle, player, state, options);
181
179
  try calcDamage(battle, player, state, options);
@@ -260,7 +258,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
260
258
  try Effects.hyperBeam(battle, player, state, options);
261
259
  _ = try afterMove(!KINGS, battle, player, state, options);
262
260
  },
263
- .Counter => {
261
+ .MirrorCoat, .Counter => {
264
262
  if (!try canMove(battle, player, state, options)) return;
265
263
  try Effects.counter(battle, player, state, options);
266
264
  try applyDamage(battle, player, state, options);
@@ -344,13 +342,17 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
344
342
  if (!try canMove(battle, player, state, options)) return;
345
343
  try Effects.heal(battle, player, state, options);
346
344
  },
345
+ .LightScreen => {
346
+ if (!try canMove(battle, player, state, options)) return;
347
+ try Effects.lightScreen(battle, player, state, options);
348
+ },
347
349
  .Haze => {
348
350
  if (!try canMove(battle, player, state, options)) return;
349
351
  try Effects.haze(battle, player, state, options);
350
352
  },
351
- .LightScreen, .Reflect => {
353
+ .Reflect => {
352
354
  if (!try canMove(battle, player, state, options)) return;
353
- try Effects.screens(battle, player, state, options);
355
+ try Effects.reflect(battle, player, state, options);
354
356
  },
355
357
  .FocusEnergy => {
356
358
  if (!try canMove(battle, player, state, options)) return;
@@ -364,7 +366,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
364
366
  (volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
365
367
  if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
366
368
  // usedmovetext
367
- try log.move(ident, state.move, foe_ident); // FIXME self? from?
369
+ try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
368
370
  try Effects.bide.unleash(battle, player, state, options);
369
371
 
370
372
  if (state.immune()) {
@@ -544,19 +546,19 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
544
546
  try Effects.sandstorm(battle, player, state, options);
545
547
  },
546
548
  .Rollout => {
547
- // TODO checkcurl
549
+ try Effects.rollout.check(battle, player, state, options);
548
550
  // doturn
549
551
  const charging = false; // TODO
550
552
  const skip_pp = charging or state.move == .Struggle or
551
553
  (volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
552
554
  if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
553
555
  // usedmovetext
554
- try log.move(ident, state.move, foe_ident); // FIXME self? from?
556
+ try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
555
557
  try checkCriticalHit(battle, player, state, options);
556
558
  try calcDamage(battle, player, state, options);
557
559
  try adjustDamage(battle, player, state, options);
558
560
  try checkHit(battle, player, state, options);
559
- // TODO rolloutpower
561
+ try Effects.rollout.power(battle, player, state, options);
560
562
  try randomizeDamage(battle, player, state, options);
561
563
  try applyDamage(battle, player, state, options);
562
564
  try reportOutcome(battle, player, state, options);
@@ -577,12 +579,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
577
579
  .Swagger => {
578
580
  if (!try canMove(battle, player, state, options)) return;
579
581
  try checkHit(battle, player, state, options);
580
- // TODO switchturn
581
- // TODO attackup2
582
- // TODO switchturn
583
- // TODO failuretext
584
- // TODO switchturn
585
- // TODO switchturn
582
+ try Effects.boost(battle, player.foe(), state, options);
586
583
  try Effects.confusionChance(battle, player, state, options);
587
584
  },
588
585
  .FuryCutter => {
@@ -656,7 +653,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
656
653
  .Magnitude => {
657
654
  if (!try canMove(battle, player, state, options)) return;
658
655
  try checkCriticalHit(battle, player, state, options);
659
- // TODO getmagnitude
656
+ try Effects.magnitude(battle, player, state, options);
660
657
  try calcDamage(battle, player, state, options);
661
658
  try adjustDamage(battle, player, state, options);
662
659
  try randomizeDamage(battle, player, state, options);
@@ -731,12 +728,6 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
731
728
  if (!try canMove(battle, player, state, options)) return;
732
729
  try Effects.sunnyDay(battle, player, state, options);
733
730
  },
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
731
  .PsychUp => {
741
732
  if (!try canMove(battle, player, state, options)) return;
742
733
  try Effects.psychUp(battle, player, state, options);
@@ -65,17 +65,47 @@ pub const Battle = struct {
65
65
  };
66
66
 
67
67
  pub fn prng(rand: *PSRNG) data.PRNG {
68
- // FIXME: what is max range in gen2?
69
68
  return .{
70
69
  .src = .{
71
70
  .seed = if (showdown)
72
71
  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
- },
72
+ else seed: {
73
+ // GLITCH: The initial RNG seed in Gen II link battles is heavily constrained
74
+ // https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
75
+ var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
76
+
77
+ // Bytes in seed can only range from 0-252, not 0-255
78
+ const max: u8 = 253;
79
+ // Instructions take multiples of 4 cycles to execute
80
+ var timer: u16 = 4 * rand.range(u16, 0, 16384);
81
+ var add: u8 = rand.range(u8, 0, 256);
82
+ var sub: u8 = rand.range(u8, 0, 256);
83
+ var carry: u8 = 0;
84
+ var div: u8 = 0;
85
+
86
+ var i: u8 = 0;
87
+ while (i < seed.len) {
88
+ div = @intCast(timer >> 8);
89
+ // This is effectively @addWithOverflow but with three operands
90
+ const result = @as(u16, add) + @as(u16, div) + @as(u16, carry);
91
+ add = @intCast(result & 255);
92
+ carry = @intFromBool(result > 255);
93
+ timer +%= 44;
94
+ div = @intCast(timer >> 8);
95
+ sub -%= div +% carry;
96
+
97
+ if (sub < max) {
98
+ seed[i] = sub;
99
+ i += 1;
100
+ timer +%= 152;
101
+ carry = 1;
102
+ } else {
103
+ timer +%= 132;
104
+ carry = 0;
105
+ }
106
+ }
107
+ break :seed seed;
108
+ },
79
109
  },
80
110
  };
81
111
  }