@pkmn/engine 0.1.0-dev.46307385 → 0.1.0-dev.4cc7c5c2
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/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +2 -2
- package/build/pkg/protocol.js +56 -37
- package/build/pkg/protocol.js.map +1 -1
- package/build.zig +7 -3
- package/package.json +20 -11
- package/src/lib/binding/node.zig +1 -1
- package/src/lib/common/data.zig +4 -16
- package/src/lib/common/optional.zig +8 -1
- package/src/lib/common/protocol.zig +518 -66
- package/src/lib/common/rational.zig +1 -1
- package/src/lib/common/rng.zig +6 -6
- package/src/lib/gen1/README.md +3 -1
- package/src/lib/gen1/calc.zig +2 -3
- package/src/lib/gen1/chance.zig +51 -35
- package/src/lib/gen1/data/moves.zig +182 -170
- package/src/lib/gen1/data/types.zig +2 -2
- package/src/lib/gen1/data.zig +11 -23
- package/src/lib/gen1/helpers.zig +13 -28
- package/src/lib/gen1/mechanics.zig +43 -52
- package/src/lib/gen2/README.md +94 -72
- package/src/lib/gen2/data/items.zig +10 -16
- package/src/lib/gen2/data/moves.zig +659 -565
- package/src/lib/gen2/data/types.zig +127 -25
- package/src/lib/gen2/data.zig +100 -63
- package/src/lib/gen2/helpers.zig +8 -12
- package/src/lib/gen2/mechanics.zig +2589 -7
|
@@ -30,7 +30,7 @@ pub const Type = enum(u4) {
|
|
|
30
30
|
Ice,
|
|
31
31
|
Dragon,
|
|
32
32
|
|
|
33
|
-
const CHART
|
|
33
|
+
const CHART: [15][15]Effectiveness = .{
|
|
34
34
|
[_]Effectiveness{ N, N, N, N, N, R, N, I, N, N, N, N, N, N, N }, // Normal
|
|
35
35
|
[_]Effectiveness{ S, N, R, R, N, S, R, I, N, N, N, N, R, S, N }, // Fighting
|
|
36
36
|
[_]Effectiveness{ N, S, N, N, N, R, S, N, N, N, S, R, N, N, N }, // Flying
|
|
@@ -99,7 +99,7 @@ pub const Type = enum(u4) {
|
|
|
99
99
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
/// The precedence order of
|
|
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
105
|
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
|
package/src/lib/gen1/data.zig
CHANGED
|
@@ -214,7 +214,7 @@ pub const Pokemon = extern struct {
|
|
|
214
214
|
}
|
|
215
215
|
};
|
|
216
216
|
|
|
217
|
-
/// Representation of a Generation I
|
|
217
|
+
/// Representation of a Generation I Pokémon's move slot in a battle.
|
|
218
218
|
pub const MoveSlot = extern struct {
|
|
219
219
|
/// The identifier for the move.
|
|
220
220
|
id: Move = .None,
|
|
@@ -222,20 +222,16 @@ pub const MoveSlot = extern struct {
|
|
|
222
222
|
pp: u8 = 0,
|
|
223
223
|
|
|
224
224
|
comptime {
|
|
225
|
-
assert(@sizeOf(MoveSlot) ==
|
|
225
|
+
assert(@sizeOf(MoveSlot) == 2);
|
|
226
226
|
}
|
|
227
227
|
};
|
|
228
228
|
|
|
229
229
|
/// Details required to detect desyncs based on the move last selected/executed by players.
|
|
230
|
-
pub const MoveDetails = packed struct {
|
|
230
|
+
pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
|
|
231
231
|
/// The move slot index of the last move selected in the battle menu.
|
|
232
232
|
index: if (showdown) u8 else u4 = 0,
|
|
233
233
|
/// Whether the last move executed was counterable.
|
|
234
234
|
counterable: if (showdown) u8 else u4 = 0,
|
|
235
|
-
|
|
236
|
-
comptime {
|
|
237
|
-
assert(@sizeOf(MoveDetails) == if (showdown) 2 else 1);
|
|
238
|
-
}
|
|
239
235
|
};
|
|
240
236
|
|
|
241
237
|
/// Bitfield representation of a Generation I & II Pokémon's major status condition.
|
|
@@ -329,7 +325,7 @@ test Status {
|
|
|
329
325
|
}
|
|
330
326
|
|
|
331
327
|
/// Bitfield representation of volatile statuses and associated data in Generation I.
|
|
332
|
-
pub const Volatiles = packed struct {
|
|
328
|
+
pub const Volatiles = packed struct(u64) {
|
|
333
329
|
/// Whether the "Bide" volatile status is present.
|
|
334
330
|
Bide: bool = false,
|
|
335
331
|
/// Whether the "Thrashing" volatile status is present.
|
|
@@ -387,14 +383,10 @@ pub const Volatiles = packed struct {
|
|
|
387
383
|
disabled_move: u3 = 0,
|
|
388
384
|
/// The number of turns toxic damage has been accumulating.
|
|
389
385
|
toxic: u5 = 0,
|
|
390
|
-
|
|
391
|
-
comptime {
|
|
392
|
-
assert(@sizeOf(Volatiles) == 8);
|
|
393
|
-
}
|
|
394
386
|
};
|
|
395
387
|
|
|
396
388
|
test Volatiles {
|
|
397
|
-
var volatiles =
|
|
389
|
+
var volatiles: Volatiles = .{};
|
|
398
390
|
volatiles.Confusion = true;
|
|
399
391
|
volatiles.confusion = 2;
|
|
400
392
|
volatiles.Thrashing = true;
|
|
@@ -456,7 +448,7 @@ test Stats {
|
|
|
456
448
|
}
|
|
457
449
|
|
|
458
450
|
/// Representation of a Pokémon's boosts in Generation I.
|
|
459
|
-
pub const Boosts = packed struct {
|
|
451
|
+
pub const Boosts = packed struct(u32) {
|
|
460
452
|
/// A Pokémon's Attack boosts.
|
|
461
453
|
atk: i4 = 0,
|
|
462
454
|
/// A Pokémon's Defense boosts.
|
|
@@ -471,14 +463,10 @@ pub const Boosts = packed struct {
|
|
|
471
463
|
evasion: i4 = 0,
|
|
472
464
|
|
|
473
465
|
_: u8 = 0,
|
|
474
|
-
|
|
475
|
-
comptime {
|
|
476
|
-
assert(@sizeOf(Boosts) == 4);
|
|
477
|
-
}
|
|
478
466
|
};
|
|
479
467
|
|
|
480
468
|
test Boosts {
|
|
481
|
-
const boosts =
|
|
469
|
+
const boosts: Boosts = .{ .spc = -6 };
|
|
482
470
|
try expectEqual(0, boosts.atk);
|
|
483
471
|
try expectEqual(-6, boosts.spc);
|
|
484
472
|
}
|
|
@@ -490,7 +478,7 @@ test Move {
|
|
|
490
478
|
try expectEqual(2, @intFromEnum(Move.KarateChop));
|
|
491
479
|
const move = Move.get(.Fissure);
|
|
492
480
|
try expectEqual(Move.Effect.OHKO, move.effect);
|
|
493
|
-
try expectEqual(
|
|
481
|
+
try expectEqual(rng.Gen12.percent(30), move.accuracy);
|
|
494
482
|
try expectEqual(Type.Ground, move.type);
|
|
495
483
|
|
|
496
484
|
try expect(!Move.Effect.onBegin(.None));
|
|
@@ -628,16 +616,16 @@ pub const DVs = struct {
|
|
|
628
616
|
};
|
|
629
617
|
|
|
630
618
|
test DVs {
|
|
631
|
-
var dvs =
|
|
619
|
+
var dvs: DVs = .{ .spc = 15, .spe = 15 };
|
|
632
620
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
633
|
-
dvs =
|
|
621
|
+
dvs = .{
|
|
634
622
|
.atk = 5,
|
|
635
623
|
.def = 15,
|
|
636
624
|
.spe = 13,
|
|
637
625
|
.spc = 13,
|
|
638
626
|
};
|
|
639
627
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
640
|
-
dvs =
|
|
628
|
+
dvs = .{
|
|
641
629
|
.def = 3,
|
|
642
630
|
.spe = 10,
|
|
643
631
|
.spc = 11,
|
package/src/lib/gen1/helpers.zig
CHANGED
|
@@ -455,36 +455,21 @@ pub const Rolls = struct {
|
|
|
455
455
|
}
|
|
456
456
|
|
|
457
457
|
const MOVE_NONE = [_]Move{.None};
|
|
458
|
-
const MOVES = init: {
|
|
459
|
-
var moves: [Move.size - 2]Move = undefined;
|
|
460
|
-
var i: usize = 0;
|
|
461
|
-
for (@typeInfo(Move).Enum.fields) |f| {
|
|
462
|
-
if (!(std.mem.eql(u8, f.name, "None") or
|
|
463
|
-
std.mem.eql(u8, f.name, "Metronome") or
|
|
464
|
-
std.mem.eql(u8, f.name, "Struggle") or
|
|
465
|
-
std.mem.eql(u8, f.name, "SKIP_TURN")))
|
|
466
|
-
{
|
|
467
|
-
moves[i] = @field(Move, f.name);
|
|
468
|
-
i += 1;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
break :init moves;
|
|
472
|
-
};
|
|
473
458
|
|
|
474
459
|
/// Returns a slice with the correct range of values for metronome given the `action` state.
|
|
475
460
|
pub inline fn metronome(action: Action) []const Move {
|
|
476
|
-
return if (@field(action, "metronome") == .None) &MOVE_NONE else &
|
|
461
|
+
return if (@field(action, "metronome") == .None) &MOVE_NONE else &Move.METRONOME;
|
|
477
462
|
}
|
|
478
463
|
};
|
|
479
464
|
|
|
480
465
|
test "Rolls.speedTie" {
|
|
481
|
-
const actions =
|
|
466
|
+
const actions: Actions = .{ .p1 = .{ .speed_tie = .P2 } };
|
|
482
467
|
try expectEqualSlices(Optional(Player), &.{ .P1, .P2 }, Rolls.speedTie(actions.p1));
|
|
483
468
|
try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
|
|
484
469
|
}
|
|
485
470
|
|
|
486
471
|
test "Rolls.damage" {
|
|
487
|
-
const actions =
|
|
472
|
+
const actions: Actions = .{ .p2 = .{ .damage = 221 } };
|
|
488
473
|
try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
|
|
489
474
|
try expectEqual(Rolls.Range{ .min = 217, .max = 256 }, Rolls.damage(actions.p2, .None));
|
|
490
475
|
try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p2, .false));
|
|
@@ -502,7 +487,7 @@ test "Rolls.coalesce" {
|
|
|
502
487
|
}
|
|
503
488
|
|
|
504
489
|
test "Rolls.hit" {
|
|
505
|
-
const actions =
|
|
490
|
+
const actions: Actions = .{ .p2 = .{ .hit = .true } };
|
|
506
491
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.hit(actions.p1, .None));
|
|
507
492
|
try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.hit(actions.p2, .None));
|
|
508
493
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.hit(actions.p2, .true));
|
|
@@ -510,7 +495,7 @@ test "Rolls.hit" {
|
|
|
510
495
|
}
|
|
511
496
|
|
|
512
497
|
test "Rolls.secondaryChance" {
|
|
513
|
-
const actions =
|
|
498
|
+
const actions: Actions = .{ .p1 = .{ .secondary_chance = .true } };
|
|
514
499
|
try expectEqualSlices(
|
|
515
500
|
Optional(bool),
|
|
516
501
|
&.{ .false, .true },
|
|
@@ -521,7 +506,7 @@ test "Rolls.secondaryChance" {
|
|
|
521
506
|
}
|
|
522
507
|
|
|
523
508
|
test "Rolls.criticalHit" {
|
|
524
|
-
const actions =
|
|
509
|
+
const actions: Actions = .{ .p1 = .{ .critical_hit = .true } };
|
|
525
510
|
try expectEqualSlices(
|
|
526
511
|
Optional(bool),
|
|
527
512
|
&.{ .false, .true },
|
|
@@ -532,14 +517,14 @@ test "Rolls.criticalHit" {
|
|
|
532
517
|
}
|
|
533
518
|
|
|
534
519
|
test "Rolls.confused" {
|
|
535
|
-
const actions =
|
|
520
|
+
const actions: Actions = .{ .p2 = .{ .confused = .true } };
|
|
536
521
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, 1));
|
|
537
522
|
try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.confused(actions.p2, 1));
|
|
538
523
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, 0));
|
|
539
524
|
}
|
|
540
525
|
|
|
541
526
|
test "Rolls.paralyzed" {
|
|
542
|
-
const actions =
|
|
527
|
+
const actions: Actions = .{ .p2 = .{ .paralyzed = .true } };
|
|
543
528
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.paralyzed(actions.p1, .None));
|
|
544
529
|
try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.paralyzed(actions.p2, .None));
|
|
545
530
|
try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.paralyzed(actions.p2, .false));
|
|
@@ -547,7 +532,7 @@ test "Rolls.paralyzed" {
|
|
|
547
532
|
}
|
|
548
533
|
|
|
549
534
|
test "Rolls.duration" {
|
|
550
|
-
const actions =
|
|
535
|
+
const actions: Actions = .{ .p2 = .{ .duration = 3 } };
|
|
551
536
|
try expectEqualSlices(u1, &.{0}, Rolls.duration(actions.p1, .None));
|
|
552
537
|
try expectEqualSlices(u1, &.{1}, Rolls.duration(actions.p2, .None));
|
|
553
538
|
try expectEqualSlices(u1, &.{0}, Rolls.duration(actions.p2, .false));
|
|
@@ -757,27 +742,27 @@ test "Rolls.binding" {
|
|
|
757
742
|
}
|
|
758
743
|
|
|
759
744
|
test "Rolls.moveSlot" {
|
|
760
|
-
const actions =
|
|
745
|
+
const actions: Actions = .{ .p2 = .{ .move_slot = 3 } };
|
|
761
746
|
try expectEqualSlices(u4, &.{0}, Rolls.moveSlot(actions.p1, .None));
|
|
762
747
|
try expectEqualSlices(u4, &.{ 1, 2, 3, 4 }, Rolls.moveSlot(actions.p2, .None));
|
|
763
748
|
try expectEqualSlices(u4, &.{0}, Rolls.moveSlot(actions.p2, .false));
|
|
764
749
|
}
|
|
765
750
|
|
|
766
751
|
test "Rolls.multiHit" {
|
|
767
|
-
const actions =
|
|
752
|
+
const actions: Actions = .{ .p2 = .{ .multi_hit = 3 } };
|
|
768
753
|
try expectEqualSlices(u4, &.{0}, Rolls.multiHit(actions.p1, .None));
|
|
769
754
|
try expectEqualSlices(u4, &.{ 2, 3, 4, 5 }, Rolls.multiHit(actions.p2, .None));
|
|
770
755
|
try expectEqualSlices(u4, &.{0}, Rolls.multiHit(actions.p2, .false));
|
|
771
756
|
}
|
|
772
757
|
|
|
773
758
|
test "Rolls.metronome" {
|
|
774
|
-
const actions =
|
|
759
|
+
const actions: Actions = .{ .p2 = .{ .metronome = .Surf } };
|
|
775
760
|
try expectEqualSlices(Move, &.{.None}, Rolls.metronome(actions.p1));
|
|
776
761
|
try expectEqual(@as(Move, @enumFromInt(24)), Rolls.metronome(actions.p2)[23]);
|
|
777
762
|
}
|
|
778
763
|
|
|
779
764
|
test "Rolls.psywave" {
|
|
780
|
-
const actions =
|
|
765
|
+
const actions: Actions = .{ .p2 = .{ .psywave = 79 } };
|
|
781
766
|
var side = Side.init(&[_]Pokemon{.{
|
|
782
767
|
.species = .Bulbasaur,
|
|
783
768
|
.level = 100,
|
|
@@ -7,8 +7,6 @@ const DEBUG = @import("../common/debug.zig").print;
|
|
|
7
7
|
const protocol = @import("../common/protocol.zig");
|
|
8
8
|
const rng = @import("../common/rng.zig");
|
|
9
9
|
|
|
10
|
-
const Action = @import("chance.zig").Action;
|
|
11
|
-
|
|
12
10
|
const data = @import("data.zig");
|
|
13
11
|
|
|
14
12
|
const assert = std.debug.assert;
|
|
@@ -59,13 +57,7 @@ const BOOSTS = &[_][2]u8{
|
|
|
59
57
|
|
|
60
58
|
const MAX_STAT_VALUE = 999;
|
|
61
59
|
|
|
62
|
-
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
63
|
-
|
|
64
60
|
pub fn update(battle: anytype, c1: Choice, c2: Choice, options: anytype) !Result {
|
|
65
|
-
// FIXME
|
|
66
|
-
// std.debug.print("\n", .{});
|
|
67
|
-
// DEBUG(battle.turn);
|
|
68
|
-
// std.debug.print("// ", .{});
|
|
69
61
|
assert(c1.type != .Pass or c2.type != .Pass or battle.turn == 0);
|
|
70
62
|
if (battle.turn == 0) return start(battle, options);
|
|
71
63
|
|
|
@@ -214,12 +206,9 @@ fn selectMove(
|
|
|
214
206
|
}
|
|
215
207
|
break :ok true;
|
|
216
208
|
};
|
|
217
|
-
|
|
218
209
|
assert(struggle);
|
|
219
|
-
saveMove(battle, player, choice);
|
|
220
|
-
} else {
|
|
221
|
-
saveMove(battle, player, choice);
|
|
222
210
|
}
|
|
211
|
+
saveMove(battle, player, choice);
|
|
223
212
|
|
|
224
213
|
return null;
|
|
225
214
|
}
|
|
@@ -1438,7 +1427,7 @@ fn moveHit(
|
|
|
1438
1427
|
const overwritten = overwrite and state > 0;
|
|
1439
1428
|
assert(!overwritten or (0 < state and state <= 255 and !side.active.volatiles.Bide));
|
|
1440
1429
|
|
|
1441
|
-
var accuracy = if (overwritten) state else @as(u16,
|
|
1430
|
+
var accuracy = if (overwritten) state else @as(u16, move.accuracy);
|
|
1442
1431
|
var boost = BOOSTS[@as(u4, @intCast(@as(i8, side.active.boosts.accuracy) + 6))];
|
|
1443
1432
|
accuracy = accuracy * boost[0] / boost[1];
|
|
1444
1433
|
boost = BOOSTS[@as(u4, @intCast(@as(i8, -foe.active.boosts.evasion) + 6))];
|
|
@@ -1499,8 +1488,8 @@ fn checkFaint(
|
|
|
1499
1488
|
}
|
|
1500
1489
|
|
|
1501
1490
|
const foe_choice: Choice.Type = if (foe_fainted) .Switch else .Pass;
|
|
1502
|
-
if (player == .P1) return
|
|
1503
|
-
return
|
|
1491
|
+
if (player == .P1) return .{ .p1 = .Switch, .p2 = foe_choice };
|
|
1492
|
+
return .{ .p1 = foe_choice, .p2 = .Switch };
|
|
1504
1493
|
}
|
|
1505
1494
|
|
|
1506
1495
|
fn faint(battle: anytype, player: Player, done: bool, options: anytype) !?Result {
|
|
@@ -1548,21 +1537,19 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !void {
|
|
|
1548
1537
|
var volatiles = &side.active.volatiles;
|
|
1549
1538
|
|
|
1550
1539
|
const brn = Status.is(stored.status, .BRN);
|
|
1551
|
-
if (brn or Status.is(stored.status, .PSN))
|
|
1540
|
+
if (brn or Status.is(stored.status, .PSN)) {
|
|
1552
1541
|
var damage = @max(stored.stats.hp / 16, 1);
|
|
1553
|
-
|
|
1554
1542
|
if (volatiles.Toxic) {
|
|
1555
1543
|
volatiles.toxic += 1;
|
|
1556
1544
|
damage *= volatiles.toxic;
|
|
1557
1545
|
}
|
|
1546
|
+
damage = @min(damage, stored.hp);
|
|
1558
1547
|
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
// Pokémon Showdown uses damageOf here but its not relevant in Generation I
|
|
1565
|
-
try options.log.damage(ident, stored, if (brn) Damage.Burn else Damage.Poison);
|
|
1548
|
+
if (!showdown or damage > 0) {
|
|
1549
|
+
stored.hp -= damage;
|
|
1550
|
+
// Pokémon Showdown uses damageOf here but its not relevant in Generation I
|
|
1551
|
+
try options.log.damage(ident, stored, if (brn) Damage.Burn else Damage.Poison);
|
|
1552
|
+
}
|
|
1566
1553
|
}
|
|
1567
1554
|
|
|
1568
1555
|
if (volatiles.LeechSeed) {
|
|
@@ -2395,12 +2382,17 @@ pub const Effects = struct {
|
|
|
2395
2382
|
}
|
|
2396
2383
|
|
|
2397
2384
|
fn switchAndTeleport(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
2398
|
-
if (!showdown or battle.side(player).last_selected_move == .Teleport) return;
|
|
2399
|
-
|
|
2400
2385
|
// Whirlwind/Roar should not roll to hit/reset damage but Pokémon Showdown does anyway
|
|
2401
|
-
if (
|
|
2402
|
-
|
|
2403
|
-
|
|
2386
|
+
if (showdown) {
|
|
2387
|
+
if (battle.side(player).last_selected_move == .Teleport) return;
|
|
2388
|
+
if (try checkHit(battle, player, move, options)) {
|
|
2389
|
+
try options.chance.commit(player, .hit);
|
|
2390
|
+
}
|
|
2391
|
+
battle.last_damage = 0;
|
|
2392
|
+
} else {
|
|
2393
|
+
try options.log.fail(battle.active(player), .None);
|
|
2394
|
+
try options.log.laststill();
|
|
2395
|
+
}
|
|
2404
2396
|
}
|
|
2405
2397
|
|
|
2406
2398
|
fn thrashing(battle: anytype, player: Player, options: anytype) void {
|
|
@@ -2705,15 +2697,15 @@ fn clearVolatiles(battle: anytype, who: Player, clear: bool, options: anytype) !
|
|
|
2705
2697
|
}
|
|
2706
2698
|
if (volatiles.Mist) {
|
|
2707
2699
|
volatiles.Mist = false;
|
|
2708
|
-
try log.end(ident, .
|
|
2700
|
+
try log.end(ident, .MistSilent);
|
|
2709
2701
|
}
|
|
2710
2702
|
if (volatiles.FocusEnergy) {
|
|
2711
2703
|
volatiles.FocusEnergy = false;
|
|
2712
|
-
try log.end(ident, .
|
|
2704
|
+
try log.end(ident, .FocusEnergySilent);
|
|
2713
2705
|
}
|
|
2714
2706
|
if (volatiles.LeechSeed) {
|
|
2715
2707
|
volatiles.LeechSeed = false;
|
|
2716
|
-
try log.end(ident, .
|
|
2708
|
+
try log.end(ident, .LeechSeedSilent);
|
|
2717
2709
|
}
|
|
2718
2710
|
if (!showdown and volatiles.Toxic) {
|
|
2719
2711
|
volatiles.Toxic = false;
|
|
@@ -2721,17 +2713,17 @@ fn clearVolatiles(battle: anytype, who: Player, clear: bool, options: anytype) !
|
|
|
2721
2713
|
}
|
|
2722
2714
|
if (volatiles.LightScreen) {
|
|
2723
2715
|
volatiles.LightScreen = false;
|
|
2724
|
-
try log.end(ident, .
|
|
2716
|
+
try log.end(ident, .LightScreenSilent);
|
|
2725
2717
|
}
|
|
2726
2718
|
if (volatiles.Reflect) {
|
|
2727
2719
|
volatiles.Reflect = false;
|
|
2728
|
-
try log.end(ident, .
|
|
2720
|
+
try log.end(ident, .ReflectSilent);
|
|
2729
2721
|
}
|
|
2730
2722
|
if (showdown and volatiles.Toxic) {
|
|
2731
2723
|
volatiles.Toxic = false;
|
|
2732
2724
|
// Pokémon Showdown erroneously clears the toxic counter
|
|
2733
2725
|
volatiles.toxic = 0;
|
|
2734
|
-
try log.end(ident, .
|
|
2726
|
+
try log.end(ident, .ToxicSilent);
|
|
2735
2727
|
}
|
|
2736
2728
|
}
|
|
2737
2729
|
|
|
@@ -2867,11 +2859,9 @@ pub const Rolls = struct {
|
|
|
2867
2859
|
inline fn metronome(battle: anytype, player: Player, options: anytype) !Move {
|
|
2868
2860
|
const move: Move = if (options.calc.overridden(player, .metronome)) |val|
|
|
2869
2861
|
val
|
|
2870
|
-
else if (showdown)
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
break :move @enumFromInt(r + mod);
|
|
2874
|
-
} else move: {
|
|
2862
|
+
else if (showdown)
|
|
2863
|
+
Move.METRONOME[battle.rng.range(u8, 0, Move.METRONOME.len)]
|
|
2864
|
+
else move: {
|
|
2875
2865
|
while (true) {
|
|
2876
2866
|
const r = battle.rng.next();
|
|
2877
2867
|
if (r == 0 or r == @intFromEnum(Move.Metronome)) continue;
|
|
@@ -2905,7 +2895,7 @@ pub const Rolls = struct {
|
|
|
2905
2895
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2906
2896
|
@intCast(val)
|
|
2907
2897
|
else if (showdown)
|
|
2908
|
-
|
|
2898
|
+
battle.rng.range(u3, 1, 8)
|
|
2909
2899
|
else duration: {
|
|
2910
2900
|
while (true) {
|
|
2911
2901
|
const r = battle.rng.next() & 7;
|
|
@@ -2922,7 +2912,7 @@ pub const Rolls = struct {
|
|
|
2922
2912
|
const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
|
|
2923
2913
|
val
|
|
2924
2914
|
else if (showdown)
|
|
2925
|
-
|
|
2915
|
+
battle.rng.range(u4, 1, 9)
|
|
2926
2916
|
else
|
|
2927
2917
|
@intCast((battle.rng.next() & 7) + 1);
|
|
2928
2918
|
|
|
@@ -2935,7 +2925,7 @@ pub const Rolls = struct {
|
|
|
2935
2925
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2936
2926
|
@intCast(val)
|
|
2937
2927
|
else if (showdown)
|
|
2938
|
-
|
|
2928
|
+
battle.rng.range(u3, 2, 6)
|
|
2939
2929
|
else
|
|
2940
2930
|
@intCast((battle.rng.next() & 3) + 2);
|
|
2941
2931
|
|
|
@@ -2948,7 +2938,7 @@ pub const Rolls = struct {
|
|
|
2948
2938
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2949
2939
|
@intCast(val)
|
|
2950
2940
|
else if (showdown)
|
|
2951
|
-
|
|
2941
|
+
battle.rng.range(u3, 2, 4)
|
|
2952
2942
|
else
|
|
2953
2943
|
@intCast((battle.rng.next() & 1) + 2);
|
|
2954
2944
|
|
|
@@ -2957,6 +2947,8 @@ pub const Rolls = struct {
|
|
|
2957
2947
|
return duration;
|
|
2958
2948
|
}
|
|
2959
2949
|
|
|
2950
|
+
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
2951
|
+
|
|
2960
2952
|
inline fn distribution(
|
|
2961
2953
|
battle: anytype,
|
|
2962
2954
|
comptime effect: Move.Effect,
|
|
@@ -2967,7 +2959,7 @@ pub const Rolls = struct {
|
|
|
2967
2959
|
const n: u3 = if (options.calc.overridden(player, roll)) |val|
|
|
2968
2960
|
@intCast(val)
|
|
2969
2961
|
else if (showdown)
|
|
2970
|
-
DISTRIBUTION[battle.rng.range(
|
|
2962
|
+
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
2971
2963
|
else n: {
|
|
2972
2964
|
const r = (battle.rng.next() & 3);
|
|
2973
2965
|
break :n @intCast((if (r < 2) r else battle.rng.next() & 3) + 2);
|
|
@@ -3014,10 +3006,9 @@ pub const Rolls = struct {
|
|
|
3014
3006
|
while (i < moves.len and r > 0) : (i += 1) {
|
|
3015
3007
|
if (moves[i].pp > 0) {
|
|
3016
3008
|
r -= 1;
|
|
3017
|
-
if (r == 0) break;
|
|
3009
|
+
if (r == 0) break :slot @intCast(i + 1);
|
|
3018
3010
|
}
|
|
3019
3011
|
}
|
|
3020
|
-
break :slot @intCast(i + 1);
|
|
3021
3012
|
}
|
|
3022
3013
|
}
|
|
3023
3014
|
|
|
@@ -3039,11 +3030,11 @@ test "RNG agreement" {
|
|
|
3039
3030
|
expected[i] = @intCast(i * 0x1000000);
|
|
3040
3031
|
}
|
|
3041
3032
|
|
|
3042
|
-
var spe
|
|
3043
|
-
var cfz
|
|
3044
|
-
var par
|
|
3045
|
-
var brn
|
|
3046
|
-
var eff
|
|
3033
|
+
var spe: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
|
|
3034
|
+
var cfz: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
|
|
3035
|
+
var par: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
|
|
3036
|
+
var brn: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
|
|
3037
|
+
var eff: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
|
|
3047
3038
|
|
|
3048
3039
|
for (0..expected.len) |i| {
|
|
3049
3040
|
try expectEqual(spe.range(u8, 0, 2) == 0, i < Gen12.percent(50) + 1);
|