@pkmn/engine 0.1.0-dev.729b1e87 → 0.1.0-dev.736f7a8b
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 +101 -62
- package/build/pkg/addon.d.ts +7 -0
- package/build/pkg/addon.js +112 -0
- package/build/pkg/addon.js.map +1 -0
- package/build/pkg/data/layout.json +25 -3
- package/build/pkg/data.d.ts +1 -2
- package/build/pkg/data.js +3 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +5 -4
- package/build/pkg/gen1.js +47 -31
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +15 -12
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +4 -2
- package/build/pkg/protocol.js +5 -3
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +400 -0
- package/build/test/display.js.map +1 -0
- package/build/tools/debug.js +133 -0
- package/build/tools/debug.js.map +1 -0
- package/build.zig +101 -87
- package/package.json +56 -36
- package/src/bin/install-pkmn-engine +68 -36
- package/src/bin/pkmn-debug +62 -0
- package/src/lib/binding/node.zig +46 -141
- package/src/lib/binding/wasm.zig +6 -4
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +11 -11
- package/src/lib/common/debug.zig +21 -11
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +60 -0
- package/src/lib/common/options.zig +12 -3
- package/src/lib/common/protocol.zig +315 -197
- package/src/lib/common/rational.zig +353 -0
- package/src/lib/common/rng.zig +10 -10
- package/src/lib/common/util.zig +12 -0
- package/src/lib/gen1/README.md +280 -166
- package/src/lib/gen1/calc.zig +426 -0
- package/src/lib/gen1/chance.zig +1044 -0
- package/src/lib/gen1/data/moves.zig +10 -10
- package/src/lib/gen1/data/species.zig +2 -2
- package/src/lib/gen1/data/types.zig +3 -3
- package/src/lib/gen1/data.zig +70 -44
- package/src/lib/gen1/helpers.zig +592 -36
- package/src/lib/gen1/mechanics.zig +1049 -627
- package/src/lib/gen2/README.md +87 -13
- package/src/lib/gen2/data/items.zig +3 -3
- package/src/lib/gen2/data/moves.zig +16 -3
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +3 -3
- package/src/lib/gen2/data.zig +67 -41
- package/src/lib/gen2/helpers.zig +15 -16
- package/src/lib/gen2/mechanics.zig +352 -11
- package/src/lib/pkmn.zig +68 -9
- package/src/test/display/pkmn.html.tmpl +368 -0
package/src/lib/gen2/README.md
CHANGED
|
@@ -54,13 +54,12 @@ The following information is required to simulate a Generation II Pokémon battl
|
|
|
54
54
|
| | | |
|
|
55
55
|
| `active.last_used_move` | `PlayerUsedMove` | `side.lastMove` |
|
|
56
56
|
|
|
57
|
-
- Pokémon Showdown
|
|
57
|
+
- Pokémon Showdown doesn't implement the correct Generation II RNG and as such its `seed` is
|
|
58
58
|
different
|
|
59
|
-
- `battle.turn` only needs to be tracked
|
|
60
|
-
protocol
|
|
59
|
+
- `battle.turn` only needs to be tracked to be compatible with the Pokémon Showdown protocol
|
|
61
60
|
- Battle results (win, lose, draw) or request states are communicated via the return code of
|
|
62
61
|
`Battle.update`
|
|
63
|
-
- Nicknames (`BattleMonNick`/`pokemon.name`)
|
|
62
|
+
- Nicknames (`BattleMonNick`/`pokemon.name`) aren't handled by the pkmn engine as they're expected
|
|
64
63
|
to be handled by driver code if required
|
|
65
64
|
|
|
66
65
|
==**TODO**==
|
|
@@ -77,9 +76,9 @@ between the data structure for the "active" Pokémon and its party members (see
|
|
|
77
76
|
|
|
78
77
|
### `Pokemon` / `ActivePokemon`
|
|
79
78
|
|
|
80
|
-
Similar to the cartridge,
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
Similar to the cartridge, to save space different information is stored depending on whether a
|
|
80
|
+
[Pokémon](https://pkmn.cc/bulba/Pok%c3%a9mon_data_structure_%28Generation_II%29) is actively
|
|
81
|
+
participating in battle vs. is switched out (pret's [`battle_struct` vs.
|
|
83
82
|
`party_struct`](https://pkmn.cc/pokecrystal/macros/ram.asm)). In Pokémon Showdown, all the Pokemon
|
|
84
83
|
are represented by the same
|
|
85
84
|
[`Pokemon`](https://github.com/smogon/pokemon-showdown/blob/master/sim/pokemon.ts) class, and static
|
|
@@ -93,14 +92,14 @@ A `MoveSlot` is a data-type for a `(move, current pp)` pair. A pared down versio
|
|
|
93
92
|
Showdown's `Pokemon#moveSlot`, it also stores data from the cartridge's `battle_struct::Move` macro
|
|
94
93
|
and can be used to replace the `PlayerMove*` data. Move PP is stored as a full byte instead of how
|
|
95
94
|
the cartridge (`battle_struct::PP`) stores it (6 bits for current PP and the remaining 2 bits used
|
|
96
|
-
to store the number of applied PP Ups). PP Up bits
|
|
95
|
+
to store the number of applied PP Ups). PP Up bits don't actually need to be stored on move slot
|
|
97
96
|
as max PP is never relevant in Generation II (the Mystery Berry always increases PP from 0 -> 5).
|
|
98
97
|
|
|
99
98
|
#### `Status`
|
|
100
99
|
|
|
101
100
|
Bitfield representation of a Pokémon's major [status
|
|
102
|
-
condition](https://pkmn.cc/bulba/Status_condition), mirroring how it
|
|
103
|
-
value of `0x00` means that the Pokémon
|
|
101
|
+
condition](https://pkmn.cc/bulba/Status_condition), mirroring how it's stored on the cartridge. A
|
|
102
|
+
value of `0x00` means that the Pokémon isn't affected by any major status, otherwise the lower 3
|
|
104
103
|
bits represent the remaining duration for Sleep. Other status are denoted by the presence of
|
|
105
104
|
individual bits - at most one status should be set at any given time.
|
|
106
105
|
|
|
@@ -161,7 +160,7 @@ boosts should always range from `-6`...`6` instead of `1`...`13` as on the cartr
|
|
|
161
160
|
|
|
162
161
|
`Moves` serves as an identifier for a unique [Pokémon move](https://pkmn.cc/bulba/Move) that can be
|
|
163
162
|
used to retrieve a `Move` with information regarding base power, accuracy and type. As covered
|
|
164
|
-
|
|
163
|
+
earlier, PP information isn't strictly necessary in Generation I, but fits neatly into the 4 bits of
|
|
165
164
|
padding after noticing that all PP values are multiples of 5. `Moves.None` exists as a special
|
|
166
165
|
sentinel value to indicate `null`.
|
|
167
166
|
|
|
@@ -215,11 +214,86 @@ entropy](https://en.wikipedia.org/wiki/Entropy_(information_theory))) is as foll
|
|
|
215
214
|
| **wrap** | 0...5 | 3 | | **perish song** | 0...4 | 3 |
|
|
216
215
|
| **item** | TODO | 5? | | **gender** | 0..1 | 1 |
|
|
217
216
|
|
|
218
|
-
From this
|
|
219
|
-
much overhead the representations
|
|
217
|
+
From this one can determine the minimum bits required to store each data structure to determine how
|
|
218
|
+
much overhead the preceding representations have after taking into consideration [alignment &
|
|
220
219
|
padding](https://en.wikipedia.org/wiki/Data_structure_alignment) and
|
|
221
220
|
[denormalization](https://en.wikipedia.org/wiki/Denormalization):
|
|
222
221
|
|
|
222
|
+
## Details
|
|
223
|
+
|
|
224
|
+
| pkmn | Pokémon Crystal (pret) |
|
|
225
|
+
| ------------------------- | ------------------------------------------------- |
|
|
226
|
+
| `start` | `DoBattle` |
|
|
227
|
+
| `update` | `BattleTurn` |
|
|
228
|
+
| `selectMove` / `saveMove` | `CheckPlayerLockedIn` / `ParsePlayerAction` |
|
|
229
|
+
| `switchIn` | `SwitchPlayerMon` / `SendOutPlayerMon` |
|
|
230
|
+
| `turnOrder` | `DetermineMoveOrder` |
|
|
231
|
+
| `doTurn` | `DoTurn` |
|
|
232
|
+
| `executeMove` | `DoMove` |
|
|
233
|
+
| `beforeMove` | `CheckTurn` |
|
|
234
|
+
| `canMove` | |
|
|
235
|
+
| `decrementPP` | |
|
|
236
|
+
| `doMove` | |
|
|
237
|
+
| `checkCriticalHit` | `BattleCommand_Critical` |
|
|
238
|
+
| `calcDamage` | `BattleCommand_DamageCalc` / `PlayerAttackDamage` |
|
|
239
|
+
| `adjustDamage` | `BattleCommand_Stab` |
|
|
240
|
+
| `randomizeDamage` | `BattleCommand_DamageVariation` |
|
|
241
|
+
| `specialDamage` | `BattleCommand_ConstantDamage` |
|
|
242
|
+
| `applyDamage` | `BattleCommand_ApplyDamage` |
|
|
243
|
+
| `checkHit` | `BattleCommand_CheckHit` |
|
|
244
|
+
| `checkFaint` | `CheckFaint` |
|
|
245
|
+
| `handleResidual` | `ResidualDamage` |
|
|
246
|
+
| `endTurn` | `EndTurn` |
|
|
247
|
+
|
|
248
|
+
DoBattle
|
|
249
|
+
BattleTurn
|
|
250
|
+
- HandleBerserkGene
|
|
251
|
+
- CheckPlayerLockedIn
|
|
252
|
+
- BattleMenu
|
|
253
|
+
- ParsePlayerAction
|
|
254
|
+
- DetermineMoveOrder
|
|
255
|
+
- CompareMovePriority
|
|
256
|
+
- GetMovePriority
|
|
257
|
+
- Battle_*
|
|
258
|
+
- Handle*Switch
|
|
259
|
+
- SwitchPlayerMon
|
|
260
|
+
- BreakAttraction
|
|
261
|
+
- SendOutPlayerMon
|
|
262
|
+
- SpikesDamage
|
|
263
|
+
- *Turn_EndOpponentProtectEndureDestinyBond
|
|
264
|
+
- EndUserDestinyBond
|
|
265
|
+
- Do*Turn
|
|
266
|
+
- EndOpponentProtectEndureDestinyBond
|
|
267
|
+
- ResidualDamage
|
|
268
|
+
- CheckFaint_*
|
|
269
|
+
- HandleBetweenTurnEffects
|
|
270
|
+
- HandleFutureSight
|
|
271
|
+
- HandleWeather
|
|
272
|
+
- HandleWrap
|
|
273
|
+
- HandlePerishSong
|
|
274
|
+
- HandleLeftovers
|
|
275
|
+
- HandleMysteryBerry
|
|
276
|
+
- HandleDefrost
|
|
277
|
+
- HandleSafeguard
|
|
278
|
+
- HandleScreens
|
|
279
|
+
- HandleStatBoostingHeldItems
|
|
280
|
+
- HandleHealingItems
|
|
281
|
+
- HandleEncore
|
|
282
|
+
|
|
283
|
+
DoTurn
|
|
284
|
+
DoMove = executeMove
|
|
285
|
+
CheckTurn = beforeMove
|
|
286
|
+
BattleCommand_Critical = checkCriticalHit
|
|
287
|
+
BattleCommand_Stab = adjustDamage
|
|
288
|
+
BattleCommand_DamageVariation = randomizeDamage
|
|
289
|
+
BattleCommand_CheckHit = checkHit
|
|
290
|
+
BattleCommand_ApplyDamage = applyDamage
|
|
291
|
+
BattleCommand_CheckFaint
|
|
292
|
+
BattleCommand_BuildOpponentRage = buildRage
|
|
293
|
+
BattleCommand_DamageCalc (PlayerAttackDamage / EnemyAttackDamage) = calcDamage
|
|
294
|
+
BattleCommand_ConstantDamage = specialDamage
|
|
295
|
+
DoEnemyDamage / DoPlayerDamage / DoSubstituteDamage
|
|
296
|
+
|
|
223
297
|
==**TODO**==
|
|
224
298
|
|
|
225
299
|
## Protocol
|
|
@@ -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(@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
|
};
|
|
@@ -279,9 +279,11 @@ pub const Move = enum(u8) {
|
|
|
279
279
|
target: Target,
|
|
280
280
|
/// The chance of the move's secondary effect occurring.
|
|
281
281
|
chance: u8 = 0,
|
|
282
|
+
/// The priority of the move
|
|
283
|
+
priority: i8 = 0,
|
|
282
284
|
|
|
283
285
|
comptime {
|
|
284
|
-
assert(@sizeOf(Data) ==
|
|
286
|
+
assert(@sizeOf(Data) == 8);
|
|
285
287
|
}
|
|
286
288
|
};
|
|
287
289
|
|
|
@@ -450,6 +452,7 @@ pub const Move = enum(u8) {
|
|
|
450
452
|
.pp = 20,
|
|
451
453
|
.accuracy = 100,
|
|
452
454
|
.target = .Other,
|
|
455
|
+
.priority = -1,
|
|
453
456
|
},
|
|
454
457
|
// Fly
|
|
455
458
|
.{
|
|
@@ -709,6 +712,7 @@ pub const Move = enum(u8) {
|
|
|
709
712
|
.pp = 20,
|
|
710
713
|
.accuracy = 100,
|
|
711
714
|
.target = .Other,
|
|
715
|
+
.priority = -1,
|
|
712
716
|
},
|
|
713
717
|
// Sing
|
|
714
718
|
.{
|
|
@@ -916,6 +920,7 @@ pub const Move = enum(u8) {
|
|
|
916
920
|
.pp = 20,
|
|
917
921
|
.accuracy = 100,
|
|
918
922
|
.target = .Depends,
|
|
923
|
+
.priority = -1,
|
|
919
924
|
},
|
|
920
925
|
// SeismicToss
|
|
921
926
|
.{
|
|
@@ -1191,6 +1196,7 @@ pub const Move = enum(u8) {
|
|
|
1191
1196
|
.pp = 30,
|
|
1192
1197
|
.accuracy = 100,
|
|
1193
1198
|
.target = .Other,
|
|
1199
|
+
.priority = 1,
|
|
1194
1200
|
},
|
|
1195
1201
|
// Rage
|
|
1196
1202
|
.{
|
|
@@ -1962,6 +1968,7 @@ pub const Move = enum(u8) {
|
|
|
1962
1968
|
.pp = 10,
|
|
1963
1969
|
.accuracy = 100,
|
|
1964
1970
|
.target = .Self,
|
|
1971
|
+
.priority = 2,
|
|
1965
1972
|
},
|
|
1966
1973
|
// MachPunch
|
|
1967
1974
|
.{
|
|
@@ -1971,6 +1978,7 @@ pub const Move = enum(u8) {
|
|
|
1971
1978
|
.pp = 30,
|
|
1972
1979
|
.accuracy = 100,
|
|
1973
1980
|
.target = .Other,
|
|
1981
|
+
.priority = 1,
|
|
1974
1982
|
},
|
|
1975
1983
|
// ScaryFace
|
|
1976
1984
|
.{
|
|
@@ -2102,6 +2110,7 @@ pub const Move = enum(u8) {
|
|
|
2102
2110
|
.pp = 5,
|
|
2103
2111
|
.accuracy = 100,
|
|
2104
2112
|
.target = .Self,
|
|
2113
|
+
.priority = 2,
|
|
2105
2114
|
},
|
|
2106
2115
|
// BoneRush
|
|
2107
2116
|
.{
|
|
@@ -2156,6 +2165,7 @@ pub const Move = enum(u8) {
|
|
|
2156
2165
|
.pp = 10,
|
|
2157
2166
|
.accuracy = 100,
|
|
2158
2167
|
.target = .Self,
|
|
2168
|
+
.priority = 2,
|
|
2159
2169
|
},
|
|
2160
2170
|
// Charm
|
|
2161
2171
|
.{
|
|
@@ -2433,6 +2443,7 @@ pub const Move = enum(u8) {
|
|
|
2433
2443
|
.pp = 10,
|
|
2434
2444
|
.accuracy = 100,
|
|
2435
2445
|
.target = .Other,
|
|
2446
|
+
.priority = -1,
|
|
2436
2447
|
},
|
|
2437
2448
|
// MorningSun
|
|
2438
2449
|
.{
|
|
@@ -2525,6 +2536,7 @@ pub const Move = enum(u8) {
|
|
|
2525
2536
|
.pp = 20,
|
|
2526
2537
|
.accuracy = 100,
|
|
2527
2538
|
.target = .Depends,
|
|
2539
|
+
.priority = -1,
|
|
2528
2540
|
},
|
|
2529
2541
|
// PsychUp
|
|
2530
2542
|
.{
|
|
@@ -2543,6 +2555,7 @@ pub const Move = enum(u8) {
|
|
|
2543
2555
|
.pp = 5,
|
|
2544
2556
|
.accuracy = 100,
|
|
2545
2557
|
.target = .Other,
|
|
2558
|
+
.priority = 1,
|
|
2546
2559
|
},
|
|
2547
2560
|
// AncientPower
|
|
2548
2561
|
.{
|
|
@@ -2770,7 +2783,7 @@ pub const Move = enum(u8) {
|
|
|
2770
2783
|
|
|
2771
2784
|
comptime {
|
|
2772
2785
|
assert(@sizeOf(Move) == 1);
|
|
2773
|
-
assert(@sizeOf(@TypeOf(DATA)) ==
|
|
2786
|
+
assert(@sizeOf(@TypeOf(DATA)) == 2008);
|
|
2774
2787
|
}
|
|
2775
2788
|
|
|
2776
2789
|
/// The number of Pokémon moves in this generation.
|
|
@@ -2779,6 +2792,6 @@ pub const Move = enum(u8) {
|
|
|
2779
2792
|
/// Returns the `Data` corresponding to the move.
|
|
2780
2793
|
pub inline fn get(id: Move) Data {
|
|
2781
2794
|
assert(id != .None);
|
|
2782
|
-
return DATA[@
|
|
2795
|
+
return DATA[@intFromEnum(id) - 1];
|
|
2783
2796
|
}
|
|
2784
2797
|
};
|
|
@@ -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
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const builtin = @import("builtin");
|
|
3
3
|
|
|
4
|
+
const pkmn = @import("../pkmn.zig");
|
|
5
|
+
|
|
4
6
|
const data = @import("../common/data.zig");
|
|
5
7
|
const DEBUG = @import("../common/debug.zig").print;
|
|
6
|
-
const
|
|
8
|
+
const protocol = @import("../common/protocol.zig");
|
|
7
9
|
const rng = @import("../common/rng.zig");
|
|
10
|
+
const util = @import("../common/util.zig");
|
|
8
11
|
|
|
9
12
|
const items = @import("data/items.zig");
|
|
10
13
|
const moves = @import("data/moves.zig");
|
|
@@ -13,30 +16,37 @@ const types = @import("data/types.zig");
|
|
|
13
16
|
|
|
14
17
|
const gen1 = @import("../gen1/data.zig");
|
|
15
18
|
|
|
19
|
+
const calc = @import("calc.zig");
|
|
20
|
+
const chance = @import("chance.zig");
|
|
16
21
|
const mechanics = @import("mechanics.zig");
|
|
17
22
|
|
|
18
23
|
const assert = std.debug.assert;
|
|
19
24
|
const expectEqual = std.testing.expectEqual;
|
|
20
25
|
const expect = std.testing.expect;
|
|
21
26
|
|
|
22
|
-
pub const
|
|
27
|
+
pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
|
|
23
28
|
pub const MAX_LOGS: usize = 180; // TODO
|
|
24
29
|
|
|
25
|
-
pub const
|
|
26
|
-
|
|
30
|
+
pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
31
|
+
MAX_CHOICES
|
|
27
32
|
else
|
|
28
|
-
std.math.ceilPowerOfTwo(usize,
|
|
33
|
+
std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
|
|
29
34
|
pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
30
35
|
MAX_LOGS
|
|
31
36
|
else
|
|
32
37
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
33
38
|
|
|
39
|
+
/// Null object pattern implementation of pkmn.battle.Options for Generation II.
|
|
40
|
+
pub const NULL = pkmn.battle.options(protocol.NULL, chance.NULL, calc.NULL);
|
|
41
|
+
|
|
34
42
|
const Choice = data.Choice;
|
|
35
43
|
const ID = data.ID;
|
|
36
44
|
const Player = data.Player;
|
|
37
45
|
const Result = data.Result;
|
|
38
46
|
|
|
39
|
-
const
|
|
47
|
+
const PointerType = util.PointerType;
|
|
48
|
+
|
|
49
|
+
const showdown = pkmn.options.showdown;
|
|
40
50
|
|
|
41
51
|
pub const PRNG = rng.PRNG(2);
|
|
42
52
|
|
|
@@ -49,20 +59,22 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
49
59
|
turn: u16 = 0,
|
|
50
60
|
field: Field = .{},
|
|
51
61
|
|
|
52
|
-
pub inline fn side(self:
|
|
53
|
-
|
|
62
|
+
pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
63
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
64
|
+
return &self.sides[@intFromEnum(player)];
|
|
54
65
|
}
|
|
55
66
|
|
|
56
|
-
pub inline fn foe(self:
|
|
57
|
-
|
|
67
|
+
pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
68
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
69
|
+
return &self.sides[@intFromEnum(player.foe())];
|
|
58
70
|
}
|
|
59
71
|
|
|
60
|
-
pub inline fn active(self: *Self, player: Player) ID {
|
|
61
|
-
return player.ident(@intCast(
|
|
72
|
+
pub inline fn active(self: *const Self, player: Player) ID {
|
|
73
|
+
return player.ident(@intCast(self.side(player).pokemon[0].position));
|
|
62
74
|
}
|
|
63
75
|
|
|
64
|
-
pub fn update(self: *Self, c1: Choice, c2: Choice,
|
|
65
|
-
return mechanics.update(self, c1, c2,
|
|
76
|
+
pub fn update(self: *Self, c1: Choice, c2: Choice, options: anytype) !Result {
|
|
77
|
+
return mechanics.update(self, c1, c2, options);
|
|
66
78
|
}
|
|
67
79
|
|
|
68
80
|
pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
@@ -76,7 +88,7 @@ test Battle {
|
|
|
76
88
|
try expectEqual(496, @sizeOf(Battle(PRNG)));
|
|
77
89
|
}
|
|
78
90
|
|
|
79
|
-
const Field = packed struct {
|
|
91
|
+
pub const Field = packed struct {
|
|
80
92
|
weather: Weather = .None,
|
|
81
93
|
weather_duration: u4 = 0,
|
|
82
94
|
|
|
@@ -85,19 +97,21 @@ const Field = packed struct {
|
|
|
85
97
|
}
|
|
86
98
|
};
|
|
87
99
|
|
|
88
|
-
const Weather = enum(u4) {
|
|
100
|
+
pub const Weather = enum(u4) {
|
|
89
101
|
None,
|
|
90
102
|
Rain,
|
|
91
103
|
Sun,
|
|
92
104
|
Sandstorm,
|
|
93
105
|
};
|
|
94
106
|
|
|
95
|
-
const Side = extern struct {
|
|
107
|
+
pub const Side = extern struct {
|
|
96
108
|
pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
|
|
97
109
|
active: ActivePokemon = .{},
|
|
98
|
-
conditions: Conditions,
|
|
110
|
+
conditions: Conditions = .{},
|
|
111
|
+
// NB: wCurPlayerMove / wCurEnemyMove
|
|
112
|
+
last_selected_move: Move = .None,
|
|
113
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
99
114
|
last_used_move: Move = .None,
|
|
100
|
-
_: u8 = 0,
|
|
101
115
|
|
|
102
116
|
const Conditions = packed struct {
|
|
103
117
|
Spikes: bool = false,
|
|
@@ -118,41 +132,49 @@ const Side = extern struct {
|
|
|
118
132
|
assert(@sizeOf(Side) == 240);
|
|
119
133
|
}
|
|
120
134
|
|
|
121
|
-
pub inline fn get(self:
|
|
135
|
+
pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
136
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
122
137
|
assert(slot > 0 and slot <= 6);
|
|
123
138
|
const id = self.pokemon[slot - 1].position;
|
|
124
139
|
assert(id > 0 and id <= 6);
|
|
125
140
|
return &self.pokemon[id - 1];
|
|
126
141
|
}
|
|
127
142
|
|
|
128
|
-
pub inline fn stored(self:
|
|
143
|
+
pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
144
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
129
145
|
return self.get(1);
|
|
130
146
|
}
|
|
147
|
+
|
|
148
|
+
pub inline fn lastMove(self: anytype, encore: bool) Move {
|
|
149
|
+
assert(self.last_used_move != .None or !self.active.volatiles.dirty);
|
|
150
|
+
if (encore) return self.last_used_move;
|
|
151
|
+
return if (self.active.volatiles.dirty) .None else self.last_used_move;
|
|
152
|
+
}
|
|
131
153
|
};
|
|
132
154
|
|
|
133
155
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
134
|
-
const ActivePokemon = extern struct {
|
|
135
|
-
volatiles:
|
|
156
|
+
pub const ActivePokemon = extern struct {
|
|
157
|
+
volatiles: Volatiles align(4) = .{},
|
|
136
158
|
stats: Stats(u16) = .{},
|
|
137
159
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
138
160
|
boosts: Boosts = .{},
|
|
161
|
+
types: Types = .{},
|
|
139
162
|
species: Species = .None,
|
|
140
163
|
item: Item = .None,
|
|
141
|
-
last_move: Move = .None,
|
|
142
|
-
_: u8 = 0,
|
|
143
164
|
|
|
144
165
|
comptime {
|
|
145
166
|
assert(@sizeOf(ActivePokemon) == 44);
|
|
146
167
|
}
|
|
147
168
|
|
|
148
|
-
pub inline fn move(self:
|
|
169
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
170
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
149
171
|
assert(mslot > 0 and mslot <= 4);
|
|
150
172
|
assert(self.moves[mslot - 1].id != .None);
|
|
151
173
|
return &self.moves[mslot - 1];
|
|
152
174
|
}
|
|
153
175
|
};
|
|
154
176
|
|
|
155
|
-
const Pokemon = extern struct {
|
|
177
|
+
pub const Pokemon = extern struct {
|
|
156
178
|
stats: Stats(u16) = .{},
|
|
157
179
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
158
180
|
types: Types = .{},
|
|
@@ -169,7 +191,8 @@ const Pokemon = extern struct {
|
|
|
169
191
|
assert(@sizeOf(Pokemon) == 32);
|
|
170
192
|
}
|
|
171
193
|
|
|
172
|
-
pub inline fn move(self:
|
|
194
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
195
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
173
196
|
assert(mslot > 0 and mslot <= 4);
|
|
174
197
|
assert(self.moves[mslot - 1].id != .None);
|
|
175
198
|
return &self.moves[mslot - 1];
|
|
@@ -182,7 +205,7 @@ pub const Gender = enum(u2) {
|
|
|
182
205
|
Unknown,
|
|
183
206
|
};
|
|
184
207
|
|
|
185
|
-
const DVs = packed struct {
|
|
208
|
+
pub const DVs = packed struct {
|
|
186
209
|
gender: Gender = .Male,
|
|
187
210
|
pow: u6 = 40,
|
|
188
211
|
type: Type = .Dark,
|
|
@@ -205,11 +228,11 @@ const DVs = packed struct {
|
|
|
205
228
|
0xFF => .Unknown,
|
|
206
229
|
else => if ((@as(u8, dvs.atk) << 4 | dvs.spe) < ratio) Gender.Female else Gender.Male,
|
|
207
230
|
};
|
|
208
|
-
const p = @intCast(
|
|
231
|
+
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
209
232
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
210
233
|
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 = @
|
|
234
|
+
if (t >= @intFromEnum(Type.@"???")) t = t + 1;
|
|
235
|
+
return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
|
|
213
236
|
}
|
|
214
237
|
|
|
215
238
|
comptime {
|
|
@@ -238,7 +261,7 @@ test DVs {
|
|
|
238
261
|
try expectEqual(Gender.Female, dvs.gender);
|
|
239
262
|
}
|
|
240
263
|
|
|
241
|
-
const MoveSlot = packed struct {
|
|
264
|
+
pub const MoveSlot = packed struct {
|
|
242
265
|
id: Move = .None,
|
|
243
266
|
pp_ups: u2 = 3,
|
|
244
267
|
pp: u6 = 0,
|
|
@@ -250,7 +273,7 @@ const MoveSlot = packed struct {
|
|
|
250
273
|
|
|
251
274
|
pub const Status = gen1.Status;
|
|
252
275
|
|
|
253
|
-
const
|
|
276
|
+
pub const Volatiles = packed struct {
|
|
254
277
|
Bide: bool = false,
|
|
255
278
|
Thrashing: bool = false,
|
|
256
279
|
Flinch: bool = false,
|
|
@@ -284,8 +307,9 @@ const Volatile = packed struct {
|
|
|
284
307
|
trapped: bool = false,
|
|
285
308
|
switched: bool = false,
|
|
286
309
|
switching: bool = false,
|
|
310
|
+
dirty: bool = false,
|
|
287
311
|
|
|
288
|
-
_:
|
|
312
|
+
_: u4 = 0,
|
|
289
313
|
|
|
290
314
|
bind: u4 = 0,
|
|
291
315
|
future_sight: FutureSight = .{},
|
|
@@ -312,7 +336,7 @@ const Volatile = packed struct {
|
|
|
312
336
|
};
|
|
313
337
|
|
|
314
338
|
comptime {
|
|
315
|
-
assert(@sizeOf(
|
|
339
|
+
assert(@sizeOf(Volatiles) == 16);
|
|
316
340
|
}
|
|
317
341
|
};
|
|
318
342
|
|
|
@@ -327,10 +351,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
327
351
|
|
|
328
352
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
329
353
|
assert(level > 0 and level <= 100);
|
|
330
|
-
const
|
|
354
|
+
const float: f32 = @floatFromInt(exp);
|
|
355
|
+
const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
331
356
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
332
357
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
333
|
-
return @intCast(
|
|
358
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
334
359
|
}
|
|
335
360
|
};
|
|
336
361
|
}
|
|
@@ -390,17 +415,18 @@ test Item {
|
|
|
390
415
|
pub const Move = moves.Move;
|
|
391
416
|
|
|
392
417
|
test Move {
|
|
393
|
-
try expectEqual(251, @
|
|
418
|
+
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
394
419
|
const move = Move.get(.DynamicPunch);
|
|
395
420
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
396
421
|
try expectEqual(@as(u8, 50), move.accuracy);
|
|
397
422
|
try expectEqual(@as(u8, 5), move.pp);
|
|
423
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
398
424
|
}
|
|
399
425
|
|
|
400
426
|
pub const Species = species.Species;
|
|
401
427
|
|
|
402
428
|
test Species {
|
|
403
|
-
try expectEqual(152, @
|
|
429
|
+
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
404
430
|
try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
|
|
405
431
|
}
|
|
406
432
|
|
|
@@ -409,7 +435,7 @@ pub const Types = types.Types;
|
|
|
409
435
|
pub const Effectiveness = gen1.Effectiveness;
|
|
410
436
|
|
|
411
437
|
test Types {
|
|
412
|
-
try expectEqual(13, @
|
|
438
|
+
try expectEqual(13, @intFromEnum(Type.Electric));
|
|
413
439
|
|
|
414
440
|
try expect(!Type.Steel.special());
|
|
415
441
|
try expect(Type.Dark.special());
|