@pkmn/engine 0.1.0-dev.1220fbc5 → 0.1.0-dev.130de754
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 +46 -30
- 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 -71
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- 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 +28 -19
- 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 +109 -107
- 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 +310 -203
- package/package.json +59 -132
- package/src/bin/install-pkmn-engine +70 -53
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +201 -0
- package/src/lib/{binding → bindings}/node.zig +15 -32
- package/src/lib/bindings/wasm.zig +51 -0
- package/src/lib/common/array.zig +70 -0
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +46 -30
- package/src/lib/common/js.zig +5 -9
- package/src/lib/common/optional.zig +21 -22
- package/src/lib/common/options.zig +6 -0
- package/src/lib/common/protocol.zig +573 -541
- package/src/lib/common/rational.zig +340 -254
- package/src/lib/common/rng.zig +11 -13
- package/src/lib/common/util.zig +53 -1
- package/src/lib/gen1/calc.zig +724 -175
- package/src/lib/gen1/chance.zig +494 -308
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +2 -4
- package/src/lib/gen1/data/types.zig +3 -4
- package/src/lib/gen1/data.zig +49 -49
- package/src/lib/gen1/helpers.zig +115 -562
- package/src/lib/gen1/mechanics.zig +603 -489
- package/src/lib/gen2/calc.zig +35 -28
- package/src/lib/gen2/chance.zig +122 -518
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +7 -5
- package/src/lib/gen2/data.zig +40 -39
- package/src/lib/gen2/generated.zig +30 -47
- package/src/lib/gen2/helpers.zig +68 -39
- package/src/lib/gen2/mechanics.zig +853 -533
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +14 -0
- package/src/lib/pkmn.zig +123 -29
- package/src/lib/wasm.zig +12 -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/wasm.zig +0 -12
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -330
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const data = @import("../../gen2/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Type = data.Type;
|
|
10
8
|
|
|
11
9
|
/// Representation of a held item in Generation II Pokémon.
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
|
-
const rng = @import("../../common/rng.zig");
|
|
6
3
|
const gen2 = @import("../../gen2/data.zig");
|
|
4
|
+
const rng = @import("../../common/rng.zig");
|
|
5
|
+
const std = @import("std");
|
|
7
6
|
|
|
8
7
|
const assert = std.debug.assert;
|
|
9
|
-
|
|
10
8
|
const percent = rng.Gen12.percent;
|
|
11
9
|
const Type = gen2.Type;
|
|
12
10
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen2 = @import("../../gen2/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Stats = gen2.Stats;
|
|
10
8
|
const Types = gen2.Types;
|
|
11
9
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
7
|
const Effectiveness = gen1.Effectiveness;
|
|
@@ -173,9 +172,12 @@ pub const Type = enum(u8) {
|
|
|
173
172
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
174
173
|
}
|
|
175
174
|
|
|
176
|
-
/// The precedence order of Type `
|
|
177
|
-
pub fn precedence(
|
|
178
|
-
return
|
|
175
|
+
/// The precedence order of Type `t1` vs. Type `t2`.
|
|
176
|
+
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
177
|
+
// The Ice vs. Fire matchup is out of order - return a higher
|
|
178
|
+
// number than anything else so that it sorts last correctly
|
|
179
|
+
if (t1 == .Ice and t2 == .Fire) return 18;
|
|
180
|
+
return PRECEDENCE[@intFromEnum(t2)];
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
/// The internal index of this Type used by Present.
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
|
-
const pkmn = @import("../pkmn.zig");
|
|
5
|
-
|
|
6
|
-
const data = @import("../common/data.zig");
|
|
7
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
8
|
-
const protocol = @import("../common/protocol.zig");
|
|
9
|
-
const rng = @import("../common/rng.zig");
|
|
10
|
-
const util = @import("../common/util.zig");
|
|
11
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const calc = @import("calc.zig");
|
|
5
|
+
const chance = @import("chance.zig");
|
|
6
|
+
const data = @import("../common/data.zig");
|
|
7
|
+
const gen1 = @import("../gen1/data.zig");
|
|
12
8
|
const items = @import("data/items.zig");
|
|
9
|
+
const mechanics = @import("mechanics.zig");
|
|
13
10
|
const moves = @import("data/moves.zig");
|
|
11
|
+
const pkmn = @import("../pkmn.zig");
|
|
12
|
+
const protocol = @import("../common/protocol.zig");
|
|
13
|
+
const rng = @import("../common/rng.zig");
|
|
14
14
|
const species = @import("data/species.zig");
|
|
15
|
+
const std = @import("std");
|
|
15
16
|
const types = @import("data/types.zig");
|
|
16
|
-
|
|
17
|
-
const gen1 = @import("../gen1/data.zig");
|
|
18
|
-
|
|
19
|
-
const calc = @import("calc.zig");
|
|
20
|
-
const chance = @import("chance.zig");
|
|
21
|
-
const mechanics = @import("mechanics.zig");
|
|
17
|
+
const util = @import("../common/util.zig");
|
|
22
18
|
|
|
23
19
|
const assert = std.debug.assert;
|
|
24
20
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -27,11 +23,14 @@ const expect = std.testing.expect;
|
|
|
27
23
|
pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
|
|
28
24
|
pub const MAX_LOGS: usize = 180; // TODO
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
const ReleaseSmall: std.builtin.OptimizeMode =
|
|
27
|
+
if (@hasDecl(std.builtin.OptimizeMode, "ReleaseSmall")) .small else .ReleaseSmall;
|
|
28
|
+
|
|
29
|
+
pub const CHOICES_SIZE = if (builtin.mode == ReleaseSmall)
|
|
31
30
|
MAX_CHOICES
|
|
32
31
|
else
|
|
33
32
|
std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
|
|
34
|
-
pub const LOGS_SIZE = if (builtin.mode ==
|
|
33
|
+
pub const LOGS_SIZE = if (builtin.mode == ReleaseSmall)
|
|
35
34
|
MAX_LOGS
|
|
36
35
|
else
|
|
37
36
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
@@ -45,6 +44,7 @@ const Player = data.Player;
|
|
|
45
44
|
const Result = data.Result;
|
|
46
45
|
|
|
47
46
|
const PointerType = util.PointerType;
|
|
47
|
+
const isPointerTo = util.isPointerTo;
|
|
48
48
|
|
|
49
49
|
const showdown = pkmn.options.showdown;
|
|
50
50
|
|
|
@@ -60,15 +60,15 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
60
60
|
turn: u16 = 0,
|
|
61
61
|
field: Field = .{},
|
|
62
62
|
|
|
63
|
-
_: [padding]u8 =
|
|
63
|
+
_: [padding]u8 = @splat(0),
|
|
64
64
|
|
|
65
65
|
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
66
|
-
assert(
|
|
66
|
+
assert(isPointerTo(self, Self));
|
|
67
67
|
return &self.sides[@intFromEnum(player)];
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
71
|
-
assert(
|
|
71
|
+
assert(isPointerTo(self, Self));
|
|
72
72
|
return &self.sides[@intFromEnum(player.foe())];
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -103,13 +103,13 @@ pub const Weather = enum(u4) {
|
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
pub const Side = extern struct {
|
|
106
|
-
pokemon: [6]Pokemon =
|
|
106
|
+
pokemon: [6]Pokemon = @splat(.{}),
|
|
107
107
|
active: ActivePokemon align(8) = .{},
|
|
108
108
|
conditions: Conditions = .{},
|
|
109
109
|
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
110
110
|
last_used_move: Move = .None,
|
|
111
111
|
|
|
112
|
-
_: [7]u8 =
|
|
112
|
+
_: [7]u8 = @splat(0),
|
|
113
113
|
|
|
114
114
|
const Conditions = packed struct(u32) {
|
|
115
115
|
Spikes: bool = false,
|
|
@@ -134,7 +134,7 @@ pub const Side = extern struct {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
137
|
-
assert(
|
|
137
|
+
assert(isPointerTo(self, Side));
|
|
138
138
|
assert(slot > 0 and slot <= 6);
|
|
139
139
|
const id = self.pokemon[slot - 1].position;
|
|
140
140
|
assert(id > 0 and id <= 6);
|
|
@@ -142,7 +142,7 @@ pub const Side = extern struct {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
145
|
-
assert(
|
|
145
|
+
assert(isPointerTo(self, Side));
|
|
146
146
|
return self.get(1);
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -164,7 +164,7 @@ pub const Side = extern struct {
|
|
|
164
164
|
pub const ActivePokemon = extern struct {
|
|
165
165
|
volatiles: Volatiles align(4) = .{},
|
|
166
166
|
stats: Stats(u16) = .{},
|
|
167
|
-
moves: [4]MoveSlot =
|
|
167
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
168
168
|
boosts: Boosts = .{},
|
|
169
169
|
types: Types = .{},
|
|
170
170
|
species: Species = .None,
|
|
@@ -176,7 +176,7 @@ pub const ActivePokemon = extern struct {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
179
|
-
assert(
|
|
179
|
+
assert(isPointerTo(self, ActivePokemon));
|
|
180
180
|
assert(mslot > 0 and mslot <= 4);
|
|
181
181
|
assert(self.moves[mslot - 1].id != .None);
|
|
182
182
|
return &self.moves[mslot - 1];
|
|
@@ -185,7 +185,7 @@ pub const ActivePokemon = extern struct {
|
|
|
185
185
|
|
|
186
186
|
pub const Pokemon = extern struct {
|
|
187
187
|
stats: Stats(u16) = .{},
|
|
188
|
-
moves: [4]MoveSlot =
|
|
188
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
189
189
|
types: Types = .{},
|
|
190
190
|
dvs: DVs = .{},
|
|
191
191
|
hp: u16 = 0,
|
|
@@ -201,7 +201,7 @@ pub const Pokemon = extern struct {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
204
|
-
assert(
|
|
204
|
+
assert(isPointerTo(self, Pokemon));
|
|
205
205
|
assert(mslot > 0 and mslot <= 4);
|
|
206
206
|
assert(self.moves[mslot - 1].id != .None);
|
|
207
207
|
return &self.moves[mslot - 1];
|
|
@@ -239,7 +239,7 @@ pub const DVs = packed struct(u16) {
|
|
|
239
239
|
};
|
|
240
240
|
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
241
241
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
242
|
-
|
|
242
|
+
const t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
|
|
243
243
|
return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
|
|
244
244
|
}
|
|
245
245
|
};
|
|
@@ -248,12 +248,12 @@ test DVs {
|
|
|
248
248
|
var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
|
|
249
249
|
try expectEqual(Gender.Unknown, dvs.gender);
|
|
250
250
|
try expectEqual(Type.Ice, dvs.type);
|
|
251
|
-
try expectEqual(
|
|
251
|
+
try expectEqual(70, dvs.power());
|
|
252
252
|
|
|
253
253
|
dvs = DVs.from(.Pikachu, .{ .spc = 14 });
|
|
254
254
|
try expectEqual(Gender.Male, dvs.gender);
|
|
255
255
|
try expectEqual(Type.Dark, dvs.type);
|
|
256
|
-
try expectEqual(
|
|
256
|
+
try expectEqual(69, dvs.power());
|
|
257
257
|
|
|
258
258
|
dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
|
|
259
259
|
try expectEqual(Gender.Female, dvs.gender);
|
|
@@ -314,8 +314,8 @@ pub const Volatiles = packed struct(u128) {
|
|
|
314
314
|
Encore: bool = false,
|
|
315
315
|
LockOn: bool = false,
|
|
316
316
|
DestinyBond: bool = false,
|
|
317
|
+
Trapped: bool = false,
|
|
317
318
|
|
|
318
|
-
trapped: bool = false,
|
|
319
319
|
switched: bool = false,
|
|
320
320
|
// TODO: replace with simply looking at Choice on Battle
|
|
321
321
|
switching: bool = false,
|
|
@@ -380,9 +380,9 @@ test Stats {
|
|
|
380
380
|
try expectEqual(0, stats.def);
|
|
381
381
|
|
|
382
382
|
var base = Species.get(.Gyarados).stats;
|
|
383
|
-
try expectEqual(
|
|
383
|
+
try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
384
384
|
base = Species.get(.Pidgeot).stats;
|
|
385
|
-
try expectEqual(
|
|
385
|
+
try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
pub const Boosts = packed struct(u32) {
|
|
@@ -428,8 +428,8 @@ test Move {
|
|
|
428
428
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
429
429
|
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
430
430
|
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
431
|
-
try expectEqual(
|
|
432
|
-
try expectEqual(
|
|
431
|
+
try expectEqual(5, Move.pp(.DynamicPunch));
|
|
432
|
+
try expectEqual(-1, Move.get(.Counter).priority);
|
|
433
433
|
try expect(!move.effect.isHighCritical());
|
|
434
434
|
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
435
435
|
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
@@ -439,7 +439,7 @@ pub const Species = species.Species;
|
|
|
439
439
|
|
|
440
440
|
test Species {
|
|
441
441
|
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
442
|
-
try expectEqual(
|
|
442
|
+
try expectEqual(100, Species.get(.Celebi).stats.spd);
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
pub const Type = types.Type;
|
|
@@ -452,8 +452,9 @@ test Types {
|
|
|
452
452
|
try expect(!Type.Steel.special());
|
|
453
453
|
try expect(Type.Dark.special());
|
|
454
454
|
|
|
455
|
-
try expectEqual(
|
|
456
|
-
try expect(Type.
|
|
455
|
+
try expectEqual(3, Type.precedence(.Water, .Water));
|
|
456
|
+
try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
|
|
457
|
+
try expect(Type.precedence(.Ice, .Fire) > Type.precedence(.Ice, .Water));
|
|
457
458
|
|
|
458
459
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|
|
459
460
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Water, .Fire));
|
|
@@ -1,39 +1,29 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const common = @import("../common/data.zig");
|
|
6
|
-
|
|
7
4
|
const data = @import("data.zig");
|
|
8
5
|
const mechanics = @import("mechanics.zig");
|
|
9
6
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const Effectiveness = data.Effectiveness;
|
|
16
|
-
const Move = data.Move;
|
|
17
|
-
|
|
18
|
-
const Effects = mechanics.Effects;
|
|
19
|
-
const State = mechanics.State;
|
|
20
|
-
|
|
21
|
-
const canMove = mechanics.canMove;
|
|
7
|
+
const adjustDamage = mechanics.adjustDamage;
|
|
8
|
+
const afterMove = mechanics.afterMove;
|
|
9
|
+
const applyDamage = mechanics.applyDamage;
|
|
10
|
+
const calcDamage = mechanics.calcDamage;
|
|
22
11
|
const canCharge = mechanics.canCharge;
|
|
12
|
+
const canMove = mechanics.canMove;
|
|
13
|
+
const checkCriticalHit = mechanics.checkCriticalHit;
|
|
14
|
+
const checkHit = mechanics.checkHit;
|
|
23
15
|
const decrementPP = mechanics.decrementPP;
|
|
16
|
+
const destinyBond = mechanics.destinyBond;
|
|
24
17
|
const doMove = mechanics.doMove;
|
|
25
|
-
const checkHit = mechanics.checkHit;
|
|
26
|
-
const checkCriticalHit = mechanics.checkCriticalHit;
|
|
27
|
-
const calcDamage = mechanics.calcDamage;
|
|
28
|
-
const adjustDamage = mechanics.adjustDamage;
|
|
29
|
-
const randomizeDamage = mechanics.randomizeDamage;
|
|
30
|
-
const applyDamage = mechanics.applyDamage;
|
|
31
|
-
const reportOutcome = mechanics.reportOutcome;
|
|
32
18
|
const effectChance = mechanics.effectChance;
|
|
33
|
-
const
|
|
34
|
-
const
|
|
19
|
+
const Effectiveness = data.Effectiveness;
|
|
20
|
+
const Effects = mechanics.Effects;
|
|
35
21
|
const kingsRock = mechanics.kingsRock;
|
|
36
|
-
const
|
|
22
|
+
const Move = data.Move;
|
|
23
|
+
const Player = common.Player;
|
|
24
|
+
const randomizeDamage = mechanics.randomizeDamage;
|
|
25
|
+
const reportOutcome = mechanics.reportOutcome;
|
|
26
|
+
const State = mechanics.State;
|
|
37
27
|
|
|
38
28
|
const SECONDARY = true;
|
|
39
29
|
const KINGS = true;
|
|
@@ -42,7 +32,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
42
32
|
var log = options.log;
|
|
43
33
|
|
|
44
34
|
var side = battle.side(player);
|
|
45
|
-
|
|
35
|
+
const volatiles = &side.active.volatiles;
|
|
46
36
|
|
|
47
37
|
const ident = battle.active(player);
|
|
48
38
|
const foe_ident = battle.active(player.foe());
|
|
@@ -175,7 +165,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
175
165
|
if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
|
|
176
166
|
// TODO rampage
|
|
177
167
|
// usedmovetext
|
|
178
|
-
try log.move(ident, state.move, foe_ident); // FIXME self? from?
|
|
168
|
+
try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
|
|
179
169
|
try checkHit(battle, player, state, options);
|
|
180
170
|
try checkCriticalHit(battle, player, state, options);
|
|
181
171
|
try calcDamage(battle, player, state, options);
|
|
@@ -260,7 +250,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
260
250
|
try Effects.hyperBeam(battle, player, state, options);
|
|
261
251
|
_ = try afterMove(!KINGS, battle, player, state, options);
|
|
262
252
|
},
|
|
263
|
-
.Counter => {
|
|
253
|
+
.MirrorCoat, .Counter => {
|
|
264
254
|
if (!try canMove(battle, player, state, options)) return;
|
|
265
255
|
try Effects.counter(battle, player, state, options);
|
|
266
256
|
try applyDamage(battle, player, state, options);
|
|
@@ -344,13 +334,17 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
344
334
|
if (!try canMove(battle, player, state, options)) return;
|
|
345
335
|
try Effects.heal(battle, player, state, options);
|
|
346
336
|
},
|
|
337
|
+
.LightScreen => {
|
|
338
|
+
if (!try canMove(battle, player, state, options)) return;
|
|
339
|
+
try Effects.lightScreen(battle, player, state, options);
|
|
340
|
+
},
|
|
347
341
|
.Haze => {
|
|
348
342
|
if (!try canMove(battle, player, state, options)) return;
|
|
349
343
|
try Effects.haze(battle, player, state, options);
|
|
350
344
|
},
|
|
351
|
-
.
|
|
345
|
+
.Reflect => {
|
|
352
346
|
if (!try canMove(battle, player, state, options)) return;
|
|
353
|
-
try Effects.
|
|
347
|
+
try Effects.reflect(battle, player, state, options);
|
|
354
348
|
},
|
|
355
349
|
.FocusEnergy => {
|
|
356
350
|
if (!try canMove(battle, player, state, options)) return;
|
|
@@ -364,7 +358,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
364
358
|
(volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
|
|
365
359
|
if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
|
|
366
360
|
// usedmovetext
|
|
367
|
-
try log.move(ident, state.move, foe_ident); // FIXME self? from?
|
|
361
|
+
try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
|
|
368
362
|
try Effects.bide.unleash(battle, player, state, options);
|
|
369
363
|
|
|
370
364
|
if (state.immune()) {
|
|
@@ -544,19 +538,19 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
544
538
|
try Effects.sandstorm(battle, player, state, options);
|
|
545
539
|
},
|
|
546
540
|
.Rollout => {
|
|
547
|
-
|
|
541
|
+
try Effects.rollout.check(battle, player, state, options);
|
|
548
542
|
// doturn
|
|
549
543
|
const charging = false; // TODO
|
|
550
544
|
const skip_pp = charging or state.move == .Struggle or
|
|
551
545
|
(volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
|
|
552
546
|
if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
|
|
553
547
|
// usedmovetext
|
|
554
|
-
try log.move(ident, state.move, foe_ident); // FIXME self? from?
|
|
548
|
+
try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
|
|
555
549
|
try checkCriticalHit(battle, player, state, options);
|
|
556
550
|
try calcDamage(battle, player, state, options);
|
|
557
551
|
try adjustDamage(battle, player, state, options);
|
|
558
552
|
try checkHit(battle, player, state, options);
|
|
559
|
-
|
|
553
|
+
try Effects.rollout.power(battle, player, state, options);
|
|
560
554
|
try randomizeDamage(battle, player, state, options);
|
|
561
555
|
try applyDamage(battle, player, state, options);
|
|
562
556
|
try reportOutcome(battle, player, state, options);
|
|
@@ -577,12 +571,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
577
571
|
.Swagger => {
|
|
578
572
|
if (!try canMove(battle, player, state, options)) return;
|
|
579
573
|
try checkHit(battle, player, state, options);
|
|
580
|
-
|
|
581
|
-
// TODO attackup2
|
|
582
|
-
// TODO switchturn
|
|
583
|
-
// TODO failuretext
|
|
584
|
-
// TODO switchturn
|
|
585
|
-
// TODO switchturn
|
|
574
|
+
try Effects.boost(battle, player.foe(), state, options);
|
|
586
575
|
try Effects.confusionChance(battle, player, state, options);
|
|
587
576
|
},
|
|
588
577
|
.FuryCutter => {
|
|
@@ -656,7 +645,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
656
645
|
.Magnitude => {
|
|
657
646
|
if (!try canMove(battle, player, state, options)) return;
|
|
658
647
|
try checkCriticalHit(battle, player, state, options);
|
|
659
|
-
|
|
648
|
+
try Effects.magnitude(battle, player, state, options);
|
|
660
649
|
try calcDamage(battle, player, state, options);
|
|
661
650
|
try adjustDamage(battle, player, state, options);
|
|
662
651
|
try randomizeDamage(battle, player, state, options);
|
|
@@ -731,12 +720,6 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
731
720
|
if (!try canMove(battle, player, state, options)) return;
|
|
732
721
|
try Effects.sunnyDay(battle, player, state, options);
|
|
733
722
|
},
|
|
734
|
-
.MirrorCoat => {
|
|
735
|
-
if (!try canMove(battle, player, state, options)) return;
|
|
736
|
-
try Effects.mirrorCoat(battle, player, state, options);
|
|
737
|
-
try applyDamage(battle, player, state, options);
|
|
738
|
-
_ = try afterMove(KINGS, battle, player, state, options);
|
|
739
|
-
},
|
|
740
723
|
.PsychUp => {
|
|
741
724
|
if (!try canMove(battle, player, state, options)) return;
|
|
742
725
|
try Effects.psychUp(battle, player, state, options);
|
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,
|
|
@@ -65,17 +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
|
-
|
|
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
|
+
},
|
|
79
98
|
},
|
|
80
99
|
};
|
|
81
100
|
}
|
|
@@ -121,15 +140,20 @@ pub const Pokemon = struct {
|
|
|
121
140
|
var pokemon = data.Pokemon{};
|
|
122
141
|
pokemon.species = p.species;
|
|
123
142
|
const species = Species.get(p.species);
|
|
124
|
-
inline for (@typeInfo(@TypeOf(pokemon.stats))
|
|
125
|
-
|
|
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");
|
|
126
150
|
const spc =
|
|
127
|
-
comptime std.mem.eql(u8,
|
|
128
|
-
@field(pokemon.stats,
|
|
129
|
-
|
|
130
|
-
@field(species.stats,
|
|
131
|
-
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs,
|
|
132
|
-
@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),
|
|
133
157
|
p.level,
|
|
134
158
|
);
|
|
135
159
|
}
|
|
@@ -162,19 +186,24 @@ pub const Pokemon = struct {
|
|
|
162
186
|
|
|
163
187
|
var stats: Stats(u16) = .{};
|
|
164
188
|
const dvs = gen1.DVs.random(rand);
|
|
165
|
-
inline for (@typeInfo(@TypeOf(stats))
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
@
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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),
|
|
172
201
|
if (rand.chance(u8, 1, 20)) rand.range(u8, 0, 255 + 1) else 255,
|
|
173
202
|
lvl,
|
|
174
203
|
);
|
|
175
204
|
}
|
|
176
205
|
|
|
177
|
-
var ms
|
|
206
|
+
var ms: [4]MoveSlot = @splat(.{});
|
|
178
207
|
const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
|
|
179
208
|
for (0..n) |i| {
|
|
180
209
|
var m: Move = .None;
|