@pkmn/engine 0.1.0-dev.cf6f5d2c → 0.1.0-dev.d3e796d5
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 +55 -44
- package/build/pkg/addon.js +3 -2
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +25 -3
- package/build/pkg/gen1.d.ts +2 -1
- package/build/pkg/gen1.js +36 -18
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +4 -2
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +4 -2
- package/build/pkg/protocol.js +4 -2
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +7 -3
- package/build/test/display.js.map +1 -1
- package/build.zig +86 -62
- package/package.json +25 -16
- package/src/bin/install-pkmn-engine +10 -5
- package/src/lib/binding/node.zig +45 -140
- package/src/lib/binding/wasm.zig +4 -2
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +11 -11
- package/src/lib/common/debug.zig +20 -11
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +60 -0
- package/src/lib/common/options.zig +9 -0
- package/src/lib/common/protocol.zig +277 -159
- 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 +239 -151
- 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 +64 -38
- package/src/lib/gen1/helpers.zig +592 -35
- package/src/lib/gen1/mechanics.zig +804 -500
- 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 +73 -38
- package/src/lib/gen2/helpers.zig +15 -16
- package/src/lib/gen2/mechanics.zig +476 -11
- package/src/lib/pkmn.zig +61 -2
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,6 +16,8 @@ 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;
|
|
@@ -31,12 +36,17 @@ pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
|
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,
|
|
99
|
-
|
|
110
|
+
conditions: Conditions = .{},
|
|
111
|
+
// NB: wCurPlayerMove / wCurEnemyMove (?)
|
|
100
112
|
_: u8 = 0,
|
|
113
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
114
|
+
last_used_move: Move = .None,
|
|
101
115
|
|
|
102
116
|
const Conditions = packed struct {
|
|
103
117
|
Spikes: bool = false,
|
|
@@ -118,41 +132,57 @@ 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
|
+
}
|
|
153
|
+
|
|
154
|
+
// TODO
|
|
155
|
+
// pub inline fn useMove(self: anytype) void {
|
|
156
|
+
// assert(side.last_selected_move != .None);
|
|
157
|
+
// self.last_used_move = side.last_selected_move;
|
|
158
|
+
// self.active.volatiles.dirty = false;
|
|
159
|
+
// }
|
|
131
160
|
};
|
|
132
161
|
|
|
133
162
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
134
|
-
const ActivePokemon = extern struct {
|
|
135
|
-
volatiles:
|
|
163
|
+
pub const ActivePokemon = extern struct {
|
|
164
|
+
volatiles: Volatiles align(4) = .{},
|
|
136
165
|
stats: Stats(u16) = .{},
|
|
137
166
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
138
167
|
boosts: Boosts = .{},
|
|
168
|
+
types: Types = .{},
|
|
139
169
|
species: Species = .None,
|
|
140
|
-
|
|
141
|
-
last_move: Move = .None,
|
|
170
|
+
|
|
142
171
|
_: u8 = 0,
|
|
143
172
|
|
|
144
173
|
comptime {
|
|
145
174
|
assert(@sizeOf(ActivePokemon) == 44);
|
|
146
175
|
}
|
|
147
176
|
|
|
148
|
-
pub inline fn move(self:
|
|
177
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
178
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
149
179
|
assert(mslot > 0 and mslot <= 4);
|
|
150
180
|
assert(self.moves[mslot - 1].id != .None);
|
|
151
181
|
return &self.moves[mslot - 1];
|
|
152
182
|
}
|
|
153
183
|
};
|
|
154
184
|
|
|
155
|
-
const Pokemon = extern struct {
|
|
185
|
+
pub const Pokemon = extern struct {
|
|
156
186
|
stats: Stats(u16) = .{},
|
|
157
187
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
158
188
|
types: Types = .{},
|
|
@@ -169,7 +199,8 @@ const Pokemon = extern struct {
|
|
|
169
199
|
assert(@sizeOf(Pokemon) == 32);
|
|
170
200
|
}
|
|
171
201
|
|
|
172
|
-
pub inline fn move(self:
|
|
202
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
203
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
173
204
|
assert(mslot > 0 and mslot <= 4);
|
|
174
205
|
assert(self.moves[mslot - 1].id != .None);
|
|
175
206
|
return &self.moves[mslot - 1];
|
|
@@ -182,7 +213,7 @@ pub const Gender = enum(u2) {
|
|
|
182
213
|
Unknown,
|
|
183
214
|
};
|
|
184
215
|
|
|
185
|
-
const DVs = packed struct {
|
|
216
|
+
pub const DVs = packed struct {
|
|
186
217
|
gender: Gender = .Male,
|
|
187
218
|
pow: u6 = 40,
|
|
188
219
|
type: Type = .Dark,
|
|
@@ -205,11 +236,11 @@ const DVs = packed struct {
|
|
|
205
236
|
0xFF => .Unknown,
|
|
206
237
|
else => if ((@as(u8, dvs.atk) << 4 | dvs.spe) < ratio) Gender.Female else Gender.Male,
|
|
207
238
|
};
|
|
208
|
-
const p = @intCast(
|
|
239
|
+
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
209
240
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
210
241
|
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 = @
|
|
242
|
+
if (t >= @intFromEnum(Type.@"???")) t = t + 1;
|
|
243
|
+
return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
|
|
213
244
|
}
|
|
214
245
|
|
|
215
246
|
comptime {
|
|
@@ -238,7 +269,7 @@ test DVs {
|
|
|
238
269
|
try expectEqual(Gender.Female, dvs.gender);
|
|
239
270
|
}
|
|
240
271
|
|
|
241
|
-
const MoveSlot = packed struct {
|
|
272
|
+
pub const MoveSlot = packed struct {
|
|
242
273
|
id: Move = .None,
|
|
243
274
|
pp_ups: u2 = 3,
|
|
244
275
|
pp: u6 = 0,
|
|
@@ -250,7 +281,7 @@ const MoveSlot = packed struct {
|
|
|
250
281
|
|
|
251
282
|
pub const Status = gen1.Status;
|
|
252
283
|
|
|
253
|
-
const
|
|
284
|
+
pub const Volatiles = packed struct {
|
|
254
285
|
Bide: bool = false,
|
|
255
286
|
Thrashing: bool = false,
|
|
256
287
|
Flinch: bool = false,
|
|
@@ -284,8 +315,9 @@ const Volatile = packed struct {
|
|
|
284
315
|
trapped: bool = false,
|
|
285
316
|
switched: bool = false,
|
|
286
317
|
switching: bool = false,
|
|
318
|
+
dirty: bool = false,
|
|
287
319
|
|
|
288
|
-
_:
|
|
320
|
+
_: u4 = 0,
|
|
289
321
|
|
|
290
322
|
bind: u4 = 0,
|
|
291
323
|
future_sight: FutureSight = .{},
|
|
@@ -296,10 +328,11 @@ const Volatile = packed struct {
|
|
|
296
328
|
toxic: u8 = 0,
|
|
297
329
|
confusion: u4 = 0,
|
|
298
330
|
encore: u4 = 0,
|
|
331
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
299
332
|
fury_cutter: u4 = 0,
|
|
300
333
|
perish_song: u4 = 0,
|
|
301
334
|
protect: u4 = 0,
|
|
302
|
-
|
|
335
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
303
336
|
|
|
304
337
|
const Disabled = packed struct {
|
|
305
338
|
move: u4 = 0,
|
|
@@ -312,7 +345,7 @@ const Volatile = packed struct {
|
|
|
312
345
|
};
|
|
313
346
|
|
|
314
347
|
comptime {
|
|
315
|
-
assert(@sizeOf(
|
|
348
|
+
assert(@sizeOf(Volatiles) == 16);
|
|
316
349
|
}
|
|
317
350
|
};
|
|
318
351
|
|
|
@@ -327,10 +360,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
327
360
|
|
|
328
361
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
329
362
|
assert(level > 0 and level <= 100);
|
|
330
|
-
const
|
|
363
|
+
const float: f32 = @floatFromInt(exp);
|
|
364
|
+
const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
331
365
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
332
366
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
333
|
-
return @intCast(
|
|
367
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
334
368
|
}
|
|
335
369
|
};
|
|
336
370
|
}
|
|
@@ -390,17 +424,18 @@ test Item {
|
|
|
390
424
|
pub const Move = moves.Move;
|
|
391
425
|
|
|
392
426
|
test Move {
|
|
393
|
-
try expectEqual(251, @
|
|
427
|
+
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
394
428
|
const move = Move.get(.DynamicPunch);
|
|
395
429
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
396
430
|
try expectEqual(@as(u8, 50), move.accuracy);
|
|
397
431
|
try expectEqual(@as(u8, 5), move.pp);
|
|
432
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
398
433
|
}
|
|
399
434
|
|
|
400
435
|
pub const Species = species.Species;
|
|
401
436
|
|
|
402
437
|
test Species {
|
|
403
|
-
try expectEqual(152, @
|
|
438
|
+
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
404
439
|
try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
|
|
405
440
|
}
|
|
406
441
|
|
|
@@ -409,7 +444,7 @@ pub const Types = types.Types;
|
|
|
409
444
|
pub const Effectiveness = gen1.Effectiveness;
|
|
410
445
|
|
|
411
446
|
test Types {
|
|
412
|
-
try expectEqual(13, @
|
|
447
|
+
try expectEqual(13, @intFromEnum(Type.Electric));
|
|
413
448
|
|
|
414
449
|
try expect(!Type.Steel.special());
|
|
415
450
|
try expect(Type.Dark.special());
|