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