@pkmn/engine 0.1.0-dev.04a96f24 → 0.1.0-dev.059a0591

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 (41) hide show
  1. package/README.md +5 -4
  2. package/build/pkg/addon/browser.js +4 -1
  3. package/build/pkg/addon/browser.js.map +1 -1
  4. package/build/pkg/addon/node.js +3 -0
  5. package/build/pkg/addon/node.js.map +1 -1
  6. package/build/pkg/addon/wasm.js +41 -9
  7. package/build/pkg/addon/wasm.js.map +1 -1
  8. package/build/pkg/addon.d.ts +5 -5
  9. package/build/pkg/addon.js +8 -17
  10. package/build/pkg/addon.js.map +1 -1
  11. package/build/pkg/data/layout.json +15 -2
  12. package/build/pkg/data/protocol.json +23 -9
  13. package/build/pkg/gen1.d.ts +15 -18
  14. package/build/pkg/gen1.js +11 -12
  15. package/build/pkg/gen1.js.map +1 -1
  16. package/build/pkg/index.d.ts +37 -100
  17. package/build/pkg/index.js +6 -68
  18. package/build/pkg/index.js.map +1 -1
  19. package/build/tools/debug.js +0 -1
  20. package/build/tools/debug.js.map +1 -1
  21. package/build/tools/display/index.js +1 -0
  22. package/build/tools/display/index.js.map +1 -1
  23. package/build/tools/display/ui.jsx.map +1 -1
  24. package/build/tools/display/util.js +16 -1
  25. package/build/tools/display/util.js.map +1 -1
  26. package/build.zig +118 -27
  27. package/package.json +23 -124
  28. package/src/bin/install-pkmn-engine +3 -2
  29. package/src/lib/bindings/c.zig +254 -0
  30. package/src/lib/bindings/node.zig +121 -0
  31. package/src/lib/bindings/wasm.zig +59 -0
  32. package/src/lib/common/array.zig +1 -1
  33. package/src/lib/common/optional.zig +1 -1
  34. package/src/lib/common/options.zig +2 -0
  35. package/src/lib/common/util.zig +1 -1
  36. package/src/lib/gen1/calc.zig +9 -9
  37. package/src/lib/gen1/mechanics.zig +52 -21
  38. package/src/lib/gen2/mechanics.zig +22 -11
  39. package/src/lib/node.zig +2 -113
  40. package/src/lib/pkmn.zig +3 -2
  41. package/src/lib/wasm.zig +14 -9
@@ -1,5 +1,6 @@
1
1
  const DEBUG = @import("../common/debug.zig").print;
2
2
 
3
+ const builtin = @import("builtin");
3
4
  const chance = @import("chance.zig");
4
5
  const common = @import("../common/data.zig");
5
6
  const data = @import("data.zig");
@@ -165,7 +166,10 @@ fn selectMove(
165
166
  if (foe_choice.type == .Switch) {
166
167
  const last = side.active.move(battle.lastMove(player).index);
167
168
  if (last.id == .Metronome) side.last_selected_move = last.id;
168
- if (last.id == .MirrorMove) return Result.Error;
169
+ if (last.id == .MirrorMove) {
170
+ if (!pkmn.options.mod) return Result.Error;
171
+ side.last_selected_move = last.id;
172
+ }
169
173
  }
170
174
  }
171
175
  return null;
@@ -416,7 +420,7 @@ fn executeMove(
416
420
  mslot = @intCast(battle.lastMove(player).index);
417
421
  const stored = side.stored();
418
422
  // GLITCH: Struggle bypass PP underflow via Hyper Beam / Trapping-switch auto selection
419
- auto = isForced(&side.active) or
423
+ auto = pkmn.options.mod or isForced(&side.active) or
420
424
  side.active.volatiles.Binding or side.active.volatiles.Bide or
421
425
  side.last_selected_move == .HyperBeam or
422
426
  Status.is(stored.status, .FRZ) or Status.is(stored.status, .SLP);
@@ -449,6 +453,25 @@ fn executeMove(
449
453
  .err => return @as(?Result, Result.Error),
450
454
  }
451
455
 
456
+ // Pokémon Showdown incorrectly implements PP deduction when handling the Hyper Beam automatic
457
+ // selection glitch - if the move was proced due to Metronome / Mirror Move it either subtracts
458
+ // from the Hyper Beam slot if present or skips PP deduction entirely...
459
+ if (showdown and !skip_pp and side.last_selected_move == .HyperBeam and
460
+ side.last_selected_move != side.active.move(mslot).id)
461
+ {
462
+ assert(mslot == 1);
463
+ const has_beam = has_beam: {
464
+ for (side.active.moves, 0..) |m, i| {
465
+ if (m.id == .HyperBeam) {
466
+ mslot = @intCast(i + 1);
467
+ break :has_beam true;
468
+ }
469
+ }
470
+ break :has_beam false;
471
+ };
472
+ if (!has_beam) skip_pp = true;
473
+ }
474
+
452
475
  const can = skip_can or
453
476
  try canMove(battle, player, mslot, auto, skip_pp, .None, residual, options);
454
477
  if (!can) return null;
@@ -546,7 +569,7 @@ fn beforeMove(
546
569
  // This can only happen if a Pokémon started the battle frozen/sleeping and was thawed/woken
547
570
  // before the side had a selected a move - we simply need to assume this leads to a desync
548
571
  if (side.last_selected_move == .None) {
549
- assert(!showdown);
572
+ assert(!pkmn.options.mod);
550
573
  return .err;
551
574
  }
552
575
 
@@ -765,9 +788,14 @@ fn canMove(
765
788
 
766
789
  if (move.effect == .Thrashing) {
767
790
  Effects.thrashing(battle, player, options);
768
- } else if (!showdown and move.effect == .Binding) {
769
- // Pokémon Showdown handles this after hit/miss checks and damage calculation
770
- try Effects.binding(battle, player, false, options);
791
+ } else if (move.effect == .Binding) {
792
+ // Pokémon Showdown handles this after hit/miss checks and damage calculation, though clears
793
+ // Recharging in onTryMove (required for the Hyper Beam automatic selection glitch)
794
+ if (showdown) {
795
+ battle.foe(player).active.volatiles.Recharging = false;
796
+ } else {
797
+ try Effects.binding(battle, player, false, options);
798
+ }
771
799
  }
772
800
 
773
801
  return true;
@@ -1121,7 +1149,7 @@ fn calcDamage(
1121
1149
  if ((!showdown or !cfz) and (atk > 255 or def > 255)) {
1122
1150
  atk = @max((atk / 4) & 255, 1);
1123
1151
  // GLITCH: not adjusted to be a min of 1 on cartridge (can lead to division-by-zero freeze)
1124
- def = @max((def / 4) & 255, if (showdown) 1 else 0);
1152
+ def = @max((def / 4) & 255, if (pkmn.options.mod) 1 else 0);
1125
1153
  }
1126
1154
 
1127
1155
  const lvl: u32 = side.stored().level * @as(u2, if (crit) 2 else 1);
@@ -1142,7 +1170,7 @@ fn calcDamage(
1142
1170
  // encountered so we need to make sure that in the event of a miss we commit the status of the
1143
1171
  // crit roll. The alternative to doing this extra work is to *always* commit the crit roll but
1144
1172
  // that results in counter intuitive probabilities in the common case
1145
- if (pkmn.options.chance and !showdown) {
1173
+ if (pkmn.options.chance and !pkmn.options.mod and move.effect != .Explode) {
1146
1174
  atk = attack(side, !crit, special);
1147
1175
  def = defense(target, !crit, special, false);
1148
1176
  if ((atk > 255 or def > 255) and (def / 4) & 255 == 0) options.chance.glitch();
@@ -1227,13 +1255,13 @@ fn specialDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
1227
1255
  .Psywave => power: {
1228
1256
  const max: u8 = @intCast(@as(u16, side.stored().level) * 3 / 2);
1229
1257
  // GLITCH: Psywave infinite glitch loop
1230
- if (!showdown and max <= 1) return Result.Error;
1258
+ if (!pkmn.options.mod and max <= 1) return Result.Error;
1231
1259
  break :power try Rolls.psywave(battle, player, max, options);
1232
1260
  },
1233
1261
  else => unreachable,
1234
1262
  };
1235
1263
 
1236
- if (battle.last_damage == 0) return if (showdown) null else Result.Error;
1264
+ if (battle.last_damage == 0) return if (pkmn.options.mod) null else Result.Error;
1237
1265
 
1238
1266
  const sub = showdown and foe.active.volatiles.Substitute;
1239
1267
  _ = try applyDamage(battle, player.foe(), player.foe(), .None, options);
@@ -1270,12 +1298,16 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
1270
1298
 
1271
1299
  if (!used or !selected) {
1272
1300
  // GLITCH: Counter desync (covered by Desync Clause Mod on Pokémon Showdown)
1273
- if (!showdown) return Result.Error;
1301
+ if (!pkmn.options.mod) return Result.Error;
1274
1302
  try options.log.fail(.{ battle.active(player), .None });
1275
1303
  return null;
1276
1304
  }
1277
1305
 
1278
- battle.last_damage *|= 2;
1306
+ // NOTE: llvm/llvm-project#58557
1307
+ battle.last_damage = if (comptime builtin.target.isWasm())
1308
+ @max(battle.last_damage *% 2, std.math.maxInt(u16))
1309
+ else
1310
+ battle.last_damage *| 2;
1279
1311
 
1280
1312
  // Pokémon Showdown calls moveHit before Counter
1281
1313
  if (!showdown and !try checkHit(battle, player, move, options)) return null;
@@ -1517,7 +1549,7 @@ fn faint(battle: anytype, player: Player, done: bool, cap: bool, options: anytyp
1517
1549
  assert(!foe_volatiles.MultiHit);
1518
1550
  if (foe_volatiles.Bide) {
1519
1551
  assert(!foe_volatiles.Thrashing and !foe_volatiles.Rage);
1520
- foe_volatiles.state = if (showdown) 0 else foe_volatiles.state & 255;
1552
+ foe_volatiles.state = if (pkmn.options.mod) 0 else foe_volatiles.state & 255;
1521
1553
  if (foe_volatiles.state != 0) return Result.Error;
1522
1554
  }
1523
1555
 
@@ -1608,12 +1640,12 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
1608
1640
 
1609
1641
  battle.turn += 1;
1610
1642
 
1611
- if (showdown and pkmn.options.ebc and checkEBC(battle)) {
1643
+ if (pkmn.options.mod and pkmn.options.ebc and checkEBC(battle)) {
1612
1644
  try options.log.tie(.{});
1613
1645
  return Result.Tie;
1614
1646
  }
1615
1647
 
1616
- if (showdown and battle.turn >= 1000) {
1648
+ if (pkmn.options.mod and battle.turn >= 1000) {
1617
1649
  try options.log.tie(.{});
1618
1650
  return Result.Tie;
1619
1651
  } else if (battle.turn >= 65535) {
@@ -1908,12 +1940,11 @@ pub const Effects = struct {
1908
1940
 
1909
1941
  // Technically this is still considered simply a "miss" on the cartridge,
1910
1942
  // but diverging from Pokémon Showdown here would mostly just be pedantic
1911
- if (n == 0) {
1943
+ if (n == 0 or (pkmn.options.mod and err)) {
1912
1944
  try options.log.fail(.{ foe_ident, .None });
1913
1945
  return null;
1914
1946
  } else if (err) {
1915
1947
  // GLITCH: Transform + Mirror Move / Metronome PP softlock
1916
- assert(!showdown);
1917
1948
  return Result.Error;
1918
1949
  }
1919
1950
 
@@ -1991,7 +2022,7 @@ pub const Effects = struct {
1991
2022
  if (foe.active.types.includes(move.type)) return;
1992
2023
  if (!try Rolls.secondaryChance(battle, player, true, options)) return;
1993
2024
  // Freeze Clause Mod
1994
- if (showdown) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
2025
+ if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
1995
2026
 
1996
2027
  foe_stored.status = Status.init(.FRZ);
1997
2028
  // GLITCH: Hyper Beam recharging status is not cleared
@@ -2332,7 +2363,7 @@ pub const Effects = struct {
2332
2363
  }
2333
2364
 
2334
2365
  // Sleep Clause Mod
2335
- if (showdown) {
2366
+ if (pkmn.options.mod) {
2336
2367
  for (foe.pokemon) |p| {
2337
2368
  if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
2338
2369
  }
@@ -2438,7 +2469,7 @@ pub const Effects = struct {
2438
2469
  if (side.active.volatiles.Binding) return;
2439
2470
  side.active.volatiles.Binding = true;
2440
2471
  // GLITCH: Hyper Beam automatic selection glitch if Recharging gets cleared on miss
2441
- // (Pokémon Showdown unitentionally patches this glitch, preventing automatic selection)
2472
+ // (Pokémon Showdown clears Recharging in onTryMove instead)
2442
2473
  if (!showdown) {
2443
2474
  foe.active.volatiles.Recharging = false;
2444
2475
  } else if (foe.stored().hp == 0) return if (!rewrap) battle.rng.advance(1);
@@ -3204,7 +3235,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3204
3235
  // Struggle (Pokémon Showdown would use 'move 1' here)
3205
3236
  if (n == before) {
3206
3237
  // GLITCH: Transform + Mirror Move / Metronome PP softlock
3207
- if (!showdown) {
3238
+ if (!pkmn.options.mod) {
3208
3239
  while (slot <= 4) : (slot += 1) {
3209
3240
  if (active.moves[slot - 1].pp != 0) return n;
3210
3241
  }
@@ -1,5 +1,6 @@
1
1
  const DEBUG = @import("../common/debug.zig").print;
2
2
 
3
+ const builtin = @import("builtin");
3
4
  const common = @import("../common/data.zig");
4
5
  const data = @import("data.zig");
5
6
  const generated = @import("generated.zig");
@@ -1192,7 +1193,7 @@ fn updateSideCondition(
1192
1193
  fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
1193
1194
  battle.turn += 1;
1194
1195
 
1195
- if (showdown and battle.turn >= 1000) {
1196
+ if (pkmn.options.mod and battle.turn >= 1000) {
1196
1197
  try options.log.tie(.{});
1197
1198
  return Result.Tie;
1198
1199
  } else if (battle.turn >= 65535) {
@@ -1426,7 +1427,8 @@ pub const Effects = struct {
1426
1427
  if (move.type.special() != (last == .MirrorCoat)) return;
1427
1428
  if (state.damage == 0) return;
1428
1429
 
1429
- state.damage *|= 2;
1430
+ // NOTE: llvm/llvm-project#58557
1431
+ state.damage = smul(state.damage, 2);
1430
1432
  state.miss = false;
1431
1433
  }
1432
1434
 
@@ -1521,15 +1523,18 @@ pub const Effects = struct {
1521
1523
 
1522
1524
  pub const double = struct {
1523
1525
  pub fn flying(battle: anytype, player: Player, state: *State, _: anytype) !void {
1524
- if (battle.foe(player).active.volatiles.Flying) state.damage *|= 2;
1526
+ if (!battle.foe(player).active.volatiles.Flying) return;
1527
+ state.damage = smul(state.damage, 2);
1525
1528
  }
1526
1529
 
1527
1530
  pub fn underground(battle: anytype, player: Player, state: *State, _: anytype) !void {
1528
- if (battle.foe(player).active.volatiles.Underground) state.damage *|= 2;
1531
+ if (!battle.foe(player).active.volatiles.Underground) return;
1532
+ state.damage = smul(state.damage, 2);
1529
1533
  }
1530
1534
 
1531
1535
  pub fn minimize(battle: anytype, player: Player, state: *State, _: anytype) !void {
1532
- if (battle.foe(player).active.volatiles.minimized) state.damage *|= 2;
1536
+ if (!battle.foe(player).active.volatiles.minimized) return;
1537
+ state.damage = smul(state.damage, 2);
1533
1538
  }
1534
1539
  };
1535
1540
 
@@ -1698,7 +1703,7 @@ pub const Effects = struct {
1698
1703
  if (!state.proc) return;
1699
1704
  if (foe.conditions.Safeguard) return;
1700
1705
  // Freeze Clause Mod
1701
- if (showdown) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
1706
+ if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
1702
1707
 
1703
1708
  foe_stored.status = Status.init(.FRZ);
1704
1709
  try options.log.status(.{ foe_ident, foe_stored.status, .None });
@@ -1727,7 +1732,7 @@ pub const Effects = struct {
1727
1732
  state.bp *= 2;
1728
1733
  assert(state.bp <= 160);
1729
1734
  } else {
1730
- state.damage *|= 2;
1735
+ state.damage = smul(state.damage, 2);
1731
1736
  }
1732
1737
  }
1733
1738
 
@@ -2198,7 +2203,7 @@ pub const Effects = struct {
2198
2203
 
2199
2204
  pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
2200
2205
  // TODO: remove switching variable and just look at Choice saved in Battle struct?
2201
- if (battle.foe(player).active.volatiles.switching) state.damage *|= 2;
2206
+ if (battle.foe(player).active.volatiles.switching) state.damage = smul(state.damage, 2);
2202
2207
  }
2203
2208
 
2204
2209
  pub const rage = struct {
@@ -2211,7 +2216,7 @@ pub const Effects = struct {
2211
2216
  const side = battle.side(player);
2212
2217
 
2213
2218
  assert(side.active.volatiles.Rage);
2214
- state.damage *|= (side.active.volatiles.rage +| 1);
2219
+ state.damage = smul(state.damage, (side.active.volatiles.rage +| 1)); // TODO: |+
2215
2220
  }
2216
2221
  };
2217
2222
 
@@ -2306,7 +2311,7 @@ pub const Effects = struct {
2306
2311
  side.active.volatiles.attacks += 1;
2307
2312
  side.active.volatiles.Rollout = side.active.volatiles.attacks != 5;
2308
2313
 
2309
- if (side.active.volatiles.DefenseCurl) state.damage *|= 2;
2314
+ if (side.active.volatiles.DefenseCurl) state.damage = smul(state.damage, 2);
2310
2315
  }
2311
2316
  };
2312
2317
 
@@ -2376,7 +2381,7 @@ pub const Effects = struct {
2376
2381
  }
2377
2382
 
2378
2383
  // Sleep Clause Mod
2379
- if (showdown) {
2384
+ if (pkmn.options.mod) {
2380
2385
  for (foe.pokemon) |p| {
2381
2386
  if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
2382
2387
  }
@@ -2834,6 +2839,12 @@ fn convertible(active: ActivePokemon, m: Move) bool {
2834
2839
  return t != .@"???" and !active.types.includes(t);
2835
2840
  }
2836
2841
 
2842
+ // NOTE: llvm/llvm-project#58557
2843
+ fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
2844
+ if (!comptime builtin.target.isWasm()) return a *| b;
2845
+ return @max(a *% b, std.math.maxInt(@TypeOf(a)));
2846
+ }
2847
+
2837
2848
  pub const Rolls = struct {
2838
2849
  fn speedTie(battle: anytype, options: anytype) !bool {
2839
2850
  const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
package/src/lib/node.zig CHANGED
@@ -1,5 +1,5 @@
1
1
  const js = @import("common/js.zig");
2
- const pkmn = @import("pkmn.zig");
2
+ const node = @import("bindings/node.zig");
3
3
  const std = @import("std");
4
4
 
5
5
  const assert = std.debug.assert;
@@ -11,119 +11,8 @@ const c = @cImport({
11
11
 
12
12
  export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
13
13
  const properties = [_]c.napi_property_descriptor{
14
- js.Property.init("engine", .{ .value = register(env) }),
14
+ js.Property.init("engine", .{ .value = node.register(env) }),
15
15
  };
16
16
  assert(c.napi_define_properties(env, exports, properties.len, &properties) == c.napi_ok);
17
17
  return exports;
18
18
  }
19
-
20
- pub fn register(env: c.napi_env) c.napi_value {
21
- const object = js.Object.init(env);
22
- const properties = [_]c.napi_property_descriptor{
23
- js.Property.init("options", .{ .value = options(env) }),
24
- js.Property.init("bindings", .{ .value = bindings(env) }),
25
- };
26
- assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
27
- return object;
28
- }
29
-
30
- fn options(env: c.napi_env) c.napi_value {
31
- const object = js.Object.init(env);
32
- const properties = [_]c.napi_property_descriptor{
33
- js.Property.init("showdown", .{ .value = js.Boolean.init(env, pkmn.options.showdown) }),
34
- js.Property.init("log", .{ .value = js.Boolean.init(env, pkmn.options.log) }),
35
- js.Property.init("chance", .{ .value = js.Boolean.init(env, pkmn.options.chance) }),
36
- js.Property.init("calc", .{ .value = js.Boolean.init(env, pkmn.options.calc) }),
37
- };
38
- assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
39
- return object;
40
- }
41
-
42
- fn bindings(env: c.napi_env) c.napi_value {
43
- const array = js.Array.init(env, .{ .length = 1 });
44
- js.Array.set(env, array, 0, bind(env, pkmn.gen1));
45
- return array;
46
- }
47
-
48
- fn bind(env: c.napi_env, gen: anytype) c.napi_value {
49
- const choices_size: u32 = @intCast(gen.CHOICES_SIZE);
50
- const logs_size: u32 = @intCast(gen.LOGS_SIZE);
51
- const object = js.Object.init(env);
52
- const properties = [_]c.napi_property_descriptor{
53
- js.Property.init("CHOICES_SIZE", .{ .value = js.Number.init(env, choices_size) }),
54
- js.Property.init("LOGS_SIZE", .{ .value = js.Number.init(env, logs_size) }),
55
- js.Property.init("update", .{ .method = update(gen) }),
56
- js.Property.init("choices", .{ .method = choices(gen) }),
57
- };
58
- assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
59
- return object;
60
- }
61
-
62
- fn update(gen: anytype) c.napi_callback {
63
- return struct {
64
- fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
65
- var argc: usize = 4;
66
- var argv: [4]c.napi_value = undefined;
67
- assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
68
- assert(argc == 4);
69
-
70
- var data: ?*anyopaque = undefined;
71
- var len: usize = 0;
72
- assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
73
- assert(len == @sizeOf(gen.Battle(gen.PRNG)));
74
-
75
- var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
76
- const c1: pkmn.Choice = @bitCast(js.Number.get(env, argv[1], u8));
77
- const c2: pkmn.Choice = @bitCast(js.Number.get(env, argv[2], u8));
78
-
79
- var vtype: c.napi_valuetype = undefined;
80
- assert(c.napi_typeof(env, argv[3], &vtype) == c.napi_ok);
81
- const result = switch (vtype) {
82
- c.napi_undefined, c.napi_null => battle.update(c1, c2, &gen.NULL),
83
- else => result: {
84
- assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
85
- assert(len == gen.LOGS_SIZE);
86
-
87
- const buf = @as([*]u8, @ptrCast(data.?))[0..gen.LOGS_SIZE];
88
- var stream: pkmn.protocol.ByteStream = .{ .buffer = buf };
89
- // TODO: extract out
90
- var opts = pkmn.battle.options(
91
- pkmn.protocol.FixedLog{ .writer = stream.writer() },
92
- gen.chance.NULL,
93
- gen.calc.NULL,
94
- );
95
- break :result battle.update(c1, c2, &opts);
96
- },
97
- } catch unreachable;
98
-
99
- return js.Number.init(env, @as(u8, @bitCast(result)));
100
- }
101
- }.call;
102
- }
103
-
104
- fn choices(gen: anytype) c.napi_callback {
105
- return struct {
106
- fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
107
- var argc: usize = 4;
108
- var argv: [4]c.napi_value = undefined;
109
- assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
110
- assert(argc == 4);
111
-
112
- var data: ?*anyopaque = undefined;
113
- var len: usize = 0;
114
- assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
115
- assert(len == @sizeOf(gen.Battle(gen.PRNG)));
116
-
117
- var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
118
- const player: pkmn.Player = @enumFromInt(js.Number.get(env, argv[1], u8));
119
- const request: pkmn.Choice.Type = @enumFromInt(js.Number.get(env, argv[2], u8));
120
-
121
- assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
122
- assert(len == gen.CHOICES_SIZE);
123
-
124
- const out = @as([*]pkmn.Choice, @ptrCast(data.?))[0..gen.CHOICES_SIZE];
125
- const n = battle.choices(player, request, out);
126
- return js.Number.init(env, @as(u8, @bitCast(n)));
127
- }
128
- }.call;
129
- }
package/src/lib/pkmn.zig CHANGED
@@ -129,6 +129,7 @@ pub const gen2 = struct {
129
129
  // NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
130
130
  pub const js = if (options.internal) @import("common/js.zig") else {};
131
131
  pub const bindings = if (options.internal) struct {
132
- pub const node = @import("node.zig");
133
- pub const wasm = @import("wasm.zig");
132
+ pub const c = @import("bindings/c.zig");
133
+ pub const node = @import("bindings/node.zig");
134
+ pub const wasm = @import("bindings/wasm.zig");
134
135
  } else {};
package/src/lib/wasm.zig CHANGED
@@ -1,12 +1,17 @@
1
- const pkmn = @import("pkmn.zig");
2
1
  const std = @import("std");
2
+ const wasm = @import("bindings/wasm.zig");
3
3
 
4
- export const SHOWDOWN = pkmn.options.showdown;
5
- export const LOG = pkmn.options.log;
6
- export const CHANCE = pkmn.options.chance;
7
- export const CALC = pkmn.options.calc;
4
+ export const SHOWDOWN = wasm.options.showdown;
5
+ export const LOG = wasm.options.log;
6
+ export const CHANCE = wasm.options.chance;
7
+ export const CALC = wasm.options.calc;
8
8
 
9
- export const GEN1_CHOICES_SIZE =
10
- std.math.ceilPowerOfTwo(u32, @as(u32, @intCast(pkmn.gen1.CHOICES_SIZE))) catch unreachable;
11
- export const GEN1_LOGS_SIZE =
12
- std.math.ceilPowerOfTwo(u32, @as(u32, @intCast(pkmn.gen1.LOGS_SIZE))) catch unreachable;
9
+ export const GEN1_CHOICES_SIZE = wasm.gen(1).CHOICES_SIZE;
10
+ export const GEN1_LOGS_SIZE = wasm.gen(1).LOGS_SIZE;
11
+
12
+ const exportable = @hasDecl(std.zig, "Zir") and !@hasField(std.zig.Zir.Inst.Tag, "export_value");
13
+
14
+ usingnamespace if (exportable) struct {
15
+ export const GEN1_update = wasm.gen(1).update;
16
+ export const GEN1_choices = wasm.gen(1).choices;
17
+ } else wasm.exports();