@pkmn/engine 0.1.0-dev.43ee07e2 → 0.1.0-dev.4438e918

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 (62) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +22 -12
  3. package/build/pkg/addon/browser.js +4 -1
  4. package/build/pkg/addon/browser.js.map +1 -1
  5. package/build/pkg/addon/node.js +20 -7
  6. package/build/pkg/addon/node.js.map +1 -1
  7. package/build/pkg/addon/wasm.js +41 -9
  8. package/build/pkg/addon/wasm.js.map +1 -1
  9. package/build/pkg/addon.d.ts +7 -7
  10. package/build/pkg/addon.js +8 -17
  11. package/build/pkg/addon.js.map +1 -1
  12. package/build/pkg/data/layout.json +15 -2
  13. package/build/pkg/data/protocol.json +23 -9
  14. package/build/pkg/data.js.map +1 -1
  15. package/build/pkg/gen1.d.ts +15 -18
  16. package/build/pkg/gen1.js +28 -19
  17. package/build/pkg/gen1.js.map +1 -1
  18. package/build/pkg/index.d.ts +37 -100
  19. package/build/pkg/index.js +23 -75
  20. package/build/pkg/index.js.map +1 -1
  21. package/build/pkg/protocol.d.ts +11 -2
  22. package/build/pkg/protocol.js.map +1 -1
  23. package/build/tools/debug.js +54 -11
  24. package/build/tools/debug.js.map +1 -1
  25. package/build/tools/display/debug.jsx +19 -94
  26. package/build/tools/display/debug.jsx.map +1 -1
  27. package/build/tools/display/index.js +18 -7
  28. package/build/tools/display/index.js.map +1 -1
  29. package/build/tools/display/ui.jsx +20 -9
  30. package/build/tools/display/ui.jsx.map +1 -1
  31. package/build/tools/display/util.js +140 -1
  32. package/build/tools/display/util.js.map +1 -1
  33. package/build.zig +185 -202
  34. package/package.json +31 -130
  35. package/src/bin/install-pkmn-engine +25 -9
  36. package/src/lib/bindings/c.zig +200 -0
  37. package/src/lib/bindings/node.zig +117 -0
  38. package/src/lib/bindings/wasm.zig +50 -0
  39. package/src/lib/common/array.zig +5 -12
  40. package/src/lib/common/debug.zig +33 -59
  41. package/src/lib/common/js.zig +3 -9
  42. package/src/lib/common/optional.zig +11 -21
  43. package/src/lib/common/protocol.zig +224 -189
  44. package/src/lib/common/rational.zig +275 -284
  45. package/src/lib/common/util.zig +14 -31
  46. package/src/lib/gen1/calc.zig +139 -117
  47. package/src/lib/gen1/chance.zig +112 -75
  48. package/src/lib/gen1/data/types.zig +2 -2
  49. package/src/lib/gen1/data.zig +5 -5
  50. package/src/lib/gen1/helpers.zig +8 -10
  51. package/src/lib/gen1/mechanics.zig +50 -43
  52. package/src/lib/gen2/calc.zig +3 -6
  53. package/src/lib/gen2/chance.zig +12 -18
  54. package/src/lib/gen2/data/types.zig +6 -3
  55. package/src/lib/gen2/data.zig +8 -7
  56. package/src/lib/gen2/helpers.zig +4 -6
  57. package/src/lib/gen2/mechanics.zig +36 -25
  58. package/src/lib/napi.h +2 -0
  59. package/src/lib/node.zig +3 -118
  60. package/src/lib/pkmn.zig +94 -19
  61. package/src/lib/wasm.zig +10 -9
  62. package/src/tools/display/dom.ts +43 -31
@@ -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");
@@ -129,11 +130,11 @@ fn start(battle: anytype, options: anytype) !Result {
129
130
 
130
131
  const p1_slot = findFirstAlive(p1);
131
132
  assert(!showdown or p1_slot == 1);
132
- if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
133
+ if (p1_slot == 0) return if (findFirstAlive(p2) == 0) .Tie else .Lose;
133
134
 
134
135
  const p2_slot = findFirstAlive(p2);
135
136
  assert(!showdown or p2_slot == 1);
136
- if (p2_slot == 0) return Result.Win;
137
+ if (p2_slot == 0) return .Win;
137
138
 
138
139
  try switchIn(battle, .P1, p1_slot, .Initial, options);
139
140
  try switchIn(battle, .P2, p2_slot, .Initial, options);
@@ -681,7 +682,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
681
682
 
682
683
  // Type effectiveness matchup precedence only matters with (NVE, SE)
683
684
  if (!showdown and (eff1 + eff2) == Effectiveness.mismatch and
684
- types.type1.precedence() > types.type2.precedence())
685
+ Type.precedence(state.type, types.type1) > Type.precedence(state.type, types.type2))
685
686
  {
686
687
  assert(eff2 != neutral);
687
688
  d = d *% eff2 / 10;
@@ -818,13 +819,13 @@ fn checkFaint(
818
819
 
819
820
  if (player_out and foe_out) {
820
821
  try options.log.tie(.{});
821
- return Result.Tie;
822
+ return .Tie;
822
823
  } else if (player_out) {
823
824
  try options.log.win(.{player.foe()});
824
- return if (player == .P1) Result.Lose else Result.Win;
825
+ return if (player == .P1) .Lose else .Win;
825
826
  } else if (foe_out) {
826
827
  try options.log.win(.{player});
827
- return if (player == .P1) Result.Win else Result.Lose;
828
+ return if (player == .P1) .Win else .Lose;
828
829
  }
829
830
 
830
831
  const foe_choice: Choice.Type = if (foe_fainted) .Switch else .Pass;
@@ -1194,14 +1195,14 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
1194
1195
 
1195
1196
  if (pkmn.options.mod and battle.turn >= 1000) {
1196
1197
  try options.log.tie(.{});
1197
- return Result.Tie;
1198
+ return .Tie;
1198
1199
  } else if (battle.turn >= 65535) {
1199
- return Result.Error;
1200
+ return .Error;
1200
1201
  }
1201
1202
 
1202
1203
  try options.log.turn(.{battle.turn});
1203
1204
 
1204
- return Result.Default;
1205
+ return .Default;
1205
1206
  }
1206
1207
 
1207
1208
  pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytype) !void {
@@ -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
 
@@ -1553,7 +1558,7 @@ pub const Effects = struct {
1553
1558
  const last = battle.foe(player).lastMove(true);
1554
1559
  const failed = volatiles.Encore or
1555
1560
  (last == .None or last == .Struggle or
1556
- last == .Encore or .last == .MirrorMove);
1561
+ last == .Encore or .last == .MirrorMove);
1557
1562
  if (failed) return options.log.fail(.{ foe_ident, .None });
1558
1563
 
1559
1564
  var slot: u3 = 0;
@@ -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
 
@@ -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.cpu.arch.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|
@@ -3070,7 +3081,7 @@ pub const Rolls = struct {
3070
3081
  }
3071
3082
 
3072
3083
  // Conversion 2 can at most choose between 7 types (vs. a Grass-type attack)
3073
- var CONVERSION_2: [7]Type = [_]Type{.@"???"} ** 7;
3084
+ var CONVERSION_2: [7]Type = @splat(.@"???");
3074
3085
 
3075
3086
  fn conversion2(battle: anytype, player: Player, mtype: Type, options: anytype) !Type {
3076
3087
  var i: u8 = 0;
@@ -3495,11 +3506,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3495
3506
  },
3496
3507
  .Switch => {
3497
3508
  const side = battle.side(player);
3498
- var slot: u4 = 2;
3509
+ var slot: usize = 2;
3499
3510
  while (slot <= 6) : (slot += 1) {
3500
3511
  const id = side.order[slot - 1];
3501
3512
  if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
3502
- out[n] = .{ .type = .Switch, .data = slot };
3513
+ out[n] = .{ .type = .Switch, .data = @intCast(slot) };
3503
3514
  n += 1;
3504
3515
  }
3505
3516
  if (n == 0) {
@@ -3529,11 +3540,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3529
3540
  return n;
3530
3541
  }
3531
3542
 
3532
- var slot: u4 = 2;
3543
+ var slot: usize = 2;
3533
3544
  while (!active.volatiles.Trapped and slot <= 6) : (slot += 1) {
3534
3545
  const id = side.order[slot - 1];
3535
3546
  if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
3536
- out[n] = .{ .type = .Switch, .data = slot };
3547
+ out[n] = .{ .type = .Switch, .data = @intCast(slot) };
3537
3548
  n += 1;
3538
3549
  }
3539
3550
 
@@ -3566,7 +3577,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3566
3577
  const struggle =
3567
3578
  m.id == .Bide and (m.pp == 0 or active.volatiles.disable.move == slot);
3568
3579
  const s = if (struggle) 0 else slot;
3569
- out[n] = .{ .type = .Move, .data = s };
3580
+ out[n] = .{ .type = .Move, .data = @intCast(s) };
3570
3581
  n += 1;
3571
3582
  return n;
3572
3583
  }
@@ -3580,7 +3591,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3580
3591
  if (m.id == .None) break;
3581
3592
  if (m.pp == 0) continue;
3582
3593
  if (active.volatiles.disable.move == slot) continue;
3583
- out[n] = .{ .type = .Move, .data = slot };
3594
+ out[n] = .{ .type = .Move, .data = @intCast(slot) };
3584
3595
  n += 1;
3585
3596
  }
3586
3597
  // Struggle (Pokémon Showdown would use 'move 1' here)
package/src/lib/napi.h ADDED
@@ -0,0 +1,2 @@
1
+ #define NAPI_VERSION 8
2
+ #include <node_api.h>
package/src/lib/node.zig CHANGED
@@ -1,129 +1,14 @@
1
+ const c = @import("napi");
1
2
  const js = @import("common/js.zig");
2
- const pkmn = @import("pkmn.zig");
3
+ const node = @import("bindings/node.zig");
3
4
  const std = @import("std");
4
5
 
5
6
  const assert = std.debug.assert;
6
7
 
7
- const c = @cImport({
8
- @cDefine("NAPI_VERSION", "8");
9
- @cInclude("node_api.h");
10
- });
11
-
12
8
  export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
13
9
  const properties = [_]c.napi_property_descriptor{
14
- js.Property.init("engine", .{ .value = register(env) }),
10
+ js.Property.init("engine", .{ .value = node.register(env) }),
15
11
  };
16
12
  assert(c.napi_define_properties(env, exports, properties.len, &properties) == c.napi_ok);
17
13
  return exports;
18
14
  }
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
@@ -22,9 +22,9 @@ pub const Choice = @import("common/data.zig").Choice;
22
22
  /// considered terminal.
23
23
  pub const Result = @import("common/data.zig").Result;
24
24
 
25
- // TODO
25
+ /// Helpers for working with bit-packed arrays of non-powers-of-2 types.
26
26
  pub const Array = @import("common/array.zig").Array;
27
- // TODO
27
+ /// Optimized optional representation which stores the empty None value as a sentinel.
28
28
  pub const Optional = @import("common/optional.zig").Optional;
29
29
 
30
30
  /// Pokémon Showdown's RNG (backed by a Generation V & VI RNG).
@@ -32,31 +32,78 @@ pub const PSRNG = @import("common/rng.zig").PSRNG;
32
32
 
33
33
  /// Namespace for helpers related to protocol message logging.
34
34
  pub const protocol = if (options.internal) @import("common/protocol.zig") else struct {
35
- /// Logs protocol information to its `Writer` during a battle update when
35
+ /// Logs protocol information to its `WriterT` during a battle update when
36
36
  /// `options.log` is enabled.
37
37
  pub const Log = @import("common/protocol.zig").Log;
38
- /// Stripped down version of `std.io.FixedBufferStream` optimized for
39
- /// efficiently writing the individual protocol bytes. Note that the
40
- /// `ByteStream.Writer` is **not** a `std.io.Writer` and should not be
41
- /// used for general purpose writing.
42
- pub const ByteStream = @import("common/protocol.zig").ByteStream;
43
- /// `Log` type backed by the optimized `ByteStream.Writer`. Intended to be
44
- /// intialized with a `LOGS_SIZE`-sized buffer.
38
+ /// `Log` type backed by the optimized `Writer`. Intended to be intialized with
39
+ /// a `LOGS_SIZE`-sized buffer.
45
40
  pub const FixedLog = @import("common/protocol.zig").FixedLog;
46
- /// Null object pattern implementation of `Log` backed by a
47
- /// `std.io.null_writer`. Ignores anything sent to it, though protocol
48
- /// logging should additionally be turned off entirely with `options.log`.
41
+ /// Minimal logging helper optimized for efficiently writing the individual
42
+ /// protocol bytes into a fixed buffer. Note that this is **not** a
43
+ /// `std.Io.Writer` and should not be used for general purpose writing.
44
+ pub const Writer = @import("common/protocol.zig").Writer;
45
+ /// Null object pattern implementation of `Log` backed by a null writer.
46
+ /// Ignores anything sent to it, though protocol logging should additionally
47
+ /// be turned off entirely with `options.log`.
49
48
  pub const NULL = @import("common/protocol.zig").NULL;
50
49
  };
51
50
 
52
- pub usingnamespace @import("common/rational.zig");
51
+ /// Specialization of a rational number used by the engine to compute probabilties.
52
+ pub const Rational = @import("common/rational.zig").Rational;
53
53
 
54
54
  /// Namespace for cross-generation battle-related types.
55
55
  pub const battle = @import("common/battle.zig");
56
56
 
57
57
  /// Namespace for Generation I Pokémon.
58
58
  pub const gen1 = struct {
59
- pub usingnamespace @import("gen1/data.zig");
59
+ const data = @import("gen1/data.zig");
60
+ /// The minimum size in bytes required to hold all Generation I choice options.
61
+ pub const MAX_CHOICES = data.MAX_CHOICES;
62
+ /// The maximum number of bytes possibly logged by a single Generation I update.
63
+ pub const MAX_LOGS = data.MAX_LOGS;
64
+ /// The optimal size in bytes required to hold all Generation I choice options.
65
+ /// At least as large as MAX_CHOICES.
66
+ pub const CHOICES_SIZE = data.CHOICES_SIZE;
67
+ /// The optimal size in bytes required to hold the largest amount of log data possible from a
68
+ /// single Generation I update. At least as large as MAX_LOGS.
69
+ pub const LOGS_SIZE = data.LOGS_SIZE;
70
+ /// Null object pattern implementation of pkmn.battle.Options for Generation I.
71
+ pub const NULL = data.NULL;
72
+ /// The pseudo random number generator used by Generation I.
73
+ pub const PRNG = data.PRNG;
74
+ /// Representation of a Generation I battle.
75
+ pub const Battle = data.Battle;
76
+ /// Representation of one side of a Generation I Pokémon battle.
77
+ pub const Side = data.Side;
78
+ /// Representation of the state for single Generation I Pokémon while active in battle.
79
+ pub const ActivePokemon = data.ActivePokemon;
80
+ /// Representation of the state for single Generation I Pokémon while inactive in the party.
81
+ pub const Pokemon = data.Pokemon;
82
+ /// Representation of a Generation I Pokémon's move slot in a battle.
83
+ pub const MoveSlot = data.MoveSlot;
84
+ /// Details required to detect desyncs based on the move last selected/executed by players.
85
+ pub const MoveDetails = data.MoveDetails;
86
+ /// Bitfield representation of a Generation I & II Pokémon's major status condition.
87
+ pub const Status = data.Status;
88
+ /// Bitfield representation of volatile statuses and associated data in Generation I.
89
+ pub const Volatiles = data.Volatiles;
90
+ /// Representation of a Pokémon's stats in Generation I.
91
+ pub const Stats = data.Stats;
92
+ /// Representation of a Pokémon's boosts in Generation I.
93
+ pub const Boosts = data.Boosts;
94
+ /// Representation of a Generation I Pokémon move.
95
+ pub const Move = data.Move;
96
+ /// Representation of a Generation I Pokémon species.
97
+ pub const Species = data.Species;
98
+ /// Representation of a Generation I type in Pokémon.
99
+ pub const Type = data.Type;
100
+ /// Representation of a Generation I Pokémon's typing.
101
+ pub const Types = data.Types;
102
+ /// Modifiers for the effectiveness of a type vs. another type in Pokémon.
103
+ pub const Effectiveness = data.Effectiveness;
104
+ /// Representation of a Generation I Pokémon's determinant values.
105
+ pub const DVs = data.DVs;
106
+
60
107
  /// Tracks chance actions and their associated probability during a
61
108
  /// Generation I battle update when `options.chance` is enabled.
62
109
  pub const Chance = @import("gen1/chance.zig").Chance;
@@ -107,7 +154,34 @@ pub const gen1 = struct {
107
154
 
108
155
  /// TODO
109
156
  pub const gen2 = struct {
110
- pub usingnamespace @import("gen2/data.zig");
157
+ const data = @import("gen2/data.zig");
158
+ pub const MAX_CHOICES = data.MAX_CHOICES;
159
+ pub const MAX_LOGS = data.MAX_LOGS;
160
+ pub const CHOICES_SIZE = data.CHOICES_SIZE;
161
+ pub const LOGS_SIZE = data.LOGS_SIZE;
162
+ pub const NULL = data.NULL;
163
+ pub const PRNG = data.PRNG;
164
+ pub const Battle = data.Battle;
165
+ pub const Field = data.Field;
166
+ pub const Weather = data.Weather;
167
+ pub const Side = data.Side;
168
+ pub const ActivePokemon = data.ActivePokemon;
169
+ pub const Pokemon = data.Pokemon;
170
+ pub const Gender = data.Gender;
171
+ pub const DVs = data.DVs;
172
+ pub const MoveSlot = data.MoveSlot;
173
+ pub const Status = data.Status;
174
+ pub const TriAttack = data.TriAttack;
175
+ pub const Volatiles = data.Volatiles;
176
+ pub const Stats = data.Stats;
177
+ pub const Boosts = data.Boosts;
178
+ pub const Item = data.Item;
179
+ pub const Move = data.Move;
180
+ pub const Species = data.Species;
181
+ pub const Type = data.Type;
182
+ pub const Types = data.Types;
183
+ pub const Effectiveness = data.Effectiveness;
184
+
111
185
  pub const Chance = @import("gen2/chance.zig").Chance;
112
186
  pub const chance = struct {
113
187
  pub const Actions = @import("gen2/chance.zig").Actions;
@@ -126,9 +200,10 @@ pub const gen2 = struct {
126
200
  };
127
201
 
128
202
  // Internal APIs used by other pkmn libraries, not actually part of the public API.
129
- // NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
130
203
  pub const js = if (options.internal) @import("common/js.zig") else {};
204
+ pub const util = if (options.internal) @import("common/util.zig") else {};
131
205
  pub const bindings = if (options.internal) struct {
132
- pub const node = @import("node.zig");
133
- pub const wasm = @import("wasm.zig");
206
+ pub const c = @import("bindings/c.zig");
207
+ pub const node = @import("bindings/node.zig");
208
+ pub const wasm = @import("bindings/wasm.zig");
134
209
  } else {};
package/src/lib/wasm.zig CHANGED
@@ -1,12 +1,13 @@
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
+ export const GEN1_update = wasm.gen(1).update;
13
+ export const GEN1_choices = wasm.gen(1).choices;
@@ -1,30 +1,22 @@
1
1
  /** @license MIT modified from Vadim Demedes's https://github.com/vadimdemedes/dom-chef */
2
- type Attributes = JSX.IntrinsicElements['div'];
3
- type DocumentFragmentConstructor = typeof DocumentFragment;
4
- type ElementFunction = (props?: any) => HTMLElement;
5
2
 
6
3
  declare global {
7
4
  namespace JSX {
8
- interface Element extends HTMLElement, DocumentFragment {
9
- addEventListener: HTMLElement['addEventListener'];
10
- removeEventListener: HTMLElement['removeEventListener'];
11
- className: HTMLElement['className'];
5
+ type Element = HTMLElement | DocumentFragment;
6
+ interface IntrinsicElements {
7
+ [elemName: string]: Record<string, unknown>;
12
8
  }
13
9
  }
14
10
  }
15
11
 
16
- interface JSXElementClassDocumentFragment extends DocumentFragment, JSX.ElementClass { }
17
- interface Fragment {
18
- prototype: JSXElementClassDocumentFragment;
19
- new(): JSXElementClassDocumentFragment;
20
- }
12
+ type Child = Node | string | number | boolean | null | undefined | Child[];
13
+
14
+ type DocumentFragmentFunction = (props?: any) => DocumentFragment;
15
+ type ElementFunction = (props?: any) => HTMLElement;
21
16
 
22
17
  // https://github.com/preactjs/preact/blob/1bbd687c/src/constants.js#L3
23
18
  const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;
24
19
 
25
- const isFragment = (type: DocumentFragmentConstructor | ElementFunction):
26
- type is DocumentFragmentConstructor => type === DocumentFragment;
27
-
28
20
  const setCSSProps = (element: HTMLElement, style: CSSStyleDeclaration) => {
29
21
  for (const [name, value] of Object.entries(style)) {
30
22
  if (name.startsWith('-')) {
@@ -41,14 +33,14 @@ const setAttribute = (element: HTMLElement, name: string, value: string) => {
41
33
  if (value !== undefined && value !== null) element.setAttribute(name, value);
42
34
  };
43
35
 
44
- const addChildren = (parent: Element | DocumentFragment, children: Node[]) => {
36
+ const addChildren = (parent: Node, children: Child[]) => {
45
37
  for (const child of children) {
46
38
  if (child instanceof Node) {
47
39
  parent.appendChild(child);
48
40
  } else if (Array.isArray(child)) {
49
41
  addChildren(parent, child);
50
42
  } else if (typeof child !== 'boolean' && typeof child !== 'undefined' && child !== null) {
51
- parent.appendChild(document.createTextNode(child));
43
+ parent.appendChild(document.createTextNode(String(child)));
52
44
  }
53
45
  }
54
46
  };
@@ -56,13 +48,33 @@ const addChildren = (parent: Element | DocumentFragment, children: Node[]) => {
56
48
  // https://github.com/facebook/react/blob/3f899089/packages/react-dom/src/shared/DOMProperty.js#L288-L322
57
49
  const FALSIFIABLE_ATTRIBUTES = ['contentEditable', 'draggable', 'spellCheck', 'value'];
58
50
 
59
- export const h = (
60
- type: DocumentFragmentConstructor | ElementFunction | string,
61
- attributes?: Attributes,
62
- ...children: Node[]
63
- ) => {
51
+ export function h<K extends keyof HTMLElementTagNameMap>(
52
+ type: K,
53
+ attributes?: any,
54
+ ...children: Child[]
55
+ ): HTMLElementTagNameMap[K];
56
+ export function h(
57
+ type: DocumentFragmentFunction,
58
+ attributes?: any,
59
+ ...children: Child[]
60
+ ): DocumentFragment;
61
+ export function h<T extends HTMLElement>(
62
+ type: (props: any) => T,
63
+ attributes?: any,
64
+ ...children: Child[]
65
+ ): T;
66
+ export function h(
67
+ type: string,
68
+ attributes?: any,
69
+ ...children: Child[]
70
+ ): HTMLElement;
71
+ export function h(
72
+ type: DocumentFragmentFunction | ElementFunction | string,
73
+ attributes?: any,
74
+ ...children: Child[]
75
+ ): JSX.Element {
64
76
  if (typeof type !== 'string') {
65
- const element = isFragment(type) ? document.createDocumentFragment() : type(attributes);
77
+ const element = type(attributes);
66
78
  addChildren(element, children);
67
79
  return element;
68
80
  }
@@ -78,19 +90,19 @@ export const h = (
78
90
  const existingClassname = element.getAttribute('class') ?? '';
79
91
  setAttribute(element, 'class', (existingClassname + ' ' + String(value)).trim());
80
92
  } else if (name === 'style') {
81
- setCSSProps(element, value);
93
+ setCSSProps(element, value as CSSStyleDeclaration);
82
94
  } else if (name.startsWith('on')) {
83
95
  const eventName = name.slice(2).toLowerCase().replace(/^-/, '');
84
- element.addEventListener(eventName, value);
85
- } else if (name === 'dangerouslySetInnerHTML' && '__html' in value) {
86
- element.innerHTML = value.__html;
96
+ element.addEventListener(eventName, value as EventListenerOrEventListenerObject);
97
+ } else if (name === 'dangerouslySetInnerHTML' && value && (value as any).__html) {
98
+ element.innerHTML = (value as any).__html;
87
99
  } else if (name !== 'key' && (FALSIFIABLE_ATTRIBUTES.includes(name) || value !== false)) {
88
- setAttribute(element, name, value === true ? '' : value);
100
+ setAttribute(element, name, value === true ? '' : String(value));
89
101
  }
90
102
  }
91
103
 
92
104
  return element;
93
- };
105
+ }
94
106
 
95
- export const Fragment =
96
- (typeof DocumentFragment === 'function' ? DocumentFragment : () => {}) as Fragment;
107
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
108
+ export const Fragment = (unused?: any) => document.createDocumentFragment();