@pkmn/engine 0.1.0-dev.fb5bf955 → 0.1.0-dev.fb6653d6
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.
- package/LICENSE +1 -3
- package/README.md +115 -76
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +44 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +97 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +27 -5
- package/build/pkg/addon.js +42 -73
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +40 -5
- package/build/pkg/data/protocol.json +84 -13
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +16 -19
- package/build/pkg/gen1.js +54 -42
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +55 -103
- package/build/pkg/index.js +6 -63
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +24 -3
- package/build/pkg/protocol.js +127 -104
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +36 -69
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +142 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +373 -152
- package/package.json +68 -120
- package/src/bin/install-pkmn-engine +41 -26
- package/src/bin/pkmn-debug +29 -6
- package/src/lib/bindings/node.zig +121 -0
- package/src/lib/bindings/wasm.zig +58 -0
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +19 -31
- package/src/lib/common/debug.zig +58 -17
- package/src/lib/common/js.zig +120 -0
- package/src/lib/common/optional.zig +71 -0
- package/src/lib/common/options.zig +18 -3
- package/src/lib/common/protocol.zig +947 -407
- package/src/lib/common/rational.zig +449 -0
- package/src/lib/common/rng.zig +31 -24
- package/src/lib/common/util.zig +61 -0
- package/src/lib/gen1/calc.zig +953 -0
- package/src/lib/gen1/chance.zig +1185 -0
- package/src/lib/gen1/data/moves.zig +203 -193
- package/src/lib/gen1/data/species.zig +6 -8
- package/src/lib/gen1/data/types.zig +11 -12
- package/src/lib/gen1/data.zig +100 -89
- package/src/lib/gen1/helpers.zig +139 -50
- package/src/lib/gen1/mechanics.zig +1344 -907
- package/src/lib/gen2/calc.zig +153 -0
- package/src/lib/gen2/chance.zig +902 -0
- package/src/lib/gen2/data/items.zig +15 -23
- package/src/lib/gen2/data/moves.zig +665 -573
- package/src/lib/gen2/data/species.zig +3 -5
- package/src/lib/gen2/data/types.zig +138 -36
- package/src/lib/gen2/data.zig +134 -99
- package/src/lib/gen2/generated.zig +801 -0
- package/src/lib/gen2/helpers.zig +59 -44
- package/src/lib/gen2/mechanics.zig +3582 -30
- package/src/lib/node.zig +18 -0
- package/src/lib/pkmn.zig +93 -12
- package/src/lib/wasm.zig +17 -0
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -382
- package/build/test/display.js.map +0 -1
- package/src/lib/binding/node.zig +0 -229
- package/src/lib/binding/wasm.zig +0 -10
- package/src/lib/gen1/README.md +0 -784
- package/src/lib/gen2/README.md +0 -309
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
@@ -48,7 +39,7 @@ pub const Battle = struct {
|
|
|
48
39
|
comptime rolls: anytype,
|
|
49
40
|
p1: []const Pokemon,
|
|
50
41
|
p2: []const Pokemon,
|
|
51
|
-
) data.Battle(rng.FixedRNG(
|
|
42
|
+
) data.Battle(rng.FixedRNG(2, rolls.len)) {
|
|
52
43
|
return .{
|
|
53
44
|
.rng = .{ .rolls = rolls },
|
|
54
45
|
.sides = .{ Side.init(p1), Side.init(p2) },
|
|
@@ -65,19 +56,47 @@ pub const Battle = struct {
|
|
|
65
56
|
};
|
|
66
57
|
|
|
67
58
|
pub fn prng(rand: *PSRNG) data.PRNG {
|
|
68
|
-
// FIXME: what is max range in gen2?
|
|
69
59
|
return .{
|
|
70
60
|
.src = .{
|
|
71
61
|
.seed = if (showdown)
|
|
72
62
|
rand.newSeed()
|
|
73
|
-
else
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
63
|
+
else seed: {
|
|
64
|
+
// GLITCH: The initial RNG seed in Gen II link battles is heavily constrained
|
|
65
|
+
// https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
|
|
66
|
+
var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
67
|
+
|
|
68
|
+
// Bytes in seed can only range from 0-252, not 0-255
|
|
69
|
+
const max: u8 = 253;
|
|
70
|
+
// Instructions take multiples of 4 cycles to execute
|
|
71
|
+
var timer: u16 = 4 * rand.range(u16, 0, 16384);
|
|
72
|
+
var add: u8 = rand.range(u8, 0, 256);
|
|
73
|
+
var sub: u8 = rand.range(u8, 0, 256);
|
|
74
|
+
var carry: u8 = 0;
|
|
75
|
+
var div: u8 = 0;
|
|
76
|
+
|
|
77
|
+
var i: u8 = 0;
|
|
78
|
+
while (i < seed.len) {
|
|
79
|
+
div = @intCast(timer >> 8);
|
|
80
|
+
// This is effectively @addWithOverflow but with three operands
|
|
81
|
+
const result = @as(u16, add) + @as(u16, div) + @as(u16, carry);
|
|
82
|
+
add = @intCast(result & 255);
|
|
83
|
+
carry = @intFromBool(result > 255);
|
|
84
|
+
timer +%= 44;
|
|
85
|
+
div = @intCast(timer >> 8);
|
|
86
|
+
sub -%= div +% carry;
|
|
87
|
+
|
|
88
|
+
if (sub < max) {
|
|
89
|
+
seed[i] = sub;
|
|
90
|
+
i += 1;
|
|
91
|
+
timer +%= 152;
|
|
92
|
+
carry = 1;
|
|
93
|
+
} else {
|
|
94
|
+
timer +%= 132;
|
|
95
|
+
carry = 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
break :seed seed;
|
|
99
|
+
},
|
|
81
100
|
},
|
|
82
101
|
};
|
|
83
102
|
}
|
|
@@ -89,7 +108,7 @@ pub const Side = struct {
|
|
|
89
108
|
|
|
90
109
|
for (0..ps.len) |i| {
|
|
91
110
|
side.pokemon[i] = Pokemon.init(ps[i]);
|
|
92
|
-
|
|
111
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
93
112
|
}
|
|
94
113
|
return side;
|
|
95
114
|
}
|
|
@@ -100,7 +119,7 @@ pub const Side = struct {
|
|
|
100
119
|
|
|
101
120
|
for (0..n) |i| {
|
|
102
121
|
side.pokemon[i] = Pokemon.random(rand, opt);
|
|
103
|
-
side.pokemon[i].position = i + 1;
|
|
122
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
104
123
|
}
|
|
105
124
|
|
|
106
125
|
return side;
|
|
@@ -123,7 +142,7 @@ pub const Pokemon = struct {
|
|
|
123
142
|
var pokemon = data.Pokemon{};
|
|
124
143
|
pokemon.species = p.species;
|
|
125
144
|
const species = Species.get(p.species);
|
|
126
|
-
inline for (@typeInfo(@TypeOf(pokemon.stats))
|
|
145
|
+
inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
|
|
127
146
|
const hp = comptime std.mem.eql(u8, field.name, "hp");
|
|
128
147
|
const spc =
|
|
129
148
|
comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
@@ -137,12 +156,9 @@ pub const Pokemon = struct {
|
|
|
137
156
|
}
|
|
138
157
|
assert(p.moves.len > 0 and p.moves.len <= 4);
|
|
139
158
|
for (p.moves, 0..) |m, j| {
|
|
140
|
-
pokemon.moves[j] =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
144
|
-
.pp = @intCast(u8, @min(Move.pp(m) / 5 * 8, 61)),
|
|
145
|
-
};
|
|
159
|
+
pokemon.moves[j].id = m;
|
|
160
|
+
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
161
|
+
pokemon.moves[j].pp = @intCast(@min(Move.pp(m) / 5 * 8, 61));
|
|
146
162
|
}
|
|
147
163
|
pokemon.item = p.item;
|
|
148
164
|
if (p.hp) |hp| {
|
|
@@ -153,21 +169,21 @@ pub const Pokemon = struct {
|
|
|
153
169
|
pokemon.status = p.status;
|
|
154
170
|
pokemon.types = species.types;
|
|
155
171
|
pokemon.level = p.level;
|
|
156
|
-
pokemon.dvs = DVs.from(p.dvs);
|
|
172
|
+
pokemon.dvs = DVs.from(p.species, p.dvs);
|
|
157
173
|
return pokemon;
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
|
|
161
|
-
const s = @
|
|
177
|
+
const s: Species = @enumFromInt(rand.range(u8, 1, Species.size + 1));
|
|
162
178
|
const species = Species.get(s);
|
|
163
179
|
const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
|
|
164
180
|
// Pokémon Showdown does not support most items without in-battle held item effects
|
|
165
|
-
const max_item = @
|
|
181
|
+
const max_item = @intFromEnum(if (showdown) Item.FlowerMail else Item.TM50);
|
|
166
182
|
const item = if (rand.chance(u8, 1, 10)) 0 else rand.range(u8, 1, max_item + 1);
|
|
167
183
|
|
|
168
184
|
var stats: Stats(u16) = .{};
|
|
169
185
|
const dvs = gen1.DVs.random(rand);
|
|
170
|
-
inline for (@typeInfo(@TypeOf(stats))
|
|
186
|
+
inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
|
|
171
187
|
const hp = std.mem.eql(u8, field.name, "hp");
|
|
172
188
|
const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
173
189
|
@field(stats, field.name) = Stats(u16).calc(
|
|
@@ -184,24 +200,23 @@ pub const Pokemon = struct {
|
|
|
184
200
|
for (0..n) |i| {
|
|
185
201
|
var m: Move = .None;
|
|
186
202
|
sample: while (true) {
|
|
187
|
-
m = @
|
|
203
|
+
m = @enumFromInt(rand.range(u8, 1, 164 + 1));
|
|
188
204
|
for (0..i) |j| if (ms[j].id == m) continue :sample;
|
|
189
205
|
break;
|
|
190
206
|
}
|
|
191
|
-
const pp_ups =
|
|
207
|
+
const pp_ups: u8 =
|
|
192
208
|
if (!opt.cleric and rand.chance(u8, 1, 10)) rand.range(u2, 0, 2 + 1) else 3;
|
|
193
209
|
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
194
|
-
const max_pp = @intCast(
|
|
210
|
+
const max_pp: u8 = @intCast(Move.pp(m) + pp_ups * @min(Move.pp(m) / 5, 7));
|
|
195
211
|
ms[i] = .{
|
|
196
212
|
.id = m,
|
|
197
|
-
.pp_ups = pp_ups,
|
|
198
213
|
.pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
|
|
199
214
|
};
|
|
200
215
|
}
|
|
201
216
|
return .{
|
|
202
217
|
.species = s,
|
|
203
218
|
.types = species.types,
|
|
204
|
-
.dvs = DVs.from(dvs),
|
|
219
|
+
.dvs = DVs.from(s, dvs),
|
|
205
220
|
.level = lvl,
|
|
206
221
|
.stats = stats,
|
|
207
222
|
.hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),
|