@pkmn/engine 0.1.0-dev.3de821b9 → 0.1.0-dev.3ed876db

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 (73) hide show
  1. package/README.md +4 -2
  2. package/build/pkg/addon/browser.d.ts +3 -0
  3. package/build/pkg/addon/browser.js +44 -0
  4. package/build/pkg/addon/browser.js.map +1 -0
  5. package/build/pkg/addon/node.d.ts +3 -0
  6. package/build/pkg/addon/node.js +97 -0
  7. package/build/pkg/addon/node.js.map +1 -0
  8. package/build/pkg/addon/wasm.d.ts +2 -0
  9. package/build/pkg/addon/wasm.js +21 -0
  10. package/build/pkg/addon/wasm.js.map +1 -0
  11. package/build/pkg/addon.d.ts +22 -0
  12. package/build/pkg/addon.js +29 -51
  13. package/build/pkg/addon.js.map +1 -1
  14. package/build/pkg/data.d.ts +23 -1
  15. package/build/pkg/data.js.map +1 -1
  16. package/build/pkg/gen1.js +1 -1
  17. package/build/pkg/gen1.js.map +1 -1
  18. package/build/pkg/index.d.ts +23 -9
  19. package/build/pkg/index.js +17 -11
  20. package/build/pkg/index.js.map +1 -1
  21. package/build/pkg/protocol.d.ts +20 -1
  22. package/build/pkg/protocol.js +105 -103
  23. package/build/pkg/protocol.js.map +1 -1
  24. package/build/tools/debug.js +35 -68
  25. package/build/tools/debug.js.map +1 -1
  26. package/build/tools/display/debug.jsx +141 -0
  27. package/build/tools/display/debug.jsx.map +1 -0
  28. package/build/tools/display/index.js +141 -0
  29. package/build/tools/display/index.js.map +1 -0
  30. package/build/tools/display/ui.jsx +349 -0
  31. package/build/tools/display/ui.jsx.map +1 -0
  32. package/build/tools/display/util.js +18 -0
  33. package/build/tools/display/util.js.map +1 -0
  34. package/build.zig +249 -77
  35. package/package.json +51 -31
  36. package/src/bin/install-pkmn-engine +7 -5
  37. package/src/bin/pkmn-debug +1 -1
  38. package/src/lib/common/array.zig +77 -0
  39. package/src/lib/common/data.zig +8 -8
  40. package/src/lib/common/debug.zig +39 -8
  41. package/src/lib/common/js.zig +4 -2
  42. package/src/lib/common/optional.zig +13 -9
  43. package/src/lib/common/options.zig +4 -0
  44. package/src/lib/common/protocol.zig +44 -43
  45. package/src/lib/common/rational.zig +53 -51
  46. package/src/lib/common/rng.zig +5 -7
  47. package/src/lib/common/util.zig +50 -1
  48. package/src/lib/gen1/calc.zig +247 -108
  49. package/src/lib/gen1/chance.zig +130 -121
  50. package/src/lib/gen1/data/moves.zig +2 -4
  51. package/src/lib/gen1/data/species.zig +1 -3
  52. package/src/lib/gen1/data/types.zig +1 -2
  53. package/src/lib/gen1/data.zig +35 -38
  54. package/src/lib/gen1/helpers.zig +15 -27
  55. package/src/lib/gen1/mechanics.zig +112 -94
  56. package/src/lib/gen2/calc.zig +37 -27
  57. package/src/lib/gen2/chance.zig +86 -66
  58. package/src/lib/gen2/data/items.zig +1 -3
  59. package/src/lib/gen2/data/moves.zig +2 -4
  60. package/src/lib/gen2/data/species.zig +1 -3
  61. package/src/lib/gen2/data/types.zig +1 -2
  62. package/src/lib/gen2/data.zig +26 -29
  63. package/src/lib/gen2/generated.zig +18 -23
  64. package/src/lib/gen2/helpers.zig +9 -18
  65. package/src/lib/gen2/mechanics.zig +62 -73
  66. package/src/lib/node.zig +2 -4
  67. package/src/lib/pkmn.zig +11 -5
  68. package/src/lib/wasm.zig +1 -2
  69. package/src/tools/display/dom.ts +96 -0
  70. package/src/tools/display/ui.css +354 -0
  71. package/build/test/display.js +0 -405
  72. package/build/test/display.js.map +0 -1
  73. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -1,31 +1,22 @@
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;
18
+
19
+ const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
29
20
 
30
21
  pub const Options = struct {
31
22
  cleric: bool = showdown,
@@ -151,7 +142,7 @@ pub const Pokemon = struct {
151
142
  var pokemon = data.Pokemon{};
152
143
  pokemon.species = p.species;
153
144
  const species = Species.get(p.species);
154
- inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
145
+ inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
155
146
  const hp = comptime std.mem.eql(u8, field.name, "hp");
156
147
  const spc =
157
148
  comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
@@ -192,7 +183,7 @@ pub const Pokemon = struct {
192
183
 
193
184
  var stats: Stats(u16) = .{};
194
185
  const dvs = gen1.DVs.random(rand);
195
- inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
186
+ inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
196
187
  const hp = std.mem.eql(u8, field.name, "hp");
197
188
  const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
198
189
  @field(stats, field.name) = Stats(u16).calc(
@@ -1,50 +1,36 @@
1
- const std = @import("std");
2
-
3
- const pkmn = @import("../pkmn.zig");
4
-
5
- const common = @import("../common/data.zig");
6
1
  const DEBUG = @import("../common/debug.zig").print;
7
- const protocol = @import("../common/protocol.zig");
8
- const rng = @import("../common/rng.zig");
9
- const util = @import("../common/util.zig");
10
2
 
3
+ const common = @import("../common/data.zig");
11
4
  const data = @import("data.zig");
12
5
  const generated = @import("generated.zig");
13
-
14
- const assert = std.debug.assert;
15
-
16
- const expectEqual = std.testing.expectEqual;
17
- const expectEqualSlices = std.testing.expectEqualSlices;
18
- const expectEqualStrings = std.testing.expectEqualStrings;
19
-
20
- const showdown = pkmn.options.showdown;
21
-
22
- const Choice = common.Choice;
23
- const ID = common.ID;
24
- const Player = common.Player;
25
- const Result = common.Result;
6
+ const pkmn = @import("../pkmn.zig");
7
+ const protocol = @import("../common/protocol.zig");
8
+ const rng = @import("../common/rng.zig");
9
+ const std = @import("std");
26
10
 
27
11
  const Activate = protocol.Activate;
12
+ const ActivePokemon = data.ActivePokemon;
13
+ const assert = std.debug.assert;
28
14
  const Boost = protocol.Boost;
15
+ const Boosts = data.Boosts;
16
+ const Choice = common.Choice;
29
17
  const Damage = protocol.Damage;
18
+ const Effectiveness = data.Effectiveness;
30
19
  const End = protocol.End;
20
+ const expectEqual = std.testing.expectEqual;
21
+ const expectEqualSlices = std.testing.expectEqualSlices;
22
+ const expectEqualStrings = std.testing.expectEqualStrings;
31
23
  const Fail = protocol.Fail;
32
- const Heal = protocol.Heal;
33
- const Start = protocol.Start;
34
-
35
24
  const Gen12 = rng.Gen12;
36
-
37
- const PointerType = util.PointerType;
38
-
39
- const ActivePokemon = data.ActivePokemon;
40
- const Boosts = data.Boosts;
41
- const Effectiveness = data.Effectiveness;
25
+ const Heal = protocol.Heal;
26
+ const ID = common.ID;
42
27
  const Item = data.Item;
43
28
  const Move = data.Move;
44
- const MoveSlot = data.MoveSlot;
45
- const Pokemon = data.Pokemon;
29
+ const Player = common.Player;
30
+ const Result = common.Result;
31
+ const showdown = pkmn.options.showdown;
46
32
  const Side = data.Side;
47
- const Species = data.Species;
33
+ const Start = protocol.Start;
48
34
  const Stats = data.Stats;
49
35
  const Status = data.Status;
50
36
  const TriAttack = data.TriAttack;
@@ -106,6 +92,7 @@ pub const State = struct {
106
92
  type: Type, // BATTLE_VARS_MOVE_TYPE
107
93
  accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
108
94
  move: Move, // wCurPlayerMove
95
+ hit: u3 = 0,
109
96
  from: Move = .None,
110
97
  mslot: u4, // wCurMoveNum
111
98
  first: bool, // wEnemyGoesFirst
@@ -644,7 +631,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
644
631
  }
645
632
  assert(stage <= 4);
646
633
 
647
- state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
634
+ state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
648
635
  }
649
636
 
650
637
  pub fn calcDamage(
@@ -712,7 +699,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
712
699
 
713
700
  pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
714
701
  if (state.damage <= 1) return;
715
- const random = try Rolls.damage(battle, player, options);
702
+ const random = try Rolls.damage(battle, player, state.hit, options);
716
703
  options.calc.base(player.foe(), state.damage);
717
704
  state.damage = @intCast(@as(u32, state.damage) *% random / 255);
718
705
  options.calc.final(player.foe(), state.damage);
@@ -1205,7 +1192,7 @@ fn updateSideCondition(
1205
1192
  fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
1206
1193
  battle.turn += 1;
1207
1194
 
1208
- if (showdown and battle.turn >= 1000) {
1195
+ if (pkmn.options.mod and battle.turn >= 1000) {
1209
1196
  try options.log.tie(.{});
1210
1197
  return Result.Tie;
1211
1198
  } else if (battle.turn >= 65535) {
@@ -1711,7 +1698,7 @@ pub const Effects = struct {
1711
1698
  if (!state.proc) return;
1712
1699
  if (foe.conditions.Safeguard) return;
1713
1700
  // Freeze Clause Mod
1714
- if (showdown) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
1701
+ if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
1715
1702
 
1716
1703
  foe_stored.status = Status.init(.FRZ);
1717
1704
  try options.log.status(.{ foe_ident, foe_stored.status, .None });
@@ -2389,7 +2376,7 @@ pub const Effects = struct {
2389
2376
  }
2390
2377
 
2391
2378
  // Sleep Clause Mod
2392
- if (showdown) {
2379
+ if (pkmn.options.mod) {
2393
2380
  for (foe.pokemon) |p| {
2394
2381
  if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
2395
2382
  }
@@ -2849,7 +2836,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
2849
2836
 
2850
2837
  pub const Rolls = struct {
2851
2838
  fn speedTie(battle: anytype, options: anytype) !bool {
2852
- const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
2839
+ const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
2853
2840
  player == .P1
2854
2841
  else if (showdown)
2855
2842
  battle.rng.range(u8, 0, 2) == 0
@@ -2861,7 +2848,7 @@ pub const Rolls = struct {
2861
2848
  }
2862
2849
 
2863
2850
  fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
2864
- const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
2851
+ const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
2865
2852
  val == .true
2866
2853
  else if (showdown)
2867
2854
  battle.rng.chance(u8, 60, 256)
@@ -2873,7 +2860,7 @@ pub const Rolls = struct {
2873
2860
  }
2874
2861
 
2875
2862
  fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
2876
- const ok = if (options.calc.overridden(player, .hit)) |val|
2863
+ const ok = if (options.calc.overridden(player, 0, .hit)) |val|
2877
2864
  val == .true
2878
2865
  else if (showdown)
2879
2866
  battle.rng.chance(u8, accuracy, 256)
@@ -2884,20 +2871,22 @@ pub const Rolls = struct {
2884
2871
  return ok;
2885
2872
  }
2886
2873
 
2887
- fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2888
- const crit = if (options.calc.overridden(player, .critical_hit)) |val|
2874
+ fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
2875
+ _ = .{ player, options };
2876
+ const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
2889
2877
  val == .true
2890
2878
  else if (showdown)
2891
2879
  battle.rng.chance(u8, rate, 256)
2892
2880
  else
2893
2881
  battle.rng.next() < rate;
2894
2882
 
2895
- try options.chance.criticalHit(player, crit, rate);
2883
+ try options.chance.criticalHit(player, i, crit, rate);
2896
2884
  return crit;
2897
2885
  }
2898
2886
 
2899
- fn damage(battle: anytype, player: Player, options: anytype) !u8 {
2900
- const roll = if (options.calc.overridden(player, .damage)) |val|
2887
+ fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
2888
+ _ = .{ player, options };
2889
+ const roll = if (options.calc.overridden(player, i, .damages)) |val|
2901
2890
  val
2902
2891
  else roll: {
2903
2892
  if (showdown) break :roll battle.rng.range(u8, 217, 256);
@@ -2908,12 +2897,12 @@ pub const Rolls = struct {
2908
2897
  };
2909
2898
 
2910
2899
  assert(roll >= 217 and roll <= 255);
2911
- try options.chance.damage(player, roll);
2900
+ try options.chance.damage(player, i, roll);
2912
2901
  return roll;
2913
2902
  }
2914
2903
 
2915
2904
  fn confused(battle: anytype, player: Player, options: anytype) !bool {
2916
- const cfz = if (options.calc.overridden(player, .confused)) |val|
2905
+ const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
2917
2906
  val == .true
2918
2907
  else if (showdown)
2919
2908
  !battle.rng.chance(u8, 128, 256)
@@ -2925,7 +2914,7 @@ pub const Rolls = struct {
2925
2914
  }
2926
2915
 
2927
2916
  fn attract(battle: anytype, player: Player, options: anytype) !bool {
2928
- const cant = if (options.calc.overridden(player, .attract)) |val|
2917
+ const cant = if (options.calc.overridden(player, 0, .attract)) |val|
2929
2918
  val == .true
2930
2919
  else if (showdown)
2931
2920
  battle.rng.chance(u8, 1, 2)
@@ -2937,7 +2926,7 @@ pub const Rolls = struct {
2937
2926
  }
2938
2927
 
2939
2928
  fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2940
- const par = if (options.calc.overridden(player, .paralyzed)) |val|
2929
+ const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
2941
2930
  val == .true
2942
2931
  else if (showdown)
2943
2932
  battle.rng.chance(u8, 63, 256)
@@ -2949,7 +2938,7 @@ pub const Rolls = struct {
2949
2938
  }
2950
2939
 
2951
2940
  fn defrost(battle: anytype, player: Player, options: anytype) !bool {
2952
- const thaw = if (options.calc.overridden(player, .defrost)) |val|
2941
+ const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
2953
2942
  val == .true
2954
2943
  else if (showdown)
2955
2944
  battle.rng.chance(u8, 25, 256)
@@ -2962,7 +2951,7 @@ pub const Rolls = struct {
2962
2951
 
2963
2952
  fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2964
2953
  // GLITCH: 100% secondary chance will still fail 1/256 of the time
2965
- const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2954
+ const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
2966
2955
  val == .true
2967
2956
  else if (showdown)
2968
2957
  battle.rng.chance(u8, rate, 256)
@@ -2974,7 +2963,7 @@ pub const Rolls = struct {
2974
2963
  }
2975
2964
 
2976
2965
  fn item(battle: anytype, player: Player, options: anytype) !bool {
2977
- const proc = if (options.calc.overridden(player, .item)) |val|
2966
+ const proc = if (options.calc.overridden(player, 0, .item)) |val|
2978
2967
  val == .true
2979
2968
  else if (showdown)
2980
2969
  battle.rng.chance(u8, 30, 256)
@@ -2986,7 +2975,7 @@ pub const Rolls = struct {
2986
2975
  }
2987
2976
 
2988
2977
  fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
2989
- const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
2978
+ const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
2990
2979
  @enumFromInt(@intFromEnum(val) - 1)
2991
2980
  else if (showdown)
2992
2981
  @enumFromInt(battle.rng.range(u2, 0, 2))
@@ -3002,7 +2991,7 @@ pub const Rolls = struct {
3002
2991
  }
3003
2992
 
3004
2993
  fn present(battle: anytype, player: Player, options: anytype) !u8 {
3005
- const power = if (options.calc.overridden(player, .present)) |val|
2994
+ const power = if (options.calc.overridden(player, 0, .present)) |val|
3006
2995
  (val - 1) * 40
3007
2996
  else if (showdown) power: {
3008
2997
  const r = battle.rng.range(u8, 0, 10);
@@ -3024,7 +3013,7 @@ pub const Rolls = struct {
3024
3013
  }
3025
3014
 
3026
3015
  fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
3027
- const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
3016
+ const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
3028
3017
  val + 3
3029
3018
  else if (showdown) num: {
3030
3019
  const r = battle.rng.range(u8, 0, 100);
@@ -3052,7 +3041,7 @@ pub const Rolls = struct {
3052
3041
  }
3053
3042
 
3054
3043
  fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
3055
- const hits = if (options.calc.overridden(player, .triple_kick)) |val|
3044
+ const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
3056
3045
  val
3057
3046
  else if (showdown)
3058
3047
  battle.rng.range(u2, 1, 4)
@@ -3070,7 +3059,7 @@ pub const Rolls = struct {
3070
3059
 
3071
3060
  fn spite(battle: anytype, player: Player, options: anytype) !u3 {
3072
3061
  assert(!showdown);
3073
- const pp: u3 = if (options.calc.overridden(player, .spite)) |val|
3062
+ const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
3074
3063
  val + 1
3075
3064
  else
3076
3065
  @intCast((battle.rng.next() & 3) + 2);
@@ -3087,7 +3076,7 @@ pub const Rolls = struct {
3087
3076
  var i: u8 = 0;
3088
3077
  const neutral = @intFromEnum(Effectiveness.Neutral);
3089
3078
  assert(mtype != .@"???");
3090
- const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
3079
+ const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
3091
3080
  @enumFromInt(@intFromEnum(val) - 1)
3092
3081
  else if (showdown) ty: {
3093
3082
  for (Type.SHOWDOWN) |t| {
@@ -3121,7 +3110,7 @@ pub const Rolls = struct {
3121
3110
  assert(count <= 8);
3122
3111
  const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
3123
3112
 
3124
- const ok = if (options.calc.overridden(player, .protect)) |val|
3113
+ const ok = if (options.calc.overridden(player, 0, .protect)) |val|
3125
3114
  val == .true
3126
3115
  else if (showdown)
3127
3116
  battle.rng.chance(u8, num, 255)
@@ -3143,7 +3132,7 @@ pub const Rolls = struct {
3143
3132
  const moves = active.moves;
3144
3133
 
3145
3134
  // TODO: consider throwing error instead of rerolling?
3146
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3135
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3147
3136
  if (convertible(active, moves[val - 1].id)) val else null
3148
3137
  else
3149
3138
  null;
@@ -3180,7 +3169,7 @@ pub const Rolls = struct {
3180
3169
  const moves = active.moves;
3181
3170
 
3182
3171
  // TODO: consider throwing error instead of rerolling?
3183
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3172
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3184
3173
  if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
3185
3174
  else
3186
3175
  null;
@@ -3224,7 +3213,7 @@ pub const Rolls = struct {
3224
3213
  assert(n > 0 and n <= 6);
3225
3214
 
3226
3215
  // TODO: consider throwing error instead of rerolling?
3227
- const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
3216
+ const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
3228
3217
  for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
3229
3218
  break :val null;
3230
3219
  } else null;
@@ -3250,7 +3239,7 @@ pub const Rolls = struct {
3250
3239
  const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
3251
3240
 
3252
3241
  fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
3253
- const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
3242
+ const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
3254
3243
  @intCast(val)
3255
3244
  else if (showdown)
3256
3245
  DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
@@ -3265,7 +3254,7 @@ pub const Rolls = struct {
3265
3254
  }
3266
3255
 
3267
3256
  fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
3268
- const power = if (options.calc.overridden(player, .psywave)) |val|
3257
+ const power = if (options.calc.overridden(player, 0, .psywave)) |val|
3269
3258
  val
3270
3259
  else if (showdown)
3271
3260
  battle.rng.range(u8, 1, max)
@@ -3291,7 +3280,7 @@ pub const Rolls = struct {
3291
3280
  }
3292
3281
 
3293
3282
  // TODO: consider throwing error instead of rerolling?
3294
- const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
3283
+ const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
3295
3284
  for (moves) |m| if (m.id == val) break :val null;
3296
3285
  break :val val;
3297
3286
  } else null;
@@ -3323,7 +3312,7 @@ pub const Rolls = struct {
3323
3312
  }
3324
3313
 
3325
3314
  fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
3326
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3315
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3327
3316
  @intCast(val)
3328
3317
  else if (showdown)
3329
3318
  battle.rng.range(u3, 1, 7)
@@ -3340,7 +3329,7 @@ pub const Rolls = struct {
3340
3329
  }
3341
3330
 
3342
3331
  fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
3343
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3332
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3344
3333
  @intCast(val)
3345
3334
  else if (showdown)
3346
3335
  // Pokémon Showdown incorrectly uses the same duration for self-confusion
@@ -3354,11 +3343,11 @@ pub const Rolls = struct {
3354
3343
  }
3355
3344
 
3356
3345
  fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
3357
- const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
3346
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3358
3347
  @intCast(val)
3359
3348
  else if (showdown)
3360
3349
  // Pokémon Showdown incorrectly inherits Generation III's Disable duration
3361
- battle.rng.range(u4, 2, 6)
3350
+ battle.rng.range(u3, 2, 6)
3362
3351
  else duration: {
3363
3352
  while (true) {
3364
3353
  const r = battle.rng.next() & 7;
@@ -3372,7 +3361,7 @@ pub const Rolls = struct {
3372
3361
  }
3373
3362
 
3374
3363
  fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
3375
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3364
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3376
3365
  @intCast(val)
3377
3366
  else if (showdown)
3378
3367
  battle.rng.range(u3, 2, 4)
@@ -3385,7 +3374,7 @@ pub const Rolls = struct {
3385
3374
  }
3386
3375
 
3387
3376
  fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
3388
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3377
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3389
3378
  @intCast(val)
3390
3379
  else if (showdown)
3391
3380
  // TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
@@ -3400,7 +3389,7 @@ pub const Rolls = struct {
3400
3389
 
3401
3390
  // TODO: consider sharing implementation with bindingDuration
3402
3391
  fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
3403
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3392
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3404
3393
  @intCast(val)
3405
3394
  else if (showdown)
3406
3395
  battle.rng.range(u3, 2, 6)
package/src/lib/node.zig CHANGED
@@ -1,8 +1,6 @@
1
- const std = @import("std");
2
-
3
- const pkmn = @import("pkmn.zig");
4
-
5
1
  const js = @import("common/js.zig");
2
+ const pkmn = @import("pkmn.zig");
3
+ const std = @import("std");
6
4
 
7
5
  const assert = std.debug.assert;
8
6
 
package/src/lib/pkmn.zig CHANGED
@@ -22,6 +22,11 @@ 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
26
+ pub const Array = @import("common/array.zig").Array;
27
+ // TODO
28
+ pub const Optional = @import("common/optional.zig").Optional;
29
+
25
30
  /// Pokémon Showdown's RNG (backed by a Generation V & VI RNG).
26
31
  pub const PSRNG = @import("common/rng.zig").PSRNG;
27
32
 
@@ -81,8 +86,6 @@ pub const gen1 = struct {
81
86
  pub const Calc = @import("gen1/calc.zig").Calc;
82
87
  /// Namespace for types associated with supported Generation I Pokémon damage calc features.
83
88
  pub const calc = if (options.internal) @import("gen1/calc.zig") else struct {
84
- /// TODO
85
- pub const Overrides = @import("gen1/calc.zig").Overrides;
86
89
  /// Information relevant to damage calculation that occured during a Generation I
87
90
  /// battle `update`.
88
91
  pub const Summaries = @import("gen1/calc.zig").Summaries;
@@ -93,6 +96,10 @@ pub const gen1 = struct {
93
96
  /// though damage calculator support should additionally be turned off
94
97
  /// entirely via `options.calc`.
95
98
  pub const NULL = @import("gen1/calc.zig").NULL;
99
+ /// TODO
100
+ pub const MAX_FRONTIER = @import("gen1/calc.zig").MAX_FRONTIER;
101
+ /// TODO
102
+ pub const Rolls = @import("gen1/calc.zig").Rolls;
96
103
  };
97
104
  /// Provides helpers for initializing Generation I Pokémon battles.
98
105
  pub const helpers = @import("gen1/helpers.zig");
@@ -111,7 +118,6 @@ pub const gen2 = struct {
111
118
  };
112
119
  pub const Calc = @import("gen2/calc.zig").Calc;
113
120
  pub const calc = if (options.internal) @import("gen2/calc.zig") else struct {
114
- pub const Overrides = @import("gen2/calc.zig").Overrides;
115
121
  pub const Summaries = @import("gen2/calc.zig").Summaries;
116
122
  pub const Summary = @import("gen2/calc.zig").Summary;
117
123
  pub const NULL = @import("gen2/calc.zig").NULL;
@@ -121,8 +127,8 @@ pub const gen2 = struct {
121
127
 
122
128
  // Internal APIs used by other pkmn libraries, not actually part of the public API.
123
129
  // NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
124
- pub const js = if (options.internal) @import("common/js.zig") else struct {};
130
+ pub const js = if (options.internal) @import("common/js.zig") else {};
125
131
  pub const bindings = if (options.internal) struct {
126
132
  pub const node = @import("node.zig");
127
133
  pub const wasm = @import("wasm.zig");
128
- } else struct {};
134
+ } else {};
package/src/lib/wasm.zig CHANGED
@@ -1,6 +1,5 @@
1
- const std = @import("std");
2
-
3
1
  const pkmn = @import("pkmn.zig");
2
+ const std = @import("std");
4
3
 
5
4
  export const SHOWDOWN = pkmn.options.showdown;
6
5
  export const LOG = pkmn.options.log;
@@ -0,0 +1,96 @@
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
+
6
+ declare global {
7
+ namespace JSX {
8
+ interface Element extends HTMLElement, DocumentFragment {
9
+ addEventListener: HTMLElement['addEventListener'];
10
+ removeEventListener: HTMLElement['removeEventListener'];
11
+ className: HTMLElement['className'];
12
+ }
13
+ }
14
+ }
15
+
16
+ interface JSXElementClassDocumentFragment extends DocumentFragment, JSX.ElementClass { }
17
+ interface Fragment {
18
+ prototype: JSXElementClassDocumentFragment;
19
+ new(): JSXElementClassDocumentFragment;
20
+ }
21
+
22
+ // https://github.com/preactjs/preact/blob/1bbd687c/src/constants.js#L3
23
+ const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;
24
+
25
+ const isFragment = (type: DocumentFragmentConstructor | ElementFunction):
26
+ type is DocumentFragmentConstructor => type === DocumentFragment;
27
+
28
+ const setCSSProps = (element: HTMLElement, style: CSSStyleDeclaration) => {
29
+ for (const [name, value] of Object.entries(style)) {
30
+ if (name.startsWith('-')) {
31
+ element.style.setProperty(name, value);
32
+ } else if (typeof value === 'number' && !IS_NON_DIMENSIONAL.test(name)) {
33
+ element.style[name as any] = `${value as string}px`;
34
+ } else {
35
+ element.style[name as any] = value;
36
+ }
37
+ }
38
+ };
39
+
40
+ const setAttribute = (element: HTMLElement, name: string, value: string) => {
41
+ if (value !== undefined && value !== null) element.setAttribute(name, value);
42
+ };
43
+
44
+ const addChildren = (parent: Element | DocumentFragment, children: Node[]) => {
45
+ for (const child of children) {
46
+ if (child instanceof Node) {
47
+ parent.appendChild(child);
48
+ } else if (Array.isArray(child)) {
49
+ addChildren(parent, child);
50
+ } else if (typeof child !== 'boolean' && typeof child !== 'undefined' && child !== null) {
51
+ parent.appendChild(document.createTextNode(child));
52
+ }
53
+ }
54
+ };
55
+
56
+ // https://github.com/facebook/react/blob/3f899089/packages/react-dom/src/shared/DOMProperty.js#L288-L322
57
+ const FALSIFIABLE_ATTRIBUTES = ['contentEditable', 'draggable', 'spellCheck', 'value'];
58
+
59
+ export const h = (
60
+ type: DocumentFragmentConstructor | ElementFunction | string,
61
+ attributes?: Attributes,
62
+ ...children: Node[]
63
+ ) => {
64
+ if (typeof type !== 'string') {
65
+ const element = isFragment(type) ? document.createDocumentFragment() : type(attributes);
66
+ addChildren(element, children);
67
+ return element;
68
+ }
69
+
70
+ const element = document.createElement(type);
71
+ addChildren(element, children);
72
+ if (!attributes) return element;
73
+
74
+ for (let [name, value] of Object.entries(attributes)) {
75
+ if (name === 'htmlFor') name = 'for';
76
+
77
+ if (name === 'class' || name === 'className') {
78
+ const existingClassname = element.getAttribute('class') ?? '';
79
+ setAttribute(element, 'class', (existingClassname + ' ' + String(value)).trim());
80
+ } else if (name === 'style') {
81
+ setCSSProps(element, value);
82
+ } else if (name.startsWith('on')) {
83
+ 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;
87
+ } else if (name !== 'key' && (FALSIFIABLE_ATTRIBUTES.includes(name) || value !== false)) {
88
+ setAttribute(element, name, value === true ? '' : value);
89
+ }
90
+ }
91
+
92
+ return element;
93
+ };
94
+
95
+ export const Fragment =
96
+ (typeof DocumentFragment === 'function' ? DocumentFragment : () => {}) as Fragment;