@pkmn/engine 0.1.0-dev.89cc1c98 → 0.1.0-dev.8b192003
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -3
- package/README.md +44 -19
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +4 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +5 -5
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +2 -2
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +5 -4
- package/build/tools/debug.js.map +1 -1
- package/build.zig +100 -102
- package/package.json +31 -23
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +393 -396
- package/src/lib/common/rational.zig +109 -15
- package/src/lib/common/rng.zig +20 -11
- package/src/lib/gen1/calc.zig +478 -92
- package/src/lib/gen1/chance.zig +195 -428
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +21 -21
- package/src/lib/gen1/helpers.zig +88 -534
- package/src/lib/gen1/mechanics.zig +377 -360
- package/src/lib/gen2/chance.zig +25 -457
- package/src/lib/gen2/data.zig +13 -11
- package/src/lib/gen2/generated.zig +27 -78
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +1022 -456
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +27 -12
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -330
package/src/lib/gen2/data.zig
CHANGED
|
@@ -239,7 +239,7 @@ pub const DVs = packed struct(u16) {
|
|
|
239
239
|
};
|
|
240
240
|
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
241
241
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
242
|
-
|
|
242
|
+
const t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
|
|
243
243
|
return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
|
|
244
244
|
}
|
|
245
245
|
};
|
|
@@ -248,12 +248,12 @@ test DVs {
|
|
|
248
248
|
var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
|
|
249
249
|
try expectEqual(Gender.Unknown, dvs.gender);
|
|
250
250
|
try expectEqual(Type.Ice, dvs.type);
|
|
251
|
-
try expectEqual(
|
|
251
|
+
try expectEqual(70, dvs.power());
|
|
252
252
|
|
|
253
253
|
dvs = DVs.from(.Pikachu, .{ .spc = 14 });
|
|
254
254
|
try expectEqual(Gender.Male, dvs.gender);
|
|
255
255
|
try expectEqual(Type.Dark, dvs.type);
|
|
256
|
-
try expectEqual(
|
|
256
|
+
try expectEqual(69, dvs.power());
|
|
257
257
|
|
|
258
258
|
dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
|
|
259
259
|
try expectEqual(Gender.Female, dvs.gender);
|
|
@@ -314,16 +314,18 @@ pub const Volatiles = packed struct(u128) {
|
|
|
314
314
|
Encore: bool = false,
|
|
315
315
|
LockOn: bool = false,
|
|
316
316
|
DestinyBond: bool = false,
|
|
317
|
+
Trapped: bool = false,
|
|
317
318
|
|
|
318
|
-
trapped: bool = false,
|
|
319
319
|
switched: bool = false,
|
|
320
|
+
// TODO: replace with simply looking at Choice on Battle
|
|
320
321
|
switching: bool = false,
|
|
322
|
+
// TODO: simply store both last moves on side
|
|
321
323
|
dirty: bool = false,
|
|
322
324
|
|
|
323
325
|
frozen: bool = false,
|
|
324
326
|
minimized: bool = false,
|
|
325
327
|
_: u10 = 0,
|
|
326
|
-
// TODO?
|
|
328
|
+
// TODO: is this needed for unmodified stats?
|
|
327
329
|
transform: u4 = 0,
|
|
328
330
|
|
|
329
331
|
bide: u16 = 0,
|
|
@@ -378,9 +380,9 @@ test Stats {
|
|
|
378
380
|
try expectEqual(0, stats.def);
|
|
379
381
|
|
|
380
382
|
var base = Species.get(.Gyarados).stats;
|
|
381
|
-
try expectEqual(
|
|
383
|
+
try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
382
384
|
base = Species.get(.Pidgeot).stats;
|
|
383
|
-
try expectEqual(
|
|
385
|
+
try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
384
386
|
}
|
|
385
387
|
|
|
386
388
|
pub const Boosts = packed struct(u32) {
|
|
@@ -426,8 +428,8 @@ test Move {
|
|
|
426
428
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
427
429
|
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
428
430
|
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
429
|
-
try expectEqual(
|
|
430
|
-
try expectEqual(
|
|
431
|
+
try expectEqual(5, Move.pp(.DynamicPunch));
|
|
432
|
+
try expectEqual(-1, Move.get(.Counter).priority);
|
|
431
433
|
try expect(!move.effect.isHighCritical());
|
|
432
434
|
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
433
435
|
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
@@ -437,7 +439,7 @@ pub const Species = species.Species;
|
|
|
437
439
|
|
|
438
440
|
test Species {
|
|
439
441
|
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
440
|
-
try expectEqual(
|
|
442
|
+
try expectEqual(100, Species.get(.Celebi).stats.spd);
|
|
441
443
|
}
|
|
442
444
|
|
|
443
445
|
pub const Type = types.Type;
|
|
@@ -450,7 +452,7 @@ test Types {
|
|
|
450
452
|
try expect(!Type.Steel.special());
|
|
451
453
|
try expect(Type.Dark.special());
|
|
452
454
|
|
|
453
|
-
try expectEqual(
|
|
455
|
+
try expectEqual(3, Type.Water.precedence());
|
|
454
456
|
try expect(Type.Bug.precedence() > Type.Poison.precedence());
|
|
455
457
|
|
|
456
458
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|
|
@@ -7,8 +7,6 @@ const common = @import("../common/data.zig");
|
|
|
7
7
|
const data = @import("data.zig");
|
|
8
8
|
const mechanics = @import("mechanics.zig");
|
|
9
9
|
|
|
10
|
-
const assert = std.debug.assert;
|
|
11
|
-
|
|
12
10
|
const Player = common.Player;
|
|
13
11
|
const Result = common.Result;
|
|
14
12
|
|
|
@@ -42,7 +40,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
42
40
|
var log = options.log;
|
|
43
41
|
|
|
44
42
|
var side = battle.side(player);
|
|
45
|
-
|
|
43
|
+
const volatiles = &side.active.volatiles;
|
|
46
44
|
|
|
47
45
|
const ident = battle.active(player);
|
|
48
46
|
const foe_ident = battle.active(player.foe());
|
|
@@ -97,17 +95,8 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
97
95
|
},
|
|
98
96
|
.OHKO => {
|
|
99
97
|
if (!try canMove(battle, player, state, options)) return;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
assert(move.type == .Normal or move.type == .Ground);
|
|
103
|
-
const foe = battle.foe(player);
|
|
104
|
-
const immune = if (move.type == .Normal)
|
|
105
|
-
foe.active.types.includes(.Ghost) and !foe.active.volatiles.Foresight
|
|
106
|
-
else
|
|
107
|
-
foe.active.types.includes(.Ghost);
|
|
108
|
-
state.effectiveness = if (immune) 0 else Effectiveness.neutral;
|
|
109
|
-
|
|
110
|
-
try Effects.ohko(battle, player, state, options);
|
|
98
|
+
try Effects.ohko.immune(battle, player, state, options);
|
|
99
|
+
try Effects.ohko.damage(battle, player, state, options);
|
|
111
100
|
try applyDamage(battle, player, state, options);
|
|
112
101
|
try reportOutcome(battle, player, state, options);
|
|
113
102
|
_ = try afterMove(!KINGS, battle, player, state, options);
|
|
@@ -118,9 +107,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
118
107
|
try calcDamage(battle, player, state, options);
|
|
119
108
|
try adjustDamage(battle, player, state, options);
|
|
120
109
|
try randomizeDamage(battle, player, state, options);
|
|
121
|
-
|
|
122
|
-
if (battle.foe(player).active.volatiles.Flying) state.damage *|= 2;
|
|
123
|
-
|
|
110
|
+
try Effects.double.flying(battle, player, state, options);
|
|
124
111
|
try checkHit(battle, player, state, options);
|
|
125
112
|
try applyDamage(battle, player, state, options);
|
|
126
113
|
try reportOutcome(battle, player, state, options);
|
|
@@ -157,9 +144,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
157
144
|
try calcDamage(battle, player, state, options);
|
|
158
145
|
try adjustDamage(battle, player, state, options);
|
|
159
146
|
try randomizeDamage(battle, player, state, options);
|
|
160
|
-
|
|
161
|
-
if (battle.foe(player).active.volatiles.minimized) state.damage *|= 2;
|
|
162
|
-
|
|
147
|
+
try Effects.double.minimize(battle, player, state, options);
|
|
163
148
|
try checkHit(battle, player, state, options);
|
|
164
149
|
try effectChance(battle, player, state, options);
|
|
165
150
|
try applyDamage(battle, player, state, options);
|
|
@@ -188,7 +173,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
188
173
|
if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
|
|
189
174
|
// TODO rampage
|
|
190
175
|
// usedmovetext
|
|
191
|
-
try log.move(ident, state.move, foe_ident); // FIXME self? from?
|
|
176
|
+
try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
|
|
192
177
|
try checkHit(battle, player, state, options);
|
|
193
178
|
try checkCriticalHit(battle, player, state, options);
|
|
194
179
|
try calcDamage(battle, player, state, options);
|
|
@@ -273,7 +258,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
273
258
|
try Effects.hyperBeam(battle, player, state, options);
|
|
274
259
|
_ = try afterMove(!KINGS, battle, player, state, options);
|
|
275
260
|
},
|
|
276
|
-
.Counter => {
|
|
261
|
+
.MirrorCoat, .Counter => {
|
|
277
262
|
if (!try canMove(battle, player, state, options)) return;
|
|
278
263
|
try Effects.counter(battle, player, state, options);
|
|
279
264
|
try applyDamage(battle, player, state, options);
|
|
@@ -308,7 +293,6 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
308
293
|
if (!try canMove(battle, player, state, options)) return;
|
|
309
294
|
try checkCriticalHit(battle, player, state, options);
|
|
310
295
|
try calcDamage(battle, player, state, options);
|
|
311
|
-
// TODO thunderaccuracy
|
|
312
296
|
try checkHit(battle, player, state, options);
|
|
313
297
|
try effectChance(battle, player, state, options);
|
|
314
298
|
try adjustDamage(battle, player, state, options);
|
|
@@ -324,9 +308,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
324
308
|
try calcDamage(battle, player, state, options);
|
|
325
309
|
try adjustDamage(battle, player, state, options);
|
|
326
310
|
try randomizeDamage(battle, player, state, options);
|
|
327
|
-
|
|
328
|
-
if (battle.foe(player).active.volatiles.Underground) state.damage *|= 2;
|
|
329
|
-
|
|
311
|
+
try Effects.double.underground(battle, player, state, options);
|
|
330
312
|
try checkHit(battle, player, state, options);
|
|
331
313
|
try effectChance(battle, player, state, options);
|
|
332
314
|
try applyDamage(battle, player, state, options);
|
|
@@ -339,13 +321,10 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
339
321
|
try calcDamage(battle, player, state, options);
|
|
340
322
|
try adjustDamage(battle, player, state, options);
|
|
341
323
|
try checkHit(battle, player, state, options);
|
|
342
|
-
|
|
343
|
-
assert(volatiles.Rage);
|
|
344
|
-
state.damage *|= (volatiles.rage +| 1);
|
|
345
|
-
|
|
324
|
+
try Effects.rage.damage(battle, player, state, options);
|
|
346
325
|
try randomizeDamage(battle, player, state, options);
|
|
347
326
|
// TODO failuretext
|
|
348
|
-
try Effects.rage(battle, player, state, options);
|
|
327
|
+
try Effects.rage.start(battle, player, state, options);
|
|
349
328
|
try applyDamage(battle, player, state, options);
|
|
350
329
|
try reportOutcome(battle, player, state, options);
|
|
351
330
|
_ = try afterMove(KINGS, battle, player, state, options);
|
|
@@ -363,47 +342,32 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
363
342
|
if (!try canMove(battle, player, state, options)) return;
|
|
364
343
|
try Effects.heal(battle, player, state, options);
|
|
365
344
|
},
|
|
345
|
+
.LightScreen => {
|
|
346
|
+
if (!try canMove(battle, player, state, options)) return;
|
|
347
|
+
try Effects.lightScreen(battle, player, state, options);
|
|
348
|
+
},
|
|
366
349
|
.Haze => {
|
|
367
350
|
if (!try canMove(battle, player, state, options)) return;
|
|
368
351
|
try Effects.haze(battle, player, state, options);
|
|
369
352
|
},
|
|
370
|
-
.
|
|
353
|
+
.Reflect => {
|
|
371
354
|
if (!try canMove(battle, player, state, options)) return;
|
|
372
|
-
try Effects.
|
|
355
|
+
try Effects.reflect(battle, player, state, options);
|
|
373
356
|
},
|
|
374
357
|
.FocusEnergy => {
|
|
375
358
|
if (!try canMove(battle, player, state, options)) return;
|
|
376
359
|
try Effects.focusEnergy(battle, player, state, options);
|
|
377
360
|
},
|
|
378
361
|
.Bide => {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
assert(volatiles.attacks > 0);
|
|
382
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
383
|
-
// if (!extend) volatiles.attacks = 0;
|
|
384
|
-
// } else {
|
|
385
|
-
volatiles.attacks -= 1;
|
|
386
|
-
// }
|
|
387
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
388
|
-
|
|
389
|
-
if (volatiles.attacks != 0) return try options.log.activate(ident, .Bide);
|
|
390
|
-
|
|
391
|
-
volatiles.Bide = false;
|
|
392
|
-
try options.log.end(ident, .Bide);
|
|
393
|
-
|
|
394
|
-
state.damage = volatiles.bide *% 2;
|
|
395
|
-
volatiles.bide = 0;
|
|
396
|
-
|
|
397
|
-
if (state.damage == 0) state.miss = true;
|
|
398
|
-
|
|
362
|
+
try Effects.bide.store(battle, player, state, options);
|
|
399
363
|
// doturn
|
|
400
364
|
const charging = false; // TODO
|
|
401
365
|
const skip_pp = charging or state.move == .Struggle or
|
|
402
366
|
(volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
|
|
403
367
|
if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
|
|
404
368
|
// usedmovetext
|
|
405
|
-
try log.move(ident, state.move, foe_ident); // FIXME self? from?
|
|
406
|
-
|
|
369
|
+
try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
|
|
370
|
+
try Effects.bide.unleash(battle, player, state, options);
|
|
407
371
|
|
|
408
372
|
if (state.immune()) {
|
|
409
373
|
state.damage = 0;
|
|
@@ -522,7 +486,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
522
486
|
try randomizeDamage(battle, player, state, options);
|
|
523
487
|
try checkHit(battle, player, state, options);
|
|
524
488
|
try effectChance(battle, player, state, options);
|
|
525
|
-
try Effects.snore(battle, player, state, options);
|
|
489
|
+
if (!try Effects.snore(battle, player, state, options)) return;
|
|
526
490
|
try applyDamage(battle, player, state, options);
|
|
527
491
|
try reportOutcome(battle, player, state, options);
|
|
528
492
|
if (!try afterMove(!KINGS, battle, player, state, options)) return;
|
|
@@ -582,19 +546,19 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
582
546
|
try Effects.sandstorm(battle, player, state, options);
|
|
583
547
|
},
|
|
584
548
|
.Rollout => {
|
|
585
|
-
|
|
549
|
+
try Effects.rollout.check(battle, player, state, options);
|
|
586
550
|
// doturn
|
|
587
551
|
const charging = false; // TODO
|
|
588
552
|
const skip_pp = charging or state.move == .Struggle or
|
|
589
553
|
(volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
|
|
590
554
|
if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
|
|
591
555
|
// usedmovetext
|
|
592
|
-
try log.move(ident, state.move, foe_ident); // FIXME self? from?
|
|
556
|
+
try log.move(.{ ident, state.move, foe_ident }); // FIXME self? from?
|
|
593
557
|
try checkCriticalHit(battle, player, state, options);
|
|
594
558
|
try calcDamage(battle, player, state, options);
|
|
595
559
|
try adjustDamage(battle, player, state, options);
|
|
596
560
|
try checkHit(battle, player, state, options);
|
|
597
|
-
|
|
561
|
+
try Effects.rollout.power(battle, player, state, options);
|
|
598
562
|
try randomizeDamage(battle, player, state, options);
|
|
599
563
|
try applyDamage(battle, player, state, options);
|
|
600
564
|
try reportOutcome(battle, player, state, options);
|
|
@@ -615,12 +579,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
615
579
|
.Swagger => {
|
|
616
580
|
if (!try canMove(battle, player, state, options)) return;
|
|
617
581
|
try checkHit(battle, player, state, options);
|
|
618
|
-
|
|
619
|
-
// TODO attackup2
|
|
620
|
-
// TODO switchturn
|
|
621
|
-
// TODO failuretext
|
|
622
|
-
// TODO switchturn
|
|
623
|
-
// TODO switchturn
|
|
582
|
+
try Effects.boost(battle, player.foe(), state, options);
|
|
624
583
|
try Effects.confusionChance(battle, player, state, options);
|
|
625
584
|
},
|
|
626
585
|
.FuryCutter => {
|
|
@@ -694,14 +653,12 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
694
653
|
.Magnitude => {
|
|
695
654
|
if (!try canMove(battle, player, state, options)) return;
|
|
696
655
|
try checkCriticalHit(battle, player, state, options);
|
|
697
|
-
|
|
656
|
+
try Effects.magnitude(battle, player, state, options);
|
|
698
657
|
try calcDamage(battle, player, state, options);
|
|
699
658
|
try adjustDamage(battle, player, state, options);
|
|
700
659
|
try randomizeDamage(battle, player, state, options);
|
|
701
660
|
try checkHit(battle, player, state, options);
|
|
702
|
-
|
|
703
|
-
if (battle.foe(player).active.volatiles.Underground) state.damage *|= 2;
|
|
704
|
-
|
|
661
|
+
try Effects.double.underground(battle, player, state, options);
|
|
705
662
|
try applyDamage(battle, player, state, options);
|
|
706
663
|
try reportOutcome(battle, player, state, options);
|
|
707
664
|
_ = try afterMove(KINGS, battle, player, state, options);
|
|
@@ -755,9 +712,7 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
755
712
|
try calcDamage(battle, player, state, options);
|
|
756
713
|
try adjustDamage(battle, player, state, options);
|
|
757
714
|
try randomizeDamage(battle, player, state, options);
|
|
758
|
-
|
|
759
|
-
if (battle.foe(player).active.volatiles.Flying) state.damage *|= 2;
|
|
760
|
-
|
|
715
|
+
try Effects.double.flying(battle, player, state, options);
|
|
761
716
|
try checkHit(battle, player, state, options);
|
|
762
717
|
try effectChance(battle, player, state, options);
|
|
763
718
|
try applyDamage(battle, player, state, options);
|
|
@@ -773,12 +728,6 @@ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype)
|
|
|
773
728
|
if (!try canMove(battle, player, state, options)) return;
|
|
774
729
|
try Effects.sunnyDay(battle, player, state, options);
|
|
775
730
|
},
|
|
776
|
-
.MirrorCoat => {
|
|
777
|
-
if (!try canMove(battle, player, state, options)) return;
|
|
778
|
-
try Effects.mirrorCoat(battle, player, state, options);
|
|
779
|
-
try applyDamage(battle, player, state, options);
|
|
780
|
-
_ = try afterMove(KINGS, battle, player, state, options);
|
|
781
|
-
},
|
|
782
731
|
.PsychUp => {
|
|
783
732
|
if (!try canMove(battle, player, state, options)) return;
|
|
784
733
|
try Effects.psychUp(battle, player, state, options);
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -65,17 +65,47 @@ pub const Battle = struct {
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
pub fn prng(rand: *PSRNG) data.PRNG {
|
|
68
|
-
// FIXME: what is max range in gen2?
|
|
69
68
|
return .{
|
|
70
69
|
.src = .{
|
|
71
70
|
.seed = if (showdown)
|
|
72
71
|
rand.newSeed()
|
|
73
|
-
else
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
else seed: {
|
|
73
|
+
// GLITCH: The initial RNG seed in Gen II link battles is heavily constrained
|
|
74
|
+
// https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
|
|
75
|
+
var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
76
|
+
|
|
77
|
+
// Bytes in seed can only range from 0-252, not 0-255
|
|
78
|
+
const max: u8 = 253;
|
|
79
|
+
// Instructions take multiples of 4 cycles to execute
|
|
80
|
+
var timer: u16 = 4 * rand.range(u16, 0, 16384);
|
|
81
|
+
var add: u8 = rand.range(u8, 0, 256);
|
|
82
|
+
var sub: u8 = rand.range(u8, 0, 256);
|
|
83
|
+
var carry: u8 = 0;
|
|
84
|
+
var div: u8 = 0;
|
|
85
|
+
|
|
86
|
+
var i: u8 = 0;
|
|
87
|
+
while (i < seed.len) {
|
|
88
|
+
div = @intCast(timer >> 8);
|
|
89
|
+
// This is effectively @addWithOverflow but with three operands
|
|
90
|
+
const result = @as(u16, add) + @as(u16, div) + @as(u16, carry);
|
|
91
|
+
add = @intCast(result & 255);
|
|
92
|
+
carry = @intFromBool(result > 255);
|
|
93
|
+
timer +%= 44;
|
|
94
|
+
div = @intCast(timer >> 8);
|
|
95
|
+
sub -%= div +% carry;
|
|
96
|
+
|
|
97
|
+
if (sub < max) {
|
|
98
|
+
seed[i] = sub;
|
|
99
|
+
i += 1;
|
|
100
|
+
timer +%= 152;
|
|
101
|
+
carry = 1;
|
|
102
|
+
} else {
|
|
103
|
+
timer +%= 132;
|
|
104
|
+
carry = 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
break :seed seed;
|
|
108
|
+
},
|
|
79
109
|
},
|
|
80
110
|
};
|
|
81
111
|
}
|