@pkmn/engine 0.1.0-dev.028ab165
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 +20 -0
- package/README.md +343 -0
- package/build/pkg/addon.d.ts +7 -0
- package/build/pkg/addon.js +112 -0
- package/build/pkg/addon.js.map +1 -0
- package/build/pkg/data/ids.json +240 -0
- package/build/pkg/data/layout.json +108 -0
- package/build/pkg/data/protocol.json +230 -0
- package/build/pkg/data.d.ts +91 -0
- package/build/pkg/data.js +150 -0
- package/build/pkg/data.js.map +1 -0
- package/build/pkg/gen1.d.ts +110 -0
- package/build/pkg/gen1.js +684 -0
- package/build/pkg/gen1.js.map +1 -0
- package/build/pkg/index.d.ts +386 -0
- package/build/pkg/index.js +122 -0
- package/build/pkg/index.js.map +1 -0
- package/build/pkg/protocol.d.ts +73 -0
- package/build/pkg/protocol.js +473 -0
- package/build/pkg/protocol.js.map +1 -0
- package/build/test/display.js +400 -0
- package/build/test/display.js.map +1 -0
- package/build/tools/debug.js +135 -0
- package/build/tools/debug.js.map +1 -0
- package/build.zig +371 -0
- package/package.json +224 -0
- package/src/bin/install-pkmn-engine +396 -0
- package/src/bin/pkmn-debug +62 -0
- package/src/lib/binding/node.zig +134 -0
- package/src/lib/binding/wasm.zig +12 -0
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +117 -0
- package/src/lib/common/debug.zig +74 -0
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +67 -0
- package/src/lib/common/options.zig +49 -0
- package/src/lib/common/protocol.zig +1841 -0
- package/src/lib/common/rational.zig +353 -0
- package/src/lib/common/rng.zig +243 -0
- package/src/lib/common/util.zig +12 -0
- package/src/lib/gen1/README.md +894 -0
- package/src/lib/gen1/calc.zig +426 -0
- package/src/lib/gen1/chance.zig +1060 -0
- package/src/lib/gen1/data/moves.zig +1866 -0
- package/src/lib/gen1/data/species.zig +1105 -0
- package/src/lib/gen1/data/types.zig +131 -0
- package/src/lib/gen1/data.zig +634 -0
- package/src/lib/gen1/helpers.zig +779 -0
- package/src/lib/gen1/mechanics.zig +3128 -0
- package/src/lib/gen2/README.md +329 -0
- package/src/lib/gen2/calc.zig +143 -0
- package/src/lib/gen2/chance.zig +1314 -0
- package/src/lib/gen2/data/items.zig +230 -0
- package/src/lib/gen2/data/moves.zig +2878 -0
- package/src/lib/gen2/data/species.zig +1798 -0
- package/src/lib/gen2/data/types.zig +226 -0
- package/src/lib/gen2/data.zig +463 -0
- package/src/lib/gen2/generated.zig +808 -0
- package/src/lib/gen2/helpers.zig +219 -0
- package/src/lib/gen2/mechanics.zig +3551 -0
- package/src/lib/pkmn.zig +113 -0
- package/src/test/display/pkmn.html.tmpl +368 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const common = @import("../common/data.zig");
|
|
4
|
+
const options = @import("../common/options.zig");
|
|
5
|
+
const protocol = @import("../common/protocol.zig");
|
|
6
|
+
const rng = @import("../common/rng.zig");
|
|
7
|
+
|
|
8
|
+
const gen1 = @import("../gen1/data.zig");
|
|
9
|
+
|
|
10
|
+
const data = @import("data.zig");
|
|
11
|
+
|
|
12
|
+
const assert = std.debug.assert;
|
|
13
|
+
|
|
14
|
+
const expectEqual = std.testing.expectEqual;
|
|
15
|
+
|
|
16
|
+
const Choice = common.Choice;
|
|
17
|
+
|
|
18
|
+
const showdown = options.showdown;
|
|
19
|
+
|
|
20
|
+
const PSRNG = rng.PSRNG;
|
|
21
|
+
|
|
22
|
+
const DVs = data.DVs;
|
|
23
|
+
const Item = data.Item;
|
|
24
|
+
const Move = data.Move;
|
|
25
|
+
const MoveSlot = data.MoveSlot;
|
|
26
|
+
const Species = data.Species;
|
|
27
|
+
const Stats = data.Stats;
|
|
28
|
+
const Status = data.Status;
|
|
29
|
+
|
|
30
|
+
pub const Options = struct {
|
|
31
|
+
cleric: bool = showdown,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
pub const Battle = struct {
|
|
35
|
+
pub fn init(
|
|
36
|
+
seed: u64,
|
|
37
|
+
p1: []const Pokemon,
|
|
38
|
+
p2: []const Pokemon,
|
|
39
|
+
) data.Battle(data.PRNG) {
|
|
40
|
+
var rand = PSRNG.init(seed);
|
|
41
|
+
return .{
|
|
42
|
+
.rng = prng(&rand),
|
|
43
|
+
.sides = .{ Side.init(p1), Side.init(p2) },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
pub fn fixed(
|
|
48
|
+
comptime rolls: anytype,
|
|
49
|
+
p1: []const Pokemon,
|
|
50
|
+
p2: []const Pokemon,
|
|
51
|
+
) data.Battle(rng.FixedRNG(2, rolls.len)) {
|
|
52
|
+
return .{
|
|
53
|
+
.rng = .{ .rolls = rolls },
|
|
54
|
+
.sides = .{ Side.init(p1), Side.init(p2) },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pub fn random(rand: *PSRNG, opt: Options) data.Battle(data.PRNG) {
|
|
59
|
+
return .{
|
|
60
|
+
.rng = prng(rand),
|
|
61
|
+
.turn = 0,
|
|
62
|
+
.sides = .{ Side.random(rand, opt), Side.random(rand, opt) },
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
pub fn prng(rand: *PSRNG) data.PRNG {
|
|
68
|
+
// FIXME: what is max range in gen2?
|
|
69
|
+
return .{
|
|
70
|
+
.src = .{
|
|
71
|
+
.seed = if (showdown)
|
|
72
|
+
rand.newSeed()
|
|
73
|
+
else
|
|
74
|
+
.{
|
|
75
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
76
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
77
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pub const Side = struct {
|
|
84
|
+
pub fn init(ps: []const Pokemon) data.Side {
|
|
85
|
+
assert(ps.len > 0 and ps.len <= 6);
|
|
86
|
+
var side = data.Side{};
|
|
87
|
+
|
|
88
|
+
for (0..ps.len) |i| {
|
|
89
|
+
side.pokemon[i] = Pokemon.init(ps[i]);
|
|
90
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
91
|
+
}
|
|
92
|
+
return side;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
pub fn random(rand: *PSRNG, opt: Options) data.Side {
|
|
96
|
+
const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 5 + 1) else 6;
|
|
97
|
+
var side = data.Side{};
|
|
98
|
+
|
|
99
|
+
for (0..n) |i| {
|
|
100
|
+
side.pokemon[i] = Pokemon.random(rand, opt);
|
|
101
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return side;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
pub const EXP = 0xFFFF;
|
|
109
|
+
|
|
110
|
+
pub const Pokemon = struct {
|
|
111
|
+
species: Species,
|
|
112
|
+
moves: []const Move,
|
|
113
|
+
item: Item = .None,
|
|
114
|
+
hp: ?u16 = null,
|
|
115
|
+
status: u8 = 0,
|
|
116
|
+
level: u8 = 100,
|
|
117
|
+
dvs: gen1.DVs = .{},
|
|
118
|
+
stats: Stats(u16) = .{ .hp = EXP, .atk = EXP, .def = EXP, .spe = EXP, .spa = EXP, .spd = EXP },
|
|
119
|
+
|
|
120
|
+
pub fn init(p: Pokemon) data.Pokemon {
|
|
121
|
+
var pokemon = data.Pokemon{};
|
|
122
|
+
pokemon.species = p.species;
|
|
123
|
+
const species = Species.get(p.species);
|
|
124
|
+
inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
|
|
125
|
+
const hp = comptime std.mem.eql(u8, field.name, "hp");
|
|
126
|
+
const spc =
|
|
127
|
+
comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
128
|
+
@field(pokemon.stats, field.name) = Stats(u16).calc(
|
|
129
|
+
field.name,
|
|
130
|
+
@field(species.stats, field.name),
|
|
131
|
+
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field.name),
|
|
132
|
+
@field(p.stats, field.name),
|
|
133
|
+
p.level,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
assert(p.moves.len > 0 and p.moves.len <= 4);
|
|
137
|
+
for (p.moves, 0..) |m, j| {
|
|
138
|
+
pokemon.moves[j].id = m;
|
|
139
|
+
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
140
|
+
pokemon.moves[j].pp = @intCast(@min(Move.pp(m) / 5 * 8, 61));
|
|
141
|
+
}
|
|
142
|
+
pokemon.item = p.item;
|
|
143
|
+
if (p.hp) |hp| {
|
|
144
|
+
pokemon.hp = hp;
|
|
145
|
+
} else {
|
|
146
|
+
pokemon.hp = pokemon.stats.hp;
|
|
147
|
+
}
|
|
148
|
+
pokemon.status = p.status;
|
|
149
|
+
pokemon.types = species.types;
|
|
150
|
+
pokemon.level = p.level;
|
|
151
|
+
pokemon.dvs = DVs.from(p.species, p.dvs);
|
|
152
|
+
return pokemon;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
|
|
156
|
+
const s: Species = @enumFromInt(rand.range(u8, 1, Species.size + 1));
|
|
157
|
+
const species = Species.get(s);
|
|
158
|
+
const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
|
|
159
|
+
// Pokémon Showdown does not support most items without in-battle held item effects
|
|
160
|
+
const max_item = @intFromEnum(if (showdown) Item.FlowerMail else Item.TM50);
|
|
161
|
+
const item = if (rand.chance(u8, 1, 10)) 0 else rand.range(u8, 1, max_item + 1);
|
|
162
|
+
|
|
163
|
+
var stats: Stats(u16) = .{};
|
|
164
|
+
const dvs = gen1.DVs.random(rand);
|
|
165
|
+
inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
|
|
166
|
+
const hp = std.mem.eql(u8, field.name, "hp");
|
|
167
|
+
const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
168
|
+
@field(stats, field.name) = Stats(u16).calc(
|
|
169
|
+
field.name,
|
|
170
|
+
@field(species.stats, field.name),
|
|
171
|
+
if (hp) dvs.hp() else if (spc) dvs.spc else @field(dvs, field.name),
|
|
172
|
+
if (rand.chance(u8, 1, 20)) rand.range(u8, 0, 255 + 1) else 255,
|
|
173
|
+
lvl,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
var ms = [_]MoveSlot{.{}} ** 4;
|
|
178
|
+
const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
|
|
179
|
+
for (0..n) |i| {
|
|
180
|
+
var m: Move = .None;
|
|
181
|
+
sample: while (true) {
|
|
182
|
+
m = @enumFromInt(rand.range(u8, 1, 164 + 1));
|
|
183
|
+
for (0..i) |j| if (ms[j].id == m) continue :sample;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
const pp_ups: u8 =
|
|
187
|
+
if (!opt.cleric and rand.chance(u8, 1, 10)) rand.range(u2, 0, 2 + 1) else 3;
|
|
188
|
+
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
189
|
+
const max_pp: u8 = @intCast(Move.pp(m) + pp_ups * @min(Move.pp(m) / 5, 7));
|
|
190
|
+
ms[i] = .{
|
|
191
|
+
.id = m,
|
|
192
|
+
.pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return .{
|
|
196
|
+
.species = s,
|
|
197
|
+
.types = species.types,
|
|
198
|
+
.dvs = DVs.from(s, dvs),
|
|
199
|
+
.level = lvl,
|
|
200
|
+
.stats = stats,
|
|
201
|
+
.hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),
|
|
202
|
+
.status = if (!opt.cleric and rand.chance(u8, 1, 6 + 1))
|
|
203
|
+
0 | (@as(u8, 1) << rand.range(u3, 1, 6 + 1))
|
|
204
|
+
else
|
|
205
|
+
0,
|
|
206
|
+
.happiness = if (rand.chance(u8, 1, 10 + 1)) rand.range(u8, 0, 255) else 255,
|
|
207
|
+
.item = item,
|
|
208
|
+
.moves = ms,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
pub fn move(slot: u4) Choice {
|
|
214
|
+
return .{ .type = .Move, .data = slot };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
pub fn swtch(slot: u4) Choice {
|
|
218
|
+
return .{ .type = .Switch, .data = slot };
|
|
219
|
+
}
|