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