@pkmn/engine 0.1.0-dev.83de29ee → 0.1.0-dev.8b83af6b
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/README.md +3 -3
- package/build/pkg/gen1.js +11 -3
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +4 -2
- package/build/pkg/protocol.js +4 -2
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +0 -1
- package/build/test/display.js.map +1 -1
- package/build.zig +7 -8
- package/package.json +14 -14
- package/src/bin/install-pkmn-engine +1 -1
- package/src/lib/binding/node.zig +17 -20
- package/src/lib/binding/wasm.zig +2 -2
- package/src/lib/common/data.zig +11 -11
- package/src/lib/common/protocol.zig +156 -140
- package/src/lib/common/rng.zig +5 -5
- package/src/lib/gen1/README.md +20 -14
- package/src/lib/gen1/data/moves.zig +10 -10
- package/src/lib/gen1/data/species.zig +2 -2
- package/src/lib/gen1/data/types.zig +3 -3
- package/src/lib/gen1/data.zig +19 -16
- package/src/lib/gen1/helpers.zig +10 -19
- package/src/lib/gen1/mechanics.zig +119 -101
- package/src/lib/gen2/data/items.zig +3 -3
- package/src/lib/gen2/data/moves.zig +1 -1
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +3 -3
- package/src/lib/gen2/data.zig +12 -11
- package/src/lib/gen2/helpers.zig +6 -6
package/src/lib/common/rng.zig
CHANGED
|
@@ -47,7 +47,7 @@ pub const PSRNG = extern struct {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
pub fn range(self: *PSRNG, comptime T: type, from: T, to: Bound(T)) T {
|
|
50
|
-
return @intCast(
|
|
50
|
+
return @intCast(@as(u64, self.src.next()) * (to - from) / divisor + from);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
pub fn chance(self: *PSRNG, comptime T: type, numerator: T, denominator: Bound(T)) bool {
|
|
@@ -80,7 +80,7 @@ pub const Gen12 = extern struct {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
pub fn percent(p: u8) u8 {
|
|
83
|
-
return @intCast(
|
|
83
|
+
return @intCast((@as(u16, p) * 0xFF) / 100);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
pub fn next(self: *Gen12) u8 {
|
|
@@ -114,7 +114,7 @@ pub const Gen34 = extern struct {
|
|
|
114
114
|
|
|
115
115
|
pub fn next(self: *Gen34) u16 {
|
|
116
116
|
self.advance();
|
|
117
|
-
return @intCast(
|
|
117
|
+
return @intCast(self.seed >> 16);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
pub fn advance(self: *Gen34) void {
|
|
@@ -144,7 +144,7 @@ pub const Gen56 = extern struct {
|
|
|
144
144
|
|
|
145
145
|
pub fn next(self: *Gen56) u32 {
|
|
146
146
|
self.advance();
|
|
147
|
-
return @intCast(
|
|
147
|
+
return @intCast(self.seed >> 32);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
pub fn advance(self: *Gen56) void {
|
|
@@ -190,7 +190,7 @@ pub fn FixedRNG(comptime gen: comptime_int, comptime len: usize) type {
|
|
|
190
190
|
|
|
191
191
|
pub fn range(self: *Self, comptime T: type, from: T, to: Bound(T)) T {
|
|
192
192
|
assert(showdown);
|
|
193
|
-
return @intCast(
|
|
193
|
+
return @intCast(@as(u64, self.next()) * (to - from) / divisor + from);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
pub fn chance(
|
package/src/lib/gen1/README.md
CHANGED
|
@@ -352,13 +352,16 @@ data in the browser for ease of debugging.
|
|
|
352
352
|
| 373-376 | 384 | `rng` | The RNG state |
|
|
353
353
|
|
|
354
354
|
- the current `turn` is 2 bytes, written in native-endianness
|
|
355
|
-
- `last_moves` layout depends on whether or not Pokémon Showdown compatibility mode is
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
`side[1]`
|
|
355
|
+
- `last_moves` layout depends on whether or not Pokémon Showdown compatibility mode is enabled
|
|
356
|
+
(`-Dshowdown`). In either case it stores the last selected move index and last executed move for
|
|
357
|
+
each player (required to detect desyncs). In the case of the move index, 0 is used as an empty
|
|
358
|
+
value and 1-4 represent actual moves:
|
|
359
|
+
- if `showdown` is enabled byte 372 is used to store the last selected move index of `side[0]` and
|
|
360
|
+
byte 373 is used to store whether or not the side's last executed move would have been considered
|
|
361
|
+
"counterable"; and bytes 374 and 375 store the same information for `side[1]`
|
|
362
|
+
- otherwise the first 3 bits of byte 372 stores the last selected move index of `side[0]` and the
|
|
363
|
+
next bit stores whether that side's last executed move would have been considered "counterable",
|
|
364
|
+
with the same information for `side[1]`'s being stored in the second 4 bits
|
|
362
365
|
- the `rng` depends on whether or not Pokémon Showdown compatibility mode is enabled (`-Dshowdown`):
|
|
363
366
|
- if `showdown` is enabled, the RNG state begins on byte 376 and consists of a 64-bit seed,
|
|
364
367
|
written in native-endianness
|
|
@@ -489,8 +492,14 @@ Beyond the general bugs listed above, several move effects are implemented incor
|
|
|
489
492
|
Showdown. Some of these moves are [too fundamentally broken to be implemented](#unimplementable) by
|
|
490
493
|
the pkmn engine, but the following moves have their broken behavior preserved in `-Dshowdown` mode:
|
|
491
494
|
|
|
495
|
+
- **Bide**: Bide's damage can overflow if OHKO moves are involved because OHKO moves work by setting
|
|
496
|
+
the damage to 65535 - Pokémon Showdown doesn't implement this overflow and instead lets Bide's
|
|
497
|
+
damage grow unbounded. Additionally, if the opponent faints after Bide inflicts damage on Pokémon
|
|
498
|
+
Showdown residual damage incorrectly still gets applied to Bide's user.
|
|
492
499
|
- **Counter**: On Pokémon Showdown choices made while sleeping (which should not have been
|
|
493
|
-
registered) can erroneously cause Counter to trigger Desync Clause Mod behavior.
|
|
500
|
+
registered) can erroneously cause Counter to trigger Desync Clause Mod behavior. Additionally,
|
|
501
|
+
because Pokémon Showdown neglects to zero the last battle damage if a move misses due to immunity
|
|
502
|
+
or invulnerability Counter will occasionally work on Pokémon Showdown when it should fail.
|
|
494
503
|
- **Leech Seed**: Leech Seed fails to heal its source side if a seeded target faints due to
|
|
495
504
|
recoil/crash damage on Pokémon Showdown.
|
|
496
505
|
- **Pay Day**: Pay Day should still scatter coins if it hits (but doesn't break) and opponent's
|
|
@@ -601,10 +610,6 @@ Showdown's behavior for the following moves (the pkmn engine attempts to reprodu
|
|
|
601
610
|
behavior that can be reproduced without making data structure changes or dramatically deviating from
|
|
602
611
|
the correct control flow):
|
|
603
612
|
|
|
604
|
-
- **Bide**: Bide's damage can overflow if OHKO moves are involved because OHKO moves work by setting
|
|
605
|
-
the damage to 65535 - Pokémon Showdown doesn't implement this overflow and instead lets Bide's
|
|
606
|
-
damage grow unbounded. Additionally, if the opponent faints after Bide inflicts damage on Pokémon
|
|
607
|
-
Showdown residual damage incorrectly still gets applied to Bide's user.
|
|
608
613
|
- **Wrap**: Binding moves like Wrap are implemented on Pokémon Showdown with an artificial
|
|
609
614
|
`partialtrappinglock` volatile as opposed to how it works on the cartridge which simply relies on
|
|
610
615
|
the `Binding` volatile of the opponent. This mistake results in choice locking not being reported
|
|
@@ -623,7 +628,8 @@ the correct control flow):
|
|
|
623
628
|
because Pokémon Showdown deducts PP based on a move's name instead of slot, if Mimic copies a move
|
|
624
629
|
the Pokémon already knows, PP deduction for using either the original move of the mimicked move
|
|
625
630
|
will instead deduct PP for whichever appears at the lower move slot index and the PP will be
|
|
626
|
-
allowed to go negative (effectively allowing for infinite PP use).
|
|
631
|
+
allowed to go negative (effectively allowing for infinite PP use). Pokémon Showdown also doesn't
|
|
632
|
+
deduct PP from using Transform if it was copied by Mimic.
|
|
627
633
|
- **Mirror Move**: As covered above, both Substitute and Binding moves misbehave when used via
|
|
628
634
|
Mirror Move (though Pokémon Showdown has its own weird behavior and does not implement the
|
|
629
635
|
[partial trapping move Mirror Move
|
|
@@ -640,7 +646,7 @@ the correct control flow):
|
|
|
640
646
|
specific *slot* (e.g. the 2nd move slot), and a Pokémon's moves can change after Transform (this
|
|
641
647
|
is not an issue with Disable + Mimic because Mimic happens to replace the same slot). Transform
|
|
642
648
|
also has a bad interaction with Pokémon Showdown's buggy Haze implementation, as Haze on Pokémon
|
|
643
|
-
Showdown does not copy unmodified stats, leaving the Transformed Pokémon with
|
|
649
|
+
Showdown does not copy unmodified stats, leaving the Transformed Pokémon with incorrect stats.
|
|
644
650
|
Furthermore, transforming and then using Mirror Move / Metronome can result in [glitchy behavior
|
|
645
651
|
and
|
|
646
652
|
softlocks](https://pkmn.cc/bulba/Transform_glitches#Transform_.2B_Mirror_Move.2FMetronome_PP_error)
|
|
@@ -1602,44 +1602,44 @@ pub const Move = enum(u8) {
|
|
|
1602
1602
|
|
|
1603
1603
|
/// Whether this effect activates during the "begin" step of move execution.
|
|
1604
1604
|
pub inline fn onBegin(effect: Effect) bool {
|
|
1605
|
-
return @
|
|
1605
|
+
return @intFromEnum(effect) > 0 and @intFromEnum(effect) <= 16;
|
|
1606
1606
|
}
|
|
1607
1607
|
|
|
1608
1608
|
/// Whether this effect lowers stats.
|
|
1609
1609
|
pub inline fn isStatDown(effect: Effect) bool {
|
|
1610
|
-
return @
|
|
1610
|
+
return @intFromEnum(effect) > 16 and @intFromEnum(effect) <= 21;
|
|
1611
1611
|
}
|
|
1612
1612
|
|
|
1613
1613
|
/// Whether this effect activates during the "end" step of move execution.
|
|
1614
1614
|
pub inline fn onEnd(effect: Effect) bool {
|
|
1615
|
-
return @
|
|
1615
|
+
return @intFromEnum(effect) > 16 and @intFromEnum(effect) <= 31;
|
|
1616
1616
|
}
|
|
1617
1617
|
|
|
1618
1618
|
/// Whether this effect is considered to "always happen".
|
|
1619
1619
|
pub inline fn alwaysHappens(effect: Effect) bool {
|
|
1620
|
-
return @
|
|
1620
|
+
return @intFromEnum(effect) > 31 and @intFromEnum(effect) <= 38;
|
|
1621
1621
|
}
|
|
1622
1622
|
|
|
1623
1623
|
/// Whether this effect is handled specially by the engine.
|
|
1624
1624
|
pub inline fn isSpecial(effect: Effect) bool {
|
|
1625
1625
|
// NB: isSpecial includes isMulti up to Twineedle
|
|
1626
|
-
return @
|
|
1626
|
+
return @intFromEnum(effect) > 31 and @intFromEnum(effect) <= 46;
|
|
1627
1627
|
}
|
|
1628
1628
|
|
|
1629
1629
|
/// Whether this effect is a multi-hit effect.
|
|
1630
1630
|
pub inline fn isMulti(effect: Effect) bool {
|
|
1631
|
-
return @
|
|
1631
|
+
return @intFromEnum(effect) > 44 and @intFromEnum(effect) <= 47;
|
|
1632
1632
|
}
|
|
1633
1633
|
|
|
1634
1634
|
/// Whether this effect is has chance of lowering stats.
|
|
1635
1635
|
pub inline fn isStatDownChance(effect: Effect) bool {
|
|
1636
|
-
return @
|
|
1636
|
+
return @intFromEnum(effect) > 47 and @intFromEnum(effect) <= 51;
|
|
1637
1637
|
}
|
|
1638
1638
|
|
|
1639
1639
|
/// Whether this effect has a secondary chance.
|
|
1640
1640
|
pub inline fn isSecondaryChance(effect: Effect) bool {
|
|
1641
1641
|
// NB: isSecondaryChance includes isStatDownChance as well as Twineedle
|
|
1642
|
-
return (@
|
|
1642
|
+
return (@intFromEnum(effect) > 46 and @intFromEnum(effect) <= 61);
|
|
1643
1643
|
}
|
|
1644
1644
|
};
|
|
1645
1645
|
|
|
@@ -1843,12 +1843,12 @@ pub const Move = enum(u8) {
|
|
|
1843
1843
|
/// Returns the `Data` corresponding to the move.
|
|
1844
1844
|
pub inline fn get(id: Move) Data {
|
|
1845
1845
|
assert(id != .None and id != .SKIP_TURN);
|
|
1846
|
-
return DATA[@
|
|
1846
|
+
return DATA[@intFromEnum(id) - 1];
|
|
1847
1847
|
}
|
|
1848
1848
|
|
|
1849
1849
|
/// Returns the base PP of the move.
|
|
1850
1850
|
pub fn pp(id: Move) u8 {
|
|
1851
1851
|
assert(id != .None);
|
|
1852
|
-
return PP[@
|
|
1852
|
+
return PP[@intFromEnum(id) - 1];
|
|
1853
1853
|
}
|
|
1854
1854
|
};
|
|
@@ -1094,12 +1094,12 @@ pub const Species = enum(u8) {
|
|
|
1094
1094
|
/// The Pokémon's critical hit rate=io out of 256.
|
|
1095
1095
|
pub inline fn chance(id: Species) u8 {
|
|
1096
1096
|
assert(id != .None);
|
|
1097
|
-
return CHANCES[@
|
|
1097
|
+
return CHANCES[@intFromEnum(id) - 1];
|
|
1098
1098
|
}
|
|
1099
1099
|
|
|
1100
1100
|
/// Returns the `Data` corresponding to the species.
|
|
1101
1101
|
pub fn get(id: Species) Data {
|
|
1102
1102
|
assert(id != .None);
|
|
1103
|
-
return DATA[@
|
|
1103
|
+
return DATA[@intFromEnum(id) - 1];
|
|
1104
1104
|
}
|
|
1105
1105
|
};
|
|
@@ -91,18 +91,18 @@ pub const Type = enum(u4) {
|
|
|
91
91
|
|
|
92
92
|
/// Whether or not this type is considered to be special as opposed to physical.
|
|
93
93
|
pub inline fn special(self: Type) bool {
|
|
94
|
-
return @
|
|
94
|
+
return @intFromEnum(self) >= @intFromEnum(Type.Fire);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/// The `Effectiveness` of type `t2` vs. type `t1`.
|
|
98
98
|
pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
|
|
99
|
-
return CHART[@
|
|
99
|
+
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/// The precedence order of type `t2` vs. type `t1`.
|
|
103
103
|
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
104
104
|
for (PRECEDENCE, 0..) |matchup, i| {
|
|
105
|
-
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(
|
|
105
|
+
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
|
|
106
106
|
}
|
|
107
107
|
unreachable;
|
|
108
108
|
}
|
package/src/lib/gen1/data.zig
CHANGED
|
@@ -55,24 +55,24 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
55
55
|
turn: u16 = 0,
|
|
56
56
|
/// The last damage dealt by either side.
|
|
57
57
|
last_damage: u16 = 0,
|
|
58
|
-
///
|
|
58
|
+
/// Details about the last move selected/used by each player required to detect desyncs.
|
|
59
59
|
last_moves: MoveDetails = .{},
|
|
60
60
|
/// The pseudo random number generator.
|
|
61
61
|
rng: RNG,
|
|
62
62
|
|
|
63
63
|
/// Returns the `Side` for the given `player`.
|
|
64
64
|
pub inline fn side(self: *Self, player: Player) *Side {
|
|
65
|
-
return &self.sides[@
|
|
65
|
+
return &self.sides[@intFromEnum(player)];
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/// Returns the `Side` of the opponent for the given `player`
|
|
69
69
|
pub inline fn foe(self: *Self, player: Player) *Side {
|
|
70
|
-
return &self.sides[@
|
|
70
|
+
return &self.sides[@intFromEnum(player.foe())];
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/// Returns an identifier for the active Pokémon of `player`.
|
|
74
74
|
pub inline fn active(self: *Self, player: Player) ID {
|
|
75
|
-
return player.ident(@intCast(
|
|
75
|
+
return player.ident(@intCast(self.side(player).order[0]));
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/// Returns the result of applying Player 1's choice `c1` and Player 2's choice `c2` to the
|
|
@@ -201,7 +201,7 @@ pub const MoveSlot = extern struct {
|
|
|
201
201
|
}
|
|
202
202
|
};
|
|
203
203
|
|
|
204
|
-
///
|
|
204
|
+
/// Details required to detect desyncs based on the move last selected/executed by players.
|
|
205
205
|
pub const MoveDetails = packed struct {
|
|
206
206
|
/// The move slot index of the last move selected in the battle menu by Player 1
|
|
207
207
|
p1_index: if (showdown) u8 else u3 = 0,
|
|
@@ -243,13 +243,13 @@ pub const Status = enum(u8) {
|
|
|
243
243
|
/// Whether or not the status `num` is the same as `status`.
|
|
244
244
|
pub inline fn is(num: u8, status: Status) bool {
|
|
245
245
|
if (status == .SLP) return Status.duration(num) > 0;
|
|
246
|
-
return ((num >> @intCast(
|
|
246
|
+
return ((num >> @intCast(@intFromEnum(status))) & 1) != 0;
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
/// Initializes a non-sleep `status`. Use `slp` or `slf` to initialize a sleep status.
|
|
250
250
|
pub inline fn init(status: Status) u8 {
|
|
251
251
|
assert(status != .SLP and status != .EXT);
|
|
252
|
-
return @as(u8, 1) << @intCast(
|
|
252
|
+
return @as(u8, 1) << @intCast(@intFromEnum(status));
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
/// Initializes a non self-inflicted sleep status with duration `dur`.
|
|
@@ -266,7 +266,7 @@ pub const Status = enum(u8) {
|
|
|
266
266
|
|
|
267
267
|
/// Returns the duration of a sleep status.
|
|
268
268
|
pub inline fn duration(num: u8) u3 {
|
|
269
|
-
return @intCast(
|
|
269
|
+
return @intCast(num & SLP);
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
/// Returns whether `num` reflects any status.
|
|
@@ -413,10 +413,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
413
413
|
/// Computes the value of `stat` given a `base`, `dv`, stat `exp` and `level`.
|
|
414
414
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
415
415
|
assert(level > 0 and level <= 100);
|
|
416
|
-
const
|
|
416
|
+
const float: f32 = @floatFromInt(exp);
|
|
417
|
+
const evs = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
417
418
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
418
419
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
419
|
-
return @intCast(
|
|
420
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
420
421
|
}
|
|
421
422
|
};
|
|
422
423
|
}
|
|
@@ -465,7 +466,7 @@ test Boosts {
|
|
|
465
466
|
pub const Move = moves.Move;
|
|
466
467
|
|
|
467
468
|
test Move {
|
|
468
|
-
try expectEqual(2, @
|
|
469
|
+
try expectEqual(2, @intFromEnum(Move.KarateChop));
|
|
469
470
|
const move = Move.get(.Fissure);
|
|
470
471
|
try expectEqual(Move.Effect.OHKO, move.effect);
|
|
471
472
|
try expectEqual(@as(u8, 30), move.accuracy);
|
|
@@ -526,7 +527,7 @@ test Move {
|
|
|
526
527
|
pub const Species = species.Species;
|
|
527
528
|
|
|
528
529
|
test Species {
|
|
529
|
-
try expectEqual(2, @
|
|
530
|
+
try expectEqual(2, @intFromEnum(Species.Ivysaur));
|
|
530
531
|
try expectEqual(@as(u8, 100), Species.get(.Mew).stats.def);
|
|
531
532
|
}
|
|
532
533
|
|
|
@@ -543,9 +544,11 @@ pub const Effectiveness = enum(u8) {
|
|
|
543
544
|
Super = 20,
|
|
544
545
|
|
|
545
546
|
/// Used to determine the effectiness of a neutral damage hit.
|
|
546
|
-
pub const neutral: u16 =
|
|
547
|
+
pub const neutral: u16 =
|
|
548
|
+
@intFromEnum(Effectiveness.Neutral) * @intFromEnum(Effectiveness.Neutral);
|
|
547
549
|
/// Used to detect whether there is a mismatch in type effectivness (relevant for precedence).
|
|
548
|
-
pub const mismatch: u16 =
|
|
550
|
+
pub const mismatch: u16 =
|
|
551
|
+
@intFromEnum(Effectiveness.Resisted) + @intFromEnum(Effectiveness.Super);
|
|
549
552
|
|
|
550
553
|
comptime {
|
|
551
554
|
assert(@sizeOf(Effectiveness) == 1);
|
|
@@ -553,8 +556,8 @@ pub const Effectiveness = enum(u8) {
|
|
|
553
556
|
};
|
|
554
557
|
|
|
555
558
|
test Types {
|
|
556
|
-
try expectEqual(14, @
|
|
557
|
-
try expectEqual(20, @
|
|
559
|
+
try expectEqual(14, @intFromEnum(Type.Dragon));
|
|
560
|
+
try expectEqual(20, @intFromEnum(Effectiveness.Super));
|
|
558
561
|
|
|
559
562
|
try expect(!Type.Ghost.special());
|
|
560
563
|
try expect(Type.Dragon.special());
|
package/src/lib/gen1/helpers.zig
CHANGED
|
@@ -64,7 +64,7 @@ pub const Battle = struct {
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
fn prng(rand: *PSRNG) data.PRNG {
|
|
68
68
|
// GLITCH: initial bytes in seed can only range from 0-252, not 0-255
|
|
69
69
|
const max: u8 = 253;
|
|
70
70
|
return .{
|
|
@@ -90,7 +90,7 @@ pub const Side = struct {
|
|
|
90
90
|
|
|
91
91
|
for (0..ps.len) |i| {
|
|
92
92
|
side.pokemon[i] = Pokemon.init(ps[i]);
|
|
93
|
-
side.order[i] = @
|
|
93
|
+
side.order[i] = @as(u4, @intCast(i)) + 1;
|
|
94
94
|
}
|
|
95
95
|
return side;
|
|
96
96
|
}
|
|
@@ -101,7 +101,7 @@ pub const Side = struct {
|
|
|
101
101
|
|
|
102
102
|
for (0..n) |i| {
|
|
103
103
|
side.pokemon[i] = Pokemon.random(rand, opt);
|
|
104
|
-
side.order[i] = @
|
|
104
|
+
side.order[i] = @as(u4, @intCast(i)) + 1;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
return side;
|
|
@@ -139,7 +139,7 @@ pub const Pokemon = struct {
|
|
|
139
139
|
for (p.moves, 0..) |m, j| {
|
|
140
140
|
pokemon.moves[j].id = m;
|
|
141
141
|
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
142
|
-
pokemon.moves[j].pp = @intCast(
|
|
142
|
+
pokemon.moves[j].pp = @intCast(@min(Move.pp(m) / 5 * 8, 61));
|
|
143
143
|
}
|
|
144
144
|
if (p.hp) |hp| {
|
|
145
145
|
pokemon.hp = hp;
|
|
@@ -153,7 +153,7 @@ pub const Pokemon = struct {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
|
|
156
|
-
const s = @
|
|
156
|
+
const s: Species = @enumFromInt(rand.range(u8, 1, Species.size + 1));
|
|
157
157
|
const species = Species.get(s);
|
|
158
158
|
const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
|
|
159
159
|
var stats: Stats(u16) = .{};
|
|
@@ -176,15 +176,15 @@ pub const Pokemon = struct {
|
|
|
176
176
|
for (0..n) |i| {
|
|
177
177
|
var m: Move = .None;
|
|
178
178
|
sample: while (true) {
|
|
179
|
-
m = @
|
|
179
|
+
m = @enumFromInt(rand.range(u8, 1, Move.size - 1 + 1));
|
|
180
180
|
if (opt.block and blocked(m)) continue :sample;
|
|
181
181
|
for (0..i) |j| if (ms[j].id == m) continue :sample;
|
|
182
182
|
break;
|
|
183
183
|
}
|
|
184
|
-
const pp_ups =
|
|
184
|
+
const pp_ups: u8 =
|
|
185
185
|
if (!opt.cleric and rand.chance(u8, 1, 10)) rand.range(u2, 0, 2 + 1) else 3;
|
|
186
186
|
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
187
|
-
const max_pp = @intCast(
|
|
187
|
+
const max_pp: u8 = @intCast(Move.pp(m) + pp_ups * @min(Move.pp(m) / 5, 7));
|
|
188
188
|
ms[i] = .{
|
|
189
189
|
.id = m,
|
|
190
190
|
.pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
|
|
@@ -206,19 +206,10 @@ pub const Pokemon = struct {
|
|
|
206
206
|
}
|
|
207
207
|
};
|
|
208
208
|
|
|
209
|
-
// TODO: uncomment once integration tests pass
|
|
210
|
-
// fn blocked(m: Move) bool {
|
|
211
|
-
// // Binding moves are borked but only via Mirror Move / Metronome which are already blocked
|
|
212
|
-
// return switch (m) {
|
|
213
|
-
// .Mimic, .Metronome, .MirrorMove, .Transform => true,
|
|
214
|
-
// else => false,
|
|
215
|
-
// };
|
|
216
|
-
// }
|
|
217
|
-
|
|
218
209
|
fn blocked(m: Move) bool {
|
|
219
|
-
|
|
210
|
+
// Binding moves are borked but only via Mirror Move / Metronome which are already blocked
|
|
220
211
|
return switch (m) {
|
|
221
|
-
.
|
|
212
|
+
.Mimic, .Metronome, .MirrorMove, .Transform => true,
|
|
222
213
|
else => false,
|
|
223
214
|
};
|
|
224
215
|
}
|