@pkmn/engine 0.1.0-dev.eaae4aa6 → 0.1.0-dev.f34af416
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 +0 -2
- package/README.md +15 -11
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +2 -3
- package/build/pkg/index.d.ts +1 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +4 -4
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +8 -3
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +2 -3
- package/build/tools/debug.js.map +1 -1
- package/build.zig +189 -72
- package/package.json +17 -16
- package/src/bin/install-pkmn-engine +17 -7
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +51 -44
- package/src/lib/common/rational.zig +114 -24
- package/src/lib/common/rng.zig +10 -10
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +508 -128
- package/src/lib/gen1/chance.zig +383 -276
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +29 -28
- package/src/lib/gen1/helpers.zig +96 -536
- package/src/lib/gen1/mechanics.zig +266 -160
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +91 -500
- package/src/lib/gen2/data.zig +15 -14
- package/src/lib/gen2/helpers.zig +41 -9
- package/src/lib/gen2/mechanics.zig +158 -97
- package/src/lib/pkmn.zig +10 -16
- package/src/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
package/src/lib/gen2/data.zig
CHANGED
|
@@ -45,6 +45,7 @@ const Player = data.Player;
|
|
|
45
45
|
const Result = data.Result;
|
|
46
46
|
|
|
47
47
|
const PointerType = util.PointerType;
|
|
48
|
+
const isPointerTo = util.isPointerTo;
|
|
48
49
|
|
|
49
50
|
const showdown = pkmn.options.showdown;
|
|
50
51
|
|
|
@@ -63,12 +64,12 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
63
64
|
_: [padding]u8 = .{0} ** padding,
|
|
64
65
|
|
|
65
66
|
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
66
|
-
assert(
|
|
67
|
+
assert(isPointerTo(self, Self));
|
|
67
68
|
return &self.sides[@intFromEnum(player)];
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
71
|
-
assert(
|
|
72
|
+
assert(isPointerTo(self, Self));
|
|
72
73
|
return &self.sides[@intFromEnum(player.foe())];
|
|
73
74
|
}
|
|
74
75
|
|
|
@@ -134,7 +135,7 @@ pub const Side = extern struct {
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
137
|
-
assert(
|
|
138
|
+
assert(isPointerTo(self, Side));
|
|
138
139
|
assert(slot > 0 and slot <= 6);
|
|
139
140
|
const id = self.pokemon[slot - 1].position;
|
|
140
141
|
assert(id > 0 and id <= 6);
|
|
@@ -142,7 +143,7 @@ pub const Side = extern struct {
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
145
|
-
assert(
|
|
146
|
+
assert(isPointerTo(self, Side));
|
|
146
147
|
return self.get(1);
|
|
147
148
|
}
|
|
148
149
|
|
|
@@ -176,7 +177,7 @@ pub const ActivePokemon = extern struct {
|
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
179
|
-
assert(
|
|
180
|
+
assert(isPointerTo(self, Pokemon));
|
|
180
181
|
assert(mslot > 0 and mslot <= 4);
|
|
181
182
|
assert(self.moves[mslot - 1].id != .None);
|
|
182
183
|
return &self.moves[mslot - 1];
|
|
@@ -201,7 +202,7 @@ pub const Pokemon = extern struct {
|
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
204
|
-
assert(
|
|
205
|
+
assert(isPointerTo(self, Pokemon));
|
|
205
206
|
assert(mslot > 0 and mslot <= 4);
|
|
206
207
|
assert(self.moves[mslot - 1].id != .None);
|
|
207
208
|
return &self.moves[mslot - 1];
|
|
@@ -248,12 +249,12 @@ test DVs {
|
|
|
248
249
|
var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
|
|
249
250
|
try expectEqual(Gender.Unknown, dvs.gender);
|
|
250
251
|
try expectEqual(Type.Ice, dvs.type);
|
|
251
|
-
try expectEqual(70, dvs.power());
|
|
252
|
+
try expectEqual(@as(u8, 70), dvs.power());
|
|
252
253
|
|
|
253
254
|
dvs = DVs.from(.Pikachu, .{ .spc = 14 });
|
|
254
255
|
try expectEqual(Gender.Male, dvs.gender);
|
|
255
256
|
try expectEqual(Type.Dark, dvs.type);
|
|
256
|
-
try expectEqual(69, dvs.power());
|
|
257
|
+
try expectEqual(@as(u8, 69), dvs.power());
|
|
257
258
|
|
|
258
259
|
dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
|
|
259
260
|
try expectEqual(Gender.Female, dvs.gender);
|
|
@@ -380,9 +381,9 @@ test Stats {
|
|
|
380
381
|
try expectEqual(0, stats.def);
|
|
381
382
|
|
|
382
383
|
var base = Species.get(.Gyarados).stats;
|
|
383
|
-
try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
384
|
+
try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
384
385
|
base = Species.get(.Pidgeot).stats;
|
|
385
|
-
try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
386
|
+
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
386
387
|
}
|
|
387
388
|
|
|
388
389
|
pub const Boosts = packed struct(u32) {
|
|
@@ -428,8 +429,8 @@ test Move {
|
|
|
428
429
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
429
430
|
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
430
431
|
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
431
|
-
try expectEqual(5, Move.pp(.DynamicPunch));
|
|
432
|
-
try expectEqual(-1, Move.get(.Counter).priority);
|
|
432
|
+
try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
|
|
433
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
433
434
|
try expect(!move.effect.isHighCritical());
|
|
434
435
|
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
435
436
|
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
@@ -439,7 +440,7 @@ pub const Species = species.Species;
|
|
|
439
440
|
|
|
440
441
|
test Species {
|
|
441
442
|
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
442
|
-
try expectEqual(100, Species.get(.Celebi).stats.spd);
|
|
443
|
+
try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
|
|
443
444
|
}
|
|
444
445
|
|
|
445
446
|
pub const Type = types.Type;
|
|
@@ -452,7 +453,7 @@ test Types {
|
|
|
452
453
|
try expect(!Type.Steel.special());
|
|
453
454
|
try expect(Type.Dark.special());
|
|
454
455
|
|
|
455
|
-
try expectEqual(3, Type.Water.precedence());
|
|
456
|
+
try expectEqual(@as(u8, 3), Type.Water.precedence());
|
|
456
457
|
try expect(Type.Bug.precedence() > Type.Poison.precedence());
|
|
457
458
|
|
|
458
459
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|
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
|
};
|
|
@@ -65,17 +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
|
-
|
|
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
|
+
},
|
|
79
111
|
},
|
|
80
112
|
};
|
|
81
113
|
}
|
|
@@ -121,7 +153,7 @@ pub const Pokemon = struct {
|
|
|
121
153
|
var pokemon = data.Pokemon{};
|
|
122
154
|
pokemon.species = p.species;
|
|
123
155
|
const species = Species.get(p.species);
|
|
124
|
-
inline for (@typeInfo(@TypeOf(pokemon.stats))
|
|
156
|
+
inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
|
|
125
157
|
const hp = comptime std.mem.eql(u8, field.name, "hp");
|
|
126
158
|
const spc =
|
|
127
159
|
comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
@@ -162,7 +194,7 @@ pub const Pokemon = struct {
|
|
|
162
194
|
|
|
163
195
|
var stats: Stats(u16) = .{};
|
|
164
196
|
const dvs = gen1.DVs.random(rand);
|
|
165
|
-
inline for (@typeInfo(@TypeOf(stats))
|
|
197
|
+
inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
|
|
166
198
|
const hp = std.mem.eql(u8, field.name, "hp");
|
|
167
199
|
const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
168
200
|
@field(stats, field.name) = Stats(u16).calc(
|