@pkmn/engine 0.1.0-dev.93b7cb0d → 0.1.0-dev.942dbfa4
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.map +1 -1
- package/build/pkg/index.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 +2 -2
- package/src/lib/common/data.zig +1 -1
- package/src/lib/common/protocol.zig +102 -95
- package/src/lib/gen1/README.md +3 -1
- 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 +2 -2
- package/src/lib/gen1/data.zig +13 -11
- package/src/lib/gen1/helpers.zig +2 -2
- package/src/lib/gen1/mechanics.zig +27 -27
- 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 +8 -8
- package/src/lib/gen2/helpers.zig +3 -3
package/src/lib/gen1/data.zig
CHANGED
|
@@ -62,12 +62,12 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
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`.
|
|
@@ -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(u3, @
|
|
246
|
+
return ((num >> @intCast(u3, @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(u3, @
|
|
252
|
+
return @as(u8, 1) << @intCast(u3, @intFromEnum(status));
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
/// Initializes a non self-inflicted sleep status with duration `dur`.
|
|
@@ -413,7 +413,7 @@ 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 evs = @min(255, @
|
|
416
|
+
const evs = @min(255, @intFromFloat(u16, @ceil(@sqrt(@floatFromInt(f32, exp)))));
|
|
417
417
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
418
418
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
419
419
|
return @intCast(T, core *% @as(u32, level) / 100 +% factor);
|
|
@@ -465,7 +465,7 @@ test Boosts {
|
|
|
465
465
|
pub const Move = moves.Move;
|
|
466
466
|
|
|
467
467
|
test Move {
|
|
468
|
-
try expectEqual(2, @
|
|
468
|
+
try expectEqual(2, @intFromEnum(Move.KarateChop));
|
|
469
469
|
const move = Move.get(.Fissure);
|
|
470
470
|
try expectEqual(Move.Effect.OHKO, move.effect);
|
|
471
471
|
try expectEqual(@as(u8, 30), move.accuracy);
|
|
@@ -526,7 +526,7 @@ test Move {
|
|
|
526
526
|
pub const Species = species.Species;
|
|
527
527
|
|
|
528
528
|
test Species {
|
|
529
|
-
try expectEqual(2, @
|
|
529
|
+
try expectEqual(2, @intFromEnum(Species.Ivysaur));
|
|
530
530
|
try expectEqual(@as(u8, 100), Species.get(.Mew).stats.def);
|
|
531
531
|
}
|
|
532
532
|
|
|
@@ -543,9 +543,11 @@ pub const Effectiveness = enum(u8) {
|
|
|
543
543
|
Super = 20,
|
|
544
544
|
|
|
545
545
|
/// Used to determine the effectiness of a neutral damage hit.
|
|
546
|
-
pub const neutral: u16 =
|
|
546
|
+
pub const neutral: u16 =
|
|
547
|
+
@intFromEnum(Effectiveness.Neutral) * @intFromEnum(Effectiveness.Neutral);
|
|
547
548
|
/// Used to detect whether there is a mismatch in type effectivness (relevant for precedence).
|
|
548
|
-
pub const mismatch: u16 =
|
|
549
|
+
pub const mismatch: u16 =
|
|
550
|
+
@intFromEnum(Effectiveness.Resisted) + @intFromEnum(Effectiveness.Super);
|
|
549
551
|
|
|
550
552
|
comptime {
|
|
551
553
|
assert(@sizeOf(Effectiveness) == 1);
|
|
@@ -553,8 +555,8 @@ pub const Effectiveness = enum(u8) {
|
|
|
553
555
|
};
|
|
554
556
|
|
|
555
557
|
test Types {
|
|
556
|
-
try expectEqual(14, @
|
|
557
|
-
try expectEqual(20, @
|
|
558
|
+
try expectEqual(14, @intFromEnum(Type.Dragon));
|
|
559
|
+
try expectEqual(20, @intFromEnum(Effectiveness.Super));
|
|
558
560
|
|
|
559
561
|
try expect(!Type.Ghost.special());
|
|
560
562
|
try expect(Type.Dragon.special());
|
package/src/lib/gen1/helpers.zig
CHANGED
|
@@ -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 = @enumFromInt(Species, 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,7 +176,7 @@ 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(Move, 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;
|
|
@@ -126,7 +126,7 @@ fn selectMove(
|
|
|
126
126
|
const stored = side.stored();
|
|
127
127
|
|
|
128
128
|
assert(!isForced(side.active) or
|
|
129
|
-
(choice.type == .Move and choice.data == @
|
|
129
|
+
(choice.type == .Move and choice.data == @intFromBool(showdown)));
|
|
130
130
|
|
|
131
131
|
// pre-battle menu
|
|
132
132
|
if (volatiles.Recharging) {
|
|
@@ -780,9 +780,9 @@ fn setCounterable(battle: anytype, player: Player, side: *Side, move: Move.Data)
|
|
|
780
780
|
const counterable = side.last_selected_move != .Counter and move.bp > 0 and
|
|
781
781
|
(move.type == .Normal or move.type == .Fighting);
|
|
782
782
|
if (player == .P1) {
|
|
783
|
-
battle.last_moves.p1_counterable = @
|
|
783
|
+
battle.last_moves.p1_counterable = @intFromBool(counterable);
|
|
784
784
|
} else {
|
|
785
|
-
battle.last_moves.p2_counterable = @
|
|
785
|
+
battle.last_moves.p2_counterable = @intFromBool(counterable);
|
|
786
786
|
}
|
|
787
787
|
}
|
|
788
788
|
|
|
@@ -849,8 +849,8 @@ fn doMove(
|
|
|
849
849
|
// calling moveHit early covers most of that but we also need to check type immunity first
|
|
850
850
|
var miss = showdown and miss: {
|
|
851
851
|
immune = move.target != .Self and !status and !counter and
|
|
852
|
-
(@
|
|
853
|
-
@
|
|
852
|
+
(@intFromEnum(move.type.effectiveness(foe.active.types.type1)) == 0 or
|
|
853
|
+
@intFromEnum(move.type.effectiveness(foe.active.types.type2)) == 0);
|
|
854
854
|
if (immune and move.effect != .Binding) break :miss true;
|
|
855
855
|
if (move.effect == .OHKO and side.active.stats.spe < foe.active.stats.spe) {
|
|
856
856
|
battle.last_damage = 0;
|
|
@@ -941,7 +941,7 @@ fn doMove(
|
|
|
941
941
|
}
|
|
942
942
|
if (move.effect == .JumpKick) {
|
|
943
943
|
// Recoil is supposed to be damage/8 but damage will always be 0 here
|
|
944
|
-
assert(battle.last_damage == 0);
|
|
944
|
+
assert(showdown or battle.last_damage == 0);
|
|
945
945
|
battle.last_damage = 1;
|
|
946
946
|
_ = try applyDamage(battle, player, player.foe(), .None, log);
|
|
947
947
|
if (showdown and side.stored().hp == 0) residual.* = false;
|
|
@@ -1137,9 +1137,9 @@ fn adjustDamage(battle: anytype, player: Player) u16 {
|
|
|
1137
1137
|
var d = battle.last_damage;
|
|
1138
1138
|
if (side.active.types.includes(move.type)) d +%= d / 2;
|
|
1139
1139
|
|
|
1140
|
-
const neutral = @
|
|
1141
|
-
const eff1: u16 = @
|
|
1142
|
-
const eff2: u16 = @
|
|
1140
|
+
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
1141
|
+
const eff1: u16 = @intFromEnum(move.type.effectiveness(types.type1));
|
|
1142
|
+
const eff2: u16 = @intFromEnum(move.type.effectiveness(types.type2));
|
|
1143
1143
|
|
|
1144
1144
|
// Type effectiveness matchup precedence only matters with (NVE, SE)
|
|
1145
1145
|
if (!showdown and (eff1 + eff2) == Effectiveness.mismatch and
|
|
@@ -1400,7 +1400,7 @@ fn moveHit(battle: anytype, player: Player, move: Move.Data, immune: *bool, mist
|
|
|
1400
1400
|
}
|
|
1401
1401
|
|
|
1402
1402
|
if (!miss) return true;
|
|
1403
|
-
battle.last_damage = 0;
|
|
1403
|
+
if (!showdown or !foe.active.volatiles.Invulnerable) battle.last_damage = 0;
|
|
1404
1404
|
side.active.volatiles.Binding = false;
|
|
1405
1405
|
return false;
|
|
1406
1406
|
}
|
|
@@ -1954,7 +1954,7 @@ pub const Effects = struct {
|
|
|
1954
1954
|
// Pokémon Showdown clears P1 then P2 instead of status -> side -> foe
|
|
1955
1955
|
if (showdown) {
|
|
1956
1956
|
for (&battle.sides, 0..) |*s, i| {
|
|
1957
|
-
const p = @
|
|
1957
|
+
const p = @enumFromInt(Player, i);
|
|
1958
1958
|
// Pokémon Showdown incorrectly does not prevent sleep/freeze from moving
|
|
1959
1959
|
if (p != player and Status.any(s.stored().status)) {
|
|
1960
1960
|
try log.curestatus(foe_ident, foe_stored.status, .Silent);
|
|
@@ -2449,12 +2449,11 @@ pub const Effects = struct {
|
|
|
2449
2449
|
fn unboost(battle: anytype, player: Player, move: Move.Data, log: anytype) !void {
|
|
2450
2450
|
var foe = battle.foe(player);
|
|
2451
2451
|
const foe_ident = battle.active(player.foe());
|
|
2452
|
+
const secondary = move.effect.isStatDownChance();
|
|
2452
2453
|
|
|
2453
|
-
if (foe.active.volatiles.Substitute)
|
|
2454
|
-
return if (!move.effect.isStatDownChance()) log.fail(foe_ident, .None);
|
|
2455
|
-
}
|
|
2454
|
+
if (foe.active.volatiles.Substitute) return if (!secondary) log.fail(foe_ident, .None);
|
|
2456
2455
|
|
|
2457
|
-
if (
|
|
2456
|
+
if (secondary) {
|
|
2458
2457
|
if (!Rolls.unboost(battle) or foe.active.volatiles.Invulnerable) return;
|
|
2459
2458
|
} else if (!showdown and !try checkHit(battle, player, move, log)) {
|
|
2460
2459
|
return; // checkHit already checks for Invulnerable
|
|
@@ -2462,11 +2461,12 @@ pub const Effects = struct {
|
|
|
2462
2461
|
|
|
2463
2462
|
var stats = &foe.active.stats;
|
|
2464
2463
|
var boosts = &foe.active.boosts;
|
|
2464
|
+
const fail = showdown or !secondary;
|
|
2465
2465
|
|
|
2466
2466
|
switch (move.effect) {
|
|
2467
2467
|
.AttackDown1, .AttackDownChance => {
|
|
2468
2468
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2469
|
-
if (boosts.atk == -6) return try log.fail(foe_ident, .None);
|
|
2469
|
+
if (boosts.atk == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2470
2470
|
boosts.atk = @intCast(i4, @max(-6, @as(i8, boosts.atk) - 1));
|
|
2471
2471
|
if (stats.atk == 1) {
|
|
2472
2472
|
boosts.atk += 1;
|
|
@@ -2483,7 +2483,7 @@ pub const Effects = struct {
|
|
|
2483
2483
|
},
|
|
2484
2484
|
.DefenseDown1, .DefenseDown2, .DefenseDownChance => {
|
|
2485
2485
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2486
|
-
if (boosts.def == -6) return try log.fail(foe_ident, .None);
|
|
2486
|
+
if (boosts.def == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2487
2487
|
const n: u2 = if (move.effect == .DefenseDown2) 2 else 1;
|
|
2488
2488
|
boosts.def = @intCast(i4, @max(-6, @as(i8, boosts.def) - n));
|
|
2489
2489
|
if (stats.def == 1) {
|
|
@@ -2501,7 +2501,7 @@ pub const Effects = struct {
|
|
|
2501
2501
|
},
|
|
2502
2502
|
.SpeedDown1, .SpeedDownChance => {
|
|
2503
2503
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2504
|
-
if (boosts.spe == -6) return try log.fail(foe_ident, .None);
|
|
2504
|
+
if (boosts.spe == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2505
2505
|
boosts.spe = @intCast(i4, @max(-6, @as(i8, boosts.spe) - 1));
|
|
2506
2506
|
if (stats.spe == 1) {
|
|
2507
2507
|
boosts.spe += 1;
|
|
@@ -2519,7 +2519,7 @@ pub const Effects = struct {
|
|
|
2519
2519
|
},
|
|
2520
2520
|
.SpecialDownChance => {
|
|
2521
2521
|
assert(boosts.spc >= -6 and boosts.spc <= 6);
|
|
2522
|
-
if (boosts.spc == -6) return try log.fail(foe_ident, .None);
|
|
2522
|
+
if (boosts.spc == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2523
2523
|
boosts.spc = @intCast(i4, @max(-6, @as(i8, boosts.spc) - 1));
|
|
2524
2524
|
if (stats.spc == 1) {
|
|
2525
2525
|
boosts.spc += 1;
|
|
@@ -2539,7 +2539,7 @@ pub const Effects = struct {
|
|
|
2539
2539
|
},
|
|
2540
2540
|
.AccuracyDown1 => {
|
|
2541
2541
|
assert(boosts.accuracy >= -6 and boosts.accuracy <= 6);
|
|
2542
|
-
if (boosts.accuracy == -6) return try log.fail(foe_ident, .None);
|
|
2542
|
+
if (boosts.accuracy == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2543
2543
|
boosts.accuracy = @intCast(i4, @max(-6, @as(i8, boosts.accuracy) - 1));
|
|
2544
2544
|
try log.boost(foe_ident, .Accuracy, -1);
|
|
2545
2545
|
},
|
|
@@ -2720,15 +2720,15 @@ pub const Rolls = struct {
|
|
|
2720
2720
|
|
|
2721
2721
|
inline fn metronome(battle: anytype) Move {
|
|
2722
2722
|
if (showdown) {
|
|
2723
|
-
const r = battle.rng.range(u8, 0, @
|
|
2724
|
-
const mod = @as(u2, (if (r < @
|
|
2725
|
-
return @
|
|
2723
|
+
const r = battle.rng.range(u8, 0, @intFromEnum(Move.Struggle) - 2);
|
|
2724
|
+
const mod = @as(u2, (if (r < @intFromEnum(Move.Metronome) - 1) 1 else 2));
|
|
2725
|
+
return @enumFromInt(Move, r + mod);
|
|
2726
2726
|
}
|
|
2727
2727
|
while (true) {
|
|
2728
2728
|
const r = battle.rng.next();
|
|
2729
|
-
if (r == 0 or r == @
|
|
2730
|
-
if (r >= @
|
|
2731
|
-
return @
|
|
2729
|
+
if (r == 0 or r == @intFromEnum(Move.Metronome)) continue;
|
|
2730
|
+
if (r >= @intFromEnum(Move.Struggle)) continue;
|
|
2731
|
+
return @enumFromInt(Move, r);
|
|
2732
2732
|
}
|
|
2733
2733
|
}
|
|
2734
2734
|
|
|
@@ -2813,7 +2813,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
2813
2813
|
// strictly correct as the player would not have been presented the option to move or
|
|
2814
2814
|
// switch at all.
|
|
2815
2815
|
if (isForced(active)) {
|
|
2816
|
-
out[n] = .{ .type = .Move, .data = @
|
|
2816
|
+
out[n] = .{ .type = .Move, .data = @intFromBool(showdown) };
|
|
2817
2817
|
n += 1;
|
|
2818
2818
|
return n;
|
|
2819
2819
|
}
|
|
@@ -219,18 +219,18 @@ pub const Item = enum(u8) {
|
|
|
219
219
|
pub inline fn boost(item: Item) ?Type {
|
|
220
220
|
assert(item != .None);
|
|
221
221
|
if (item == .PolkadotBow) return .Normal;
|
|
222
|
-
return if (@
|
|
222
|
+
return if (@intFromEnum(item) <= 18) @enumFromInt(Type, @intFromEnum(item) - 1) else null;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
/// Whether or not this item is a Berry.
|
|
226
226
|
pub inline fn berry(item: Item) bool {
|
|
227
227
|
assert(item != .None);
|
|
228
|
-
return @
|
|
228
|
+
return @intFromEnum(item) > 30 and @intFromEnum(item) <= 40;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
/// Whether or not this item is Mail.
|
|
232
232
|
pub inline fn mail(item: Item) bool {
|
|
233
233
|
assert(item != .None);
|
|
234
|
-
return @
|
|
234
|
+
return @intFromEnum(item) > 60 and @intFromEnum(item) <= 70;
|
|
235
235
|
}
|
|
236
236
|
};
|
|
@@ -86,17 +86,17 @@ pub const Type = enum(u8) {
|
|
|
86
86
|
|
|
87
87
|
/// Whether or not this type is considered to be special as opposed to physical.
|
|
88
88
|
pub inline fn special(self: Type) bool {
|
|
89
|
-
return @
|
|
89
|
+
return @intFromEnum(self) >= @intFromEnum(Type.Fire);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/// The `Effectiveness` of type `t2` vs. type `t1`.
|
|
93
93
|
pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
|
|
94
|
-
return CHART[@
|
|
94
|
+
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/// The precedence order of type type.
|
|
98
98
|
pub inline fn precedence(self: Type) u8 {
|
|
99
|
-
return PRECEDENCE[@
|
|
99
|
+
return PRECEDENCE[@intFromEnum(self)];
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
102
|
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -50,11 +50,11 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
50
50
|
field: Field = .{},
|
|
51
51
|
|
|
52
52
|
pub inline fn side(self: *Self, player: Player) *Side {
|
|
53
|
-
return &self.sides[@
|
|
53
|
+
return &self.sides[@intFromEnum(player)];
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
pub inline fn foe(self: *Self, player: Player) *Side {
|
|
57
|
-
return &self.sides[@
|
|
57
|
+
return &self.sides[@intFromEnum(player.foe())];
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
pub inline fn active(self: *Self, player: Player) ID {
|
|
@@ -208,8 +208,8 @@ const DVs = packed struct {
|
|
|
208
208
|
const p = @intCast(u6, 1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
209
209
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
210
210
|
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
|
|
211
|
-
if (t >= @
|
|
212
|
-
return DVs{ .gender = g, .pow = p, .type = @
|
|
211
|
+
if (t >= @intFromEnum(Type.@"???")) t = t + 1;
|
|
212
|
+
return DVs{ .gender = g, .pow = p, .type = @enumFromInt(Type, t) };
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
comptime {
|
|
@@ -327,7 +327,7 @@ pub fn Stats(comptime T: type) type {
|
|
|
327
327
|
|
|
328
328
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
329
329
|
assert(level > 0 and level <= 100);
|
|
330
|
-
const evs = @min(255, @
|
|
330
|
+
const evs = @min(255, @intFromFloat(u16, @ceil(@sqrt(@floatFromInt(f32, exp)))));
|
|
331
331
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
332
332
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
333
333
|
return @intCast(T, core *% @as(u32, level) / 100 +% factor);
|
|
@@ -390,7 +390,7 @@ test Item {
|
|
|
390
390
|
pub const Move = moves.Move;
|
|
391
391
|
|
|
392
392
|
test Move {
|
|
393
|
-
try expectEqual(251, @
|
|
393
|
+
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
394
394
|
const move = Move.get(.DynamicPunch);
|
|
395
395
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
396
396
|
try expectEqual(@as(u8, 50), move.accuracy);
|
|
@@ -400,7 +400,7 @@ test Move {
|
|
|
400
400
|
pub const Species = species.Species;
|
|
401
401
|
|
|
402
402
|
test Species {
|
|
403
|
-
try expectEqual(152, @
|
|
403
|
+
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
404
404
|
try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
|
|
405
405
|
}
|
|
406
406
|
|
|
@@ -409,7 +409,7 @@ pub const Types = types.Types;
|
|
|
409
409
|
pub const Effectiveness = gen1.Effectiveness;
|
|
410
410
|
|
|
411
411
|
test Types {
|
|
412
|
-
try expectEqual(13, @
|
|
412
|
+
try expectEqual(13, @intFromEnum(Type.Electric));
|
|
413
413
|
|
|
414
414
|
try expect(!Type.Steel.special());
|
|
415
415
|
try expect(Type.Dark.special());
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -158,11 +158,11 @@ pub const Pokemon = struct {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
|
|
161
|
-
const s = @
|
|
161
|
+
const s = @enumFromInt(Species, rand.range(u8, 1, Species.size + 1));
|
|
162
162
|
const species = Species.get(s);
|
|
163
163
|
const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
|
|
164
164
|
// Pokémon Showdown does not support most items without in-battle held item effects
|
|
165
|
-
const max_item = @
|
|
165
|
+
const max_item = @intFromEnum(if (showdown) Item.FlowerMail else Item.TM50);
|
|
166
166
|
const item = if (rand.chance(u8, 1, 10)) 0 else rand.range(u8, 1, max_item + 1);
|
|
167
167
|
|
|
168
168
|
var stats: Stats(u16) = .{};
|
|
@@ -184,7 +184,7 @@ pub const Pokemon = struct {
|
|
|
184
184
|
for (0..n) |i| {
|
|
185
185
|
var m: Move = .None;
|
|
186
186
|
sample: while (true) {
|
|
187
|
-
m = @
|
|
187
|
+
m = @enumFromInt(Move, rand.range(u8, 1, 164 + 1));
|
|
188
188
|
for (0..i) |j| if (ms[j].id == m) continue :sample;
|
|
189
189
|
break;
|
|
190
190
|
}
|