@pkmn/engine 0.1.0-dev.62399ed5 → 0.1.0-dev.6be9a775
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 +4 -2
- package/build/pkg/index.d.ts +1 -1
- package/build/pkg/index.js.map +1 -1
- package/build/test/display.js +6 -1
- package/build/test/display.js.map +1 -1
- package/build.zig +199 -66
- package/package.json +4 -1
- package/src/bin/install-pkmn-engine +7 -5
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/protocol.zig +41 -36
- package/src/lib/common/rational.zig +46 -46
- package/src/lib/common/rng.zig +4 -4
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +329 -119
- package/src/lib/gen1/chance.zig +332 -224
- package/src/lib/gen1/data.zig +28 -27
- package/src/lib/gen1/helpers.zig +13 -534
- package/src/lib/gen1/mechanics.zig +178 -109
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +91 -501
- package/src/lib/gen2/data.zig +15 -14
- package/src/lib/gen2/helpers.zig +4 -2
- package/src/lib/gen2/mechanics.zig +47 -71
- package/src/lib/pkmn.zig +7 -5
package/src/lib/gen2/data.zig
CHANGED
|
@@ -45,6 +45,7 @@ const Player = data.Player;
|
|
|
45
45
|
const Result = data.Result;
|
|
46
46
|
|
|
47
47
|
const PointerType = util.PointerType;
|
|
48
|
+
const isPointerTo = util.isPointerTo;
|
|
48
49
|
|
|
49
50
|
const showdown = pkmn.options.showdown;
|
|
50
51
|
|
|
@@ -63,12 +64,12 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
63
64
|
_: [padding]u8 = .{0} ** padding,
|
|
64
65
|
|
|
65
66
|
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
66
|
-
assert(
|
|
67
|
+
assert(isPointerTo(self, Self));
|
|
67
68
|
return &self.sides[@intFromEnum(player)];
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
71
|
-
assert(
|
|
72
|
+
assert(isPointerTo(self, Self));
|
|
72
73
|
return &self.sides[@intFromEnum(player.foe())];
|
|
73
74
|
}
|
|
74
75
|
|
|
@@ -134,7 +135,7 @@ pub const Side = extern struct {
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
137
|
-
assert(
|
|
138
|
+
assert(isPointerTo(self, Side));
|
|
138
139
|
assert(slot > 0 and slot <= 6);
|
|
139
140
|
const id = self.pokemon[slot - 1].position;
|
|
140
141
|
assert(id > 0 and id <= 6);
|
|
@@ -142,7 +143,7 @@ pub const Side = extern struct {
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
145
|
-
assert(
|
|
146
|
+
assert(isPointerTo(self, Side));
|
|
146
147
|
return self.get(1);
|
|
147
148
|
}
|
|
148
149
|
|
|
@@ -176,7 +177,7 @@ pub const ActivePokemon = extern struct {
|
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
179
|
-
assert(
|
|
180
|
+
assert(isPointerTo(self, Pokemon));
|
|
180
181
|
assert(mslot > 0 and mslot <= 4);
|
|
181
182
|
assert(self.moves[mslot - 1].id != .None);
|
|
182
183
|
return &self.moves[mslot - 1];
|
|
@@ -201,7 +202,7 @@ pub const Pokemon = extern struct {
|
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
204
|
-
assert(
|
|
205
|
+
assert(isPointerTo(self, Pokemon));
|
|
205
206
|
assert(mslot > 0 and mslot <= 4);
|
|
206
207
|
assert(self.moves[mslot - 1].id != .None);
|
|
207
208
|
return &self.moves[mslot - 1];
|
|
@@ -248,12 +249,12 @@ test DVs {
|
|
|
248
249
|
var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
|
|
249
250
|
try expectEqual(Gender.Unknown, dvs.gender);
|
|
250
251
|
try expectEqual(Type.Ice, dvs.type);
|
|
251
|
-
try expectEqual(70, dvs.power());
|
|
252
|
+
try expectEqual(@as(u8, 70), dvs.power());
|
|
252
253
|
|
|
253
254
|
dvs = DVs.from(.Pikachu, .{ .spc = 14 });
|
|
254
255
|
try expectEqual(Gender.Male, dvs.gender);
|
|
255
256
|
try expectEqual(Type.Dark, dvs.type);
|
|
256
|
-
try expectEqual(69, dvs.power());
|
|
257
|
+
try expectEqual(@as(u8, 69), dvs.power());
|
|
257
258
|
|
|
258
259
|
dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
|
|
259
260
|
try expectEqual(Gender.Female, dvs.gender);
|
|
@@ -380,9 +381,9 @@ test Stats {
|
|
|
380
381
|
try expectEqual(0, stats.def);
|
|
381
382
|
|
|
382
383
|
var base = Species.get(.Gyarados).stats;
|
|
383
|
-
try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
384
|
+
try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
384
385
|
base = Species.get(.Pidgeot).stats;
|
|
385
|
-
try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
386
|
+
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
386
387
|
}
|
|
387
388
|
|
|
388
389
|
pub const Boosts = packed struct(u32) {
|
|
@@ -428,8 +429,8 @@ test Move {
|
|
|
428
429
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
429
430
|
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
430
431
|
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
431
|
-
try expectEqual(5, Move.pp(.DynamicPunch));
|
|
432
|
-
try expectEqual(-1, Move.get(.Counter).priority);
|
|
432
|
+
try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
|
|
433
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
433
434
|
try expect(!move.effect.isHighCritical());
|
|
434
435
|
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
435
436
|
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
@@ -439,7 +440,7 @@ pub const Species = species.Species;
|
|
|
439
440
|
|
|
440
441
|
test Species {
|
|
441
442
|
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
442
|
-
try expectEqual(100, Species.get(.Celebi).stats.spd);
|
|
443
|
+
try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
|
|
443
444
|
}
|
|
444
445
|
|
|
445
446
|
pub const Type = types.Type;
|
|
@@ -452,7 +453,7 @@ test Types {
|
|
|
452
453
|
try expect(!Type.Steel.special());
|
|
453
454
|
try expect(Type.Dark.special());
|
|
454
455
|
|
|
455
|
-
try expectEqual(3, Type.Water.precedence());
|
|
456
|
+
try expectEqual(@as(u8, 3), Type.Water.precedence());
|
|
456
457
|
try expect(Type.Bug.precedence() > Type.Poison.precedence());
|
|
457
458
|
|
|
458
459
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -27,6 +27,8 @@ const Species = data.Species;
|
|
|
27
27
|
const Stats = data.Stats;
|
|
28
28
|
const Status = data.Status;
|
|
29
29
|
|
|
30
|
+
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
31
|
+
|
|
30
32
|
pub const Options = struct {
|
|
31
33
|
cleric: bool = showdown,
|
|
32
34
|
};
|
|
@@ -151,7 +153,7 @@ pub const Pokemon = struct {
|
|
|
151
153
|
var pokemon = data.Pokemon{};
|
|
152
154
|
pokemon.species = p.species;
|
|
153
155
|
const species = Species.get(p.species);
|
|
154
|
-
inline for (@typeInfo(@TypeOf(pokemon.stats))
|
|
156
|
+
inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
|
|
155
157
|
const hp = comptime std.mem.eql(u8, field.name, "hp");
|
|
156
158
|
const spc =
|
|
157
159
|
comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
@@ -192,7 +194,7 @@ pub const Pokemon = struct {
|
|
|
192
194
|
|
|
193
195
|
var stats: Stats(u16) = .{};
|
|
194
196
|
const dvs = gen1.DVs.random(rand);
|
|
195
|
-
inline for (@typeInfo(@TypeOf(stats))
|
|
197
|
+
inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
|
|
196
198
|
const hp = std.mem.eql(u8, field.name, "hp");
|
|
197
199
|
const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
198
200
|
@field(stats, field.name) = Stats(u16).calc(
|
|
@@ -106,6 +106,7 @@ pub const State = struct {
|
|
|
106
106
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
107
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
108
|
move: Move, // wCurPlayerMove
|
|
109
|
+
hit: u3 = 0,
|
|
109
110
|
from: Move = .None,
|
|
110
111
|
mslot: u4, // wCurMoveNum
|
|
111
112
|
first: bool, // wEnemyGoesFirst
|
|
@@ -256,7 +257,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
256
257
|
} else {
|
|
257
258
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
259
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
260
|
|
|
261
261
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
262
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
@@ -442,18 +442,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
442
442
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
443
443
|
const before = stored.status;
|
|
444
444
|
// Even if the EXT bit is set this will still correctly modify the sleep duration
|
|
445
|
-
// if (options.calc.modify(player, .sleep)) |extend| {
|
|
446
|
-
// if (!extend) stored.status = 0;
|
|
447
|
-
// } else {
|
|
448
445
|
stored.status -= 1;
|
|
449
|
-
// }
|
|
450
|
-
|
|
451
|
-
const duration = Status.duration(stored.status);
|
|
452
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
453
|
-
try options.chance.sleep(player, duration);
|
|
454
|
-
}
|
|
455
446
|
|
|
456
|
-
if (duration == 0) {
|
|
447
|
+
if (Status.duration(stored.status) == 0) {
|
|
457
448
|
try log.curestatus(.{ ident, before, .Message });
|
|
458
449
|
stored.status = 0; // clears EXT if present
|
|
459
450
|
resetCant(volatiles);
|
|
@@ -483,12 +474,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
483
474
|
}
|
|
484
475
|
|
|
485
476
|
if (volatiles.disable.duration > 0) {
|
|
486
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
487
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
488
|
-
// } else {
|
|
489
477
|
volatiles.disable.duration -= 1;
|
|
490
|
-
// }
|
|
491
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
492
478
|
|
|
493
479
|
if (volatiles.disable.duration == 0) {
|
|
494
480
|
volatiles.disable.move = 0;
|
|
@@ -498,13 +484,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
498
484
|
|
|
499
485
|
if (volatiles.Confusion) {
|
|
500
486
|
assert(volatiles.confusion > 0);
|
|
501
|
-
|
|
502
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
503
|
-
// if (!extend) volatiles.confusion = 0;
|
|
504
|
-
// } else {
|
|
505
487
|
volatiles.confusion -= 1;
|
|
506
|
-
// }
|
|
507
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
508
488
|
|
|
509
489
|
if (volatiles.confusion == 0) {
|
|
510
490
|
volatiles.Confusion = false;
|
|
@@ -665,7 +645,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
665
645
|
}
|
|
666
646
|
assert(stage <= 4);
|
|
667
647
|
|
|
668
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
648
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
669
649
|
}
|
|
670
650
|
|
|
671
651
|
pub fn calcDamage(
|
|
@@ -733,7 +713,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
733
713
|
|
|
734
714
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
735
715
|
if (state.damage <= 1) return;
|
|
736
|
-
const random = try Rolls.damage(battle, player, options);
|
|
716
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
737
717
|
options.calc.base(player.foe(), state.damage);
|
|
738
718
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
739
719
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -1341,13 +1321,7 @@ pub const Effects = struct {
|
|
|
1341
1321
|
|
|
1342
1322
|
if (!volatiles.Bide) return;
|
|
1343
1323
|
|
|
1344
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1345
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1346
|
-
// } else {
|
|
1347
1324
|
volatiles.attacks -= 1;
|
|
1348
|
-
// }
|
|
1349
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1350
|
-
|
|
1351
1325
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1352
1326
|
|
|
1353
1327
|
volatiles.Bide = false;
|
|
@@ -2876,7 +2850,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2876
2850
|
|
|
2877
2851
|
pub const Rolls = struct {
|
|
2878
2852
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2879
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2853
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2880
2854
|
player == .P1
|
|
2881
2855
|
else if (showdown)
|
|
2882
2856
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2888,7 +2862,7 @@ pub const Rolls = struct {
|
|
|
2888
2862
|
}
|
|
2889
2863
|
|
|
2890
2864
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2891
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2865
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2892
2866
|
val == .true
|
|
2893
2867
|
else if (showdown)
|
|
2894
2868
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2900,7 +2874,7 @@ pub const Rolls = struct {
|
|
|
2900
2874
|
}
|
|
2901
2875
|
|
|
2902
2876
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2903
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2877
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2904
2878
|
val == .true
|
|
2905
2879
|
else if (showdown)
|
|
2906
2880
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2911,20 +2885,22 @@ pub const Rolls = struct {
|
|
|
2911
2885
|
return ok;
|
|
2912
2886
|
}
|
|
2913
2887
|
|
|
2914
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2915
|
-
|
|
2888
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2889
|
+
_ = .{ player, options };
|
|
2890
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2916
2891
|
val == .true
|
|
2917
2892
|
else if (showdown)
|
|
2918
2893
|
battle.rng.chance(u8, rate, 256)
|
|
2919
2894
|
else
|
|
2920
2895
|
battle.rng.next() < rate;
|
|
2921
2896
|
|
|
2922
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2897
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2923
2898
|
return crit;
|
|
2924
2899
|
}
|
|
2925
2900
|
|
|
2926
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2927
|
-
|
|
2901
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2902
|
+
_ = .{ player, options };
|
|
2903
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2928
2904
|
val
|
|
2929
2905
|
else roll: {
|
|
2930
2906
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2935,12 +2911,12 @@ pub const Rolls = struct {
|
|
|
2935
2911
|
};
|
|
2936
2912
|
|
|
2937
2913
|
assert(roll >= 217 and roll <= 255);
|
|
2938
|
-
try options.chance.damage(player, roll);
|
|
2914
|
+
try options.chance.damage(player, i, roll);
|
|
2939
2915
|
return roll;
|
|
2940
2916
|
}
|
|
2941
2917
|
|
|
2942
2918
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2943
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2919
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2944
2920
|
val == .true
|
|
2945
2921
|
else if (showdown)
|
|
2946
2922
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2952,7 +2928,7 @@ pub const Rolls = struct {
|
|
|
2952
2928
|
}
|
|
2953
2929
|
|
|
2954
2930
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2955
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2931
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2956
2932
|
val == .true
|
|
2957
2933
|
else if (showdown)
|
|
2958
2934
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2964,7 +2940,7 @@ pub const Rolls = struct {
|
|
|
2964
2940
|
}
|
|
2965
2941
|
|
|
2966
2942
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2967
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2943
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2968
2944
|
val == .true
|
|
2969
2945
|
else if (showdown)
|
|
2970
2946
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2976,7 +2952,7 @@ pub const Rolls = struct {
|
|
|
2976
2952
|
}
|
|
2977
2953
|
|
|
2978
2954
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2979
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2955
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2980
2956
|
val == .true
|
|
2981
2957
|
else if (showdown)
|
|
2982
2958
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2989,7 +2965,7 @@ pub const Rolls = struct {
|
|
|
2989
2965
|
|
|
2990
2966
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2991
2967
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2992
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2968
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2993
2969
|
val == .true
|
|
2994
2970
|
else if (showdown)
|
|
2995
2971
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -3001,7 +2977,7 @@ pub const Rolls = struct {
|
|
|
3001
2977
|
}
|
|
3002
2978
|
|
|
3003
2979
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
3004
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2980
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
3005
2981
|
val == .true
|
|
3006
2982
|
else if (showdown)
|
|
3007
2983
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -3013,7 +2989,7 @@ pub const Rolls = struct {
|
|
|
3013
2989
|
}
|
|
3014
2990
|
|
|
3015
2991
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
3016
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2992
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
3017
2993
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3018
2994
|
else if (showdown)
|
|
3019
2995
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -3029,7 +3005,7 @@ pub const Rolls = struct {
|
|
|
3029
3005
|
}
|
|
3030
3006
|
|
|
3031
3007
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
3032
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3008
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
3033
3009
|
(val - 1) * 40
|
|
3034
3010
|
else if (showdown) power: {
|
|
3035
3011
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -3051,7 +3027,7 @@ pub const Rolls = struct {
|
|
|
3051
3027
|
}
|
|
3052
3028
|
|
|
3053
3029
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
3054
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3030
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
3055
3031
|
val + 3
|
|
3056
3032
|
else if (showdown) num: {
|
|
3057
3033
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -3079,7 +3055,7 @@ pub const Rolls = struct {
|
|
|
3079
3055
|
}
|
|
3080
3056
|
|
|
3081
3057
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
3082
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3058
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
3083
3059
|
val
|
|
3084
3060
|
else if (showdown)
|
|
3085
3061
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3097,7 +3073,7 @@ pub const Rolls = struct {
|
|
|
3097
3073
|
|
|
3098
3074
|
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3099
3075
|
assert(!showdown);
|
|
3100
|
-
const pp: u3 = if (options.calc.overridden(player, .spite)) |val|
|
|
3076
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3101
3077
|
val + 1
|
|
3102
3078
|
else
|
|
3103
3079
|
@intCast((battle.rng.next() & 3) + 2);
|
|
@@ -3114,7 +3090,7 @@ pub const Rolls = struct {
|
|
|
3114
3090
|
var i: u8 = 0;
|
|
3115
3091
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3116
3092
|
assert(mtype != .@"???");
|
|
3117
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3093
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3118
3094
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3119
3095
|
else if (showdown) ty: {
|
|
3120
3096
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3148,7 +3124,7 @@ pub const Rolls = struct {
|
|
|
3148
3124
|
assert(count <= 8);
|
|
3149
3125
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3150
3126
|
|
|
3151
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3127
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3152
3128
|
val == .true
|
|
3153
3129
|
else if (showdown)
|
|
3154
3130
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3170,7 +3146,7 @@ pub const Rolls = struct {
|
|
|
3170
3146
|
const moves = active.moves;
|
|
3171
3147
|
|
|
3172
3148
|
// TODO: consider throwing error instead of rerolling?
|
|
3173
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3149
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3174
3150
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3175
3151
|
else
|
|
3176
3152
|
null;
|
|
@@ -3207,7 +3183,7 @@ pub const Rolls = struct {
|
|
|
3207
3183
|
const moves = active.moves;
|
|
3208
3184
|
|
|
3209
3185
|
// TODO: consider throwing error instead of rerolling?
|
|
3210
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3186
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3211
3187
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3212
3188
|
else
|
|
3213
3189
|
null;
|
|
@@ -3251,7 +3227,7 @@ pub const Rolls = struct {
|
|
|
3251
3227
|
assert(n > 0 and n <= 6);
|
|
3252
3228
|
|
|
3253
3229
|
// TODO: consider throwing error instead of rerolling?
|
|
3254
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3230
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3255
3231
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3256
3232
|
break :val null;
|
|
3257
3233
|
} else null;
|
|
@@ -3277,7 +3253,7 @@ pub const Rolls = struct {
|
|
|
3277
3253
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3278
3254
|
|
|
3279
3255
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3280
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3256
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3281
3257
|
@intCast(val)
|
|
3282
3258
|
else if (showdown)
|
|
3283
3259
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3292,7 +3268,7 @@ pub const Rolls = struct {
|
|
|
3292
3268
|
}
|
|
3293
3269
|
|
|
3294
3270
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3295
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3271
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3296
3272
|
val
|
|
3297
3273
|
else if (showdown)
|
|
3298
3274
|
battle.rng.range(u8, 1, max)
|
|
@@ -3318,7 +3294,7 @@ pub const Rolls = struct {
|
|
|
3318
3294
|
}
|
|
3319
3295
|
|
|
3320
3296
|
// TODO: consider throwing error instead of rerolling?
|
|
3321
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3297
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3322
3298
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3323
3299
|
break :val val;
|
|
3324
3300
|
} else null;
|
|
@@ -3350,7 +3326,7 @@ pub const Rolls = struct {
|
|
|
3350
3326
|
}
|
|
3351
3327
|
|
|
3352
3328
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3353
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3329
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3354
3330
|
@intCast(val)
|
|
3355
3331
|
else if (showdown)
|
|
3356
3332
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3362,12 +3338,12 @@ pub const Rolls = struct {
|
|
|
3362
3338
|
};
|
|
3363
3339
|
|
|
3364
3340
|
assert(duration >= 1 and duration <= 6);
|
|
3365
|
-
options.chance.duration(
|
|
3341
|
+
options.chance.duration(player, duration);
|
|
3366
3342
|
return duration + 1;
|
|
3367
3343
|
}
|
|
3368
3344
|
|
|
3369
3345
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3370
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3346
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3371
3347
|
@intCast(val)
|
|
3372
3348
|
else if (showdown)
|
|
3373
3349
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3376,16 +3352,16 @@ pub const Rolls = struct {
|
|
|
3376
3352
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3377
3353
|
|
|
3378
3354
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3379
|
-
options.chance.duration(
|
|
3355
|
+
options.chance.duration(player, duration);
|
|
3380
3356
|
return duration;
|
|
3381
3357
|
}
|
|
3382
3358
|
|
|
3383
3359
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3384
|
-
const duration:
|
|
3360
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3385
3361
|
@intCast(val)
|
|
3386
3362
|
else if (showdown)
|
|
3387
3363
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3388
|
-
battle.rng.range(
|
|
3364
|
+
battle.rng.range(u3, 2, 6)
|
|
3389
3365
|
else duration: {
|
|
3390
3366
|
while (true) {
|
|
3391
3367
|
const r = battle.rng.next() & 7;
|
|
@@ -3394,12 +3370,12 @@ pub const Rolls = struct {
|
|
|
3394
3370
|
};
|
|
3395
3371
|
|
|
3396
3372
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3397
|
-
options.chance.duration(
|
|
3373
|
+
options.chance.duration(player, duration);
|
|
3398
3374
|
return duration;
|
|
3399
3375
|
}
|
|
3400
3376
|
|
|
3401
3377
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3402
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3378
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3403
3379
|
@intCast(val)
|
|
3404
3380
|
else if (showdown)
|
|
3405
3381
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3407,12 +3383,12 @@ pub const Rolls = struct {
|
|
|
3407
3383
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3408
3384
|
|
|
3409
3385
|
assert(duration >= 2 and duration <= 3);
|
|
3410
|
-
options.chance.duration(
|
|
3386
|
+
options.chance.duration(player, duration);
|
|
3411
3387
|
return duration;
|
|
3412
3388
|
}
|
|
3413
3389
|
|
|
3414
3390
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3415
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3391
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3416
3392
|
@intCast(val)
|
|
3417
3393
|
else if (showdown)
|
|
3418
3394
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3421,13 +3397,13 @@ pub const Rolls = struct {
|
|
|
3421
3397
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3422
3398
|
|
|
3423
3399
|
assert(duration >= 2 and duration <= 5);
|
|
3424
|
-
options.chance.duration(
|
|
3400
|
+
options.chance.duration(player, duration);
|
|
3425
3401
|
return duration + 1;
|
|
3426
3402
|
}
|
|
3427
3403
|
|
|
3428
3404
|
// TODO: consider sharing implementation with bindingDuration
|
|
3429
3405
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3430
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3406
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3431
3407
|
@intCast(val)
|
|
3432
3408
|
else if (showdown)
|
|
3433
3409
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3435,7 +3411,7 @@ pub const Rolls = struct {
|
|
|
3435
3411
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3436
3412
|
|
|
3437
3413
|
assert(duration >= 2 and duration <= 5);
|
|
3438
|
-
options.chance.duration(
|
|
3414
|
+
options.chance.duration(player, duration);
|
|
3439
3415
|
return duration + 1;
|
|
3440
3416
|
}
|
|
3441
3417
|
};
|
package/src/lib/pkmn.zig
CHANGED
|
@@ -22,6 +22,11 @@ pub const Choice = @import("common/data.zig").Choice;
|
|
|
22
22
|
/// considered terminal.
|
|
23
23
|
pub const Result = @import("common/data.zig").Result;
|
|
24
24
|
|
|
25
|
+
// TODO
|
|
26
|
+
pub const Array = @import("common/array.zig").Array;
|
|
27
|
+
// TODO
|
|
28
|
+
pub const Optional = @import("common/optional.zig").Optional;
|
|
29
|
+
|
|
25
30
|
/// Pokémon Showdown's RNG (backed by a Generation V & VI RNG).
|
|
26
31
|
pub const PSRNG = @import("common/rng.zig").PSRNG;
|
|
27
32
|
|
|
@@ -81,8 +86,6 @@ pub const gen1 = struct {
|
|
|
81
86
|
pub const Calc = @import("gen1/calc.zig").Calc;
|
|
82
87
|
/// Namespace for types associated with supported Generation I Pokémon damage calc features.
|
|
83
88
|
pub const calc = if (options.internal) @import("gen1/calc.zig") else struct {
|
|
84
|
-
/// TODO
|
|
85
|
-
pub const Overrides = @import("gen1/calc.zig").Overrides;
|
|
86
89
|
/// Information relevant to damage calculation that occured during a Generation I
|
|
87
90
|
/// battle `update`.
|
|
88
91
|
pub const Summaries = @import("gen1/calc.zig").Summaries;
|
|
@@ -111,7 +114,6 @@ pub const gen2 = struct {
|
|
|
111
114
|
};
|
|
112
115
|
pub const Calc = @import("gen2/calc.zig").Calc;
|
|
113
116
|
pub const calc = if (options.internal) @import("gen2/calc.zig") else struct {
|
|
114
|
-
pub const Overrides = @import("gen2/calc.zig").Overrides;
|
|
115
117
|
pub const Summaries = @import("gen2/calc.zig").Summaries;
|
|
116
118
|
pub const Summary = @import("gen2/calc.zig").Summary;
|
|
117
119
|
pub const NULL = @import("gen2/calc.zig").NULL;
|
|
@@ -121,8 +123,8 @@ pub const gen2 = struct {
|
|
|
121
123
|
|
|
122
124
|
// Internal APIs used by other pkmn libraries, not actually part of the public API.
|
|
123
125
|
// NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
|
|
124
|
-
pub const js = if (options.internal) @import("common/js.zig") else
|
|
126
|
+
pub const js = if (options.internal) @import("common/js.zig") else {};
|
|
125
127
|
pub const bindings = if (options.internal) struct {
|
|
126
128
|
pub const node = @import("node.zig");
|
|
127
129
|
pub const wasm = @import("wasm.zig");
|
|
128
|
-
} else
|
|
130
|
+
} else {};
|