@pkmn/engine 0.1.0-dev.46307385 → 0.1.0-dev.4cc7c5c2
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/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +2 -2
- package/build/pkg/protocol.js +56 -37
- package/build/pkg/protocol.js.map +1 -1
- package/build.zig +7 -3
- package/package.json +20 -11
- package/src/lib/binding/node.zig +1 -1
- package/src/lib/common/data.zig +4 -16
- package/src/lib/common/optional.zig +8 -1
- package/src/lib/common/protocol.zig +518 -66
- package/src/lib/common/rational.zig +1 -1
- package/src/lib/common/rng.zig +6 -6
- package/src/lib/gen1/README.md +3 -1
- package/src/lib/gen1/calc.zig +2 -3
- package/src/lib/gen1/chance.zig +51 -35
- package/src/lib/gen1/data/moves.zig +182 -170
- package/src/lib/gen1/data/types.zig +2 -2
- package/src/lib/gen1/data.zig +11 -23
- package/src/lib/gen1/helpers.zig +13 -28
- package/src/lib/gen1/mechanics.zig +43 -52
- package/src/lib/gen2/README.md +94 -72
- package/src/lib/gen2/data/items.zig +10 -16
- package/src/lib/gen2/data/moves.zig +659 -565
- package/src/lib/gen2/data/types.zig +127 -25
- package/src/lib/gen2/data.zig +100 -63
- package/src/lib/gen2/helpers.zig +8 -12
- package/src/lib/gen2/mechanics.zig +2589 -7
|
@@ -14,15 +14,15 @@ const I = Effectiveness.Immune;
|
|
|
14
14
|
|
|
15
15
|
/// Representation of a Generation II type in Pokémon.
|
|
16
16
|
pub const Type = enum(u8) {
|
|
17
|
+
Ground,
|
|
18
|
+
Rock,
|
|
19
|
+
Steel,
|
|
17
20
|
Normal,
|
|
18
21
|
Fighting,
|
|
19
22
|
Flying,
|
|
20
23
|
Poison,
|
|
21
|
-
Ground,
|
|
22
|
-
Rock,
|
|
23
24
|
Bug,
|
|
24
25
|
Ghost,
|
|
25
|
-
Steel,
|
|
26
26
|
@"???",
|
|
27
27
|
Fire,
|
|
28
28
|
Water,
|
|
@@ -33,37 +33,37 @@ pub const Type = enum(u8) {
|
|
|
33
33
|
Dragon,
|
|
34
34
|
Dark,
|
|
35
35
|
|
|
36
|
-
const CHART
|
|
37
|
-
[_]Effectiveness{ N,
|
|
38
|
-
[_]Effectiveness{
|
|
39
|
-
[_]Effectiveness{ N, S,
|
|
40
|
-
[_]Effectiveness{ N,
|
|
41
|
-
[_]Effectiveness{ N,
|
|
42
|
-
[_]Effectiveness{ N, R,
|
|
43
|
-
[_]Effectiveness{
|
|
44
|
-
[_]Effectiveness{
|
|
45
|
-
[_]Effectiveness{ N, N,
|
|
36
|
+
const CHART: [18][18]Effectiveness = .{
|
|
37
|
+
[_]Effectiveness{ N, S, S, N, N, I, S, R, N, N, S, N, R, S, N, N, N, N }, // Ground
|
|
38
|
+
[_]Effectiveness{ R, N, R, N, R, S, N, S, N, N, S, N, N, N, N, S, N, N }, // Rock
|
|
39
|
+
[_]Effectiveness{ N, S, R, N, N, N, N, N, N, N, R, R, N, R, N, S, N, N }, // Steel
|
|
40
|
+
[_]Effectiveness{ N, R, R, N, N, N, N, N, I, N, N, N, N, N, N, N, N, N }, // Normal
|
|
41
|
+
[_]Effectiveness{ N, S, S, S, N, R, R, R, I, N, N, N, N, N, R, S, N, S }, // Fighting
|
|
42
|
+
[_]Effectiveness{ N, R, R, N, S, N, N, S, N, N, N, N, S, R, N, N, N, N }, // Flying
|
|
43
|
+
[_]Effectiveness{ R, R, I, N, N, N, R, N, R, N, N, N, S, N, N, N, N, N }, // Poison
|
|
44
|
+
[_]Effectiveness{ N, N, R, N, R, R, R, N, R, N, R, N, S, N, S, N, N, S }, // Bug
|
|
45
|
+
[_]Effectiveness{ N, N, R, I, N, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Ghost
|
|
46
46
|
[_]Effectiveness{ N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N }, // ???
|
|
47
|
-
[_]Effectiveness{ N,
|
|
48
|
-
[_]Effectiveness{
|
|
49
|
-
[_]Effectiveness{
|
|
50
|
-
[_]Effectiveness{
|
|
51
|
-
[_]Effectiveness{ N,
|
|
52
|
-
[_]Effectiveness{
|
|
53
|
-
[_]Effectiveness{ N, N,
|
|
54
|
-
[_]Effectiveness{ N, R, N,
|
|
47
|
+
[_]Effectiveness{ N, R, S, N, N, N, N, S, N, N, R, R, S, N, N, S, R, N }, // Fire
|
|
48
|
+
[_]Effectiveness{ S, S, N, N, N, N, N, N, N, N, S, R, R, N, N, N, R, N }, // Water
|
|
49
|
+
[_]Effectiveness{ S, S, R, N, N, R, R, R, N, N, R, S, R, N, N, N, R, N }, // Grass
|
|
50
|
+
[_]Effectiveness{ I, N, N, N, N, S, N, N, N, N, N, S, R, R, N, N, R, N }, // Electric
|
|
51
|
+
[_]Effectiveness{ N, N, R, N, S, N, S, N, N, N, N, N, N, N, R, N, N, I }, // Psychic
|
|
52
|
+
[_]Effectiveness{ S, N, R, N, N, S, N, N, N, N, R, R, S, N, N, R, S, N }, // Ice
|
|
53
|
+
[_]Effectiveness{ N, N, R, N, N, N, N, N, N, N, N, N, N, N, N, N, S, N }, // Dragon
|
|
54
|
+
[_]Effectiveness{ N, N, R, N, R, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Dark
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
const PRECEDENCE = [_]u8{
|
|
58
|
+
9, // Ground
|
|
59
|
+
13, // Rock
|
|
60
|
+
17, // Steel
|
|
58
61
|
1, // Normal
|
|
59
62
|
7, // Fighting
|
|
60
63
|
10, // Flying
|
|
61
64
|
8, // Poison
|
|
62
|
-
9, // Ground
|
|
63
|
-
13, // Rock
|
|
64
65
|
12, // Bug
|
|
65
66
|
14, // Ghost
|
|
66
|
-
17, // Steel
|
|
67
67
|
0, // ???
|
|
68
68
|
2, // Fire
|
|
69
69
|
3, // Water
|
|
@@ -75,10 +75,89 @@ pub const Type = enum(u8) {
|
|
|
75
75
|
16, // Dark
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
const INDEXES = [_]u8{
|
|
79
|
+
4, // Ground
|
|
80
|
+
5, // Rock
|
|
81
|
+
9, // Steel
|
|
82
|
+
0, // Normal
|
|
83
|
+
1, // Fighting
|
|
84
|
+
2, // Flying
|
|
85
|
+
3, // Poison
|
|
86
|
+
7, // Bug
|
|
87
|
+
8, // Ghost
|
|
88
|
+
19, // ???
|
|
89
|
+
20, // Fire
|
|
90
|
+
21, // Water
|
|
91
|
+
22, // Grass
|
|
92
|
+
23, // Electric
|
|
93
|
+
24, // Psychic
|
|
94
|
+
25, // Ice
|
|
95
|
+
26, // Dragon
|
|
96
|
+
27, // Dark
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const CONVERSION_2 = [_]Type{
|
|
100
|
+
.Normal,
|
|
101
|
+
.Fighting,
|
|
102
|
+
.Flying,
|
|
103
|
+
.Poison,
|
|
104
|
+
.Ground,
|
|
105
|
+
.Rock,
|
|
106
|
+
.@"???", // placeholder
|
|
107
|
+
.Bug,
|
|
108
|
+
.Ghost,
|
|
109
|
+
.Steel,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const HIDDEN_POWER = [_]Type{
|
|
113
|
+
.Fighting,
|
|
114
|
+
.Flying,
|
|
115
|
+
.Poison,
|
|
116
|
+
.Ground,
|
|
117
|
+
.Rock,
|
|
118
|
+
.Bug,
|
|
119
|
+
.Ghost,
|
|
120
|
+
.Steel,
|
|
121
|
+
.Fire,
|
|
122
|
+
.Water,
|
|
123
|
+
.Grass,
|
|
124
|
+
.Electric,
|
|
125
|
+
.Psychic,
|
|
126
|
+
.Ice,
|
|
127
|
+
.Dragon,
|
|
128
|
+
.Dark,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/// Order of Pokémon Showdown's types.
|
|
132
|
+
pub const SHOWDOWN = [_]Type{
|
|
133
|
+
.Fire,
|
|
134
|
+
.Ice,
|
|
135
|
+
.Steel,
|
|
136
|
+
.Electric,
|
|
137
|
+
.Ghost,
|
|
138
|
+
.Grass,
|
|
139
|
+
.Dark,
|
|
140
|
+
.Bug,
|
|
141
|
+
.Dragon,
|
|
142
|
+
.Fighting,
|
|
143
|
+
.Flying,
|
|
144
|
+
.Ground,
|
|
145
|
+
.Normal,
|
|
146
|
+
.Poison,
|
|
147
|
+
.Psychic,
|
|
148
|
+
.Rock,
|
|
149
|
+
.Water,
|
|
150
|
+
.@"???",
|
|
151
|
+
};
|
|
152
|
+
|
|
78
153
|
comptime {
|
|
79
154
|
assert(@bitSizeOf(Type) == 8);
|
|
80
155
|
assert(@sizeOf(@TypeOf(CHART)) == 324);
|
|
81
156
|
assert(@sizeOf(@TypeOf(PRECEDENCE)) == 18);
|
|
157
|
+
assert(@sizeOf(@TypeOf(INDEXES)) == 18);
|
|
158
|
+
assert(@sizeOf(@TypeOf(CONVERSION_2)) == 10);
|
|
159
|
+
assert(@sizeOf(@TypeOf(HIDDEN_POWER)) == 16);
|
|
160
|
+
assert(@sizeOf(@TypeOf(SHOWDOWN)) == 18);
|
|
82
161
|
}
|
|
83
162
|
|
|
84
163
|
/// The number of types in this generation.
|
|
@@ -94,10 +173,28 @@ pub const Type = enum(u8) {
|
|
|
94
173
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
95
174
|
}
|
|
96
175
|
|
|
97
|
-
/// The precedence order of
|
|
176
|
+
/// The precedence order of Type `type`.
|
|
98
177
|
pub inline fn precedence(self: Type) u8 {
|
|
99
178
|
return PRECEDENCE[@intFromEnum(self)];
|
|
100
179
|
}
|
|
180
|
+
|
|
181
|
+
/// The internal index of this Type used by Present.
|
|
182
|
+
pub inline fn present(self: Type) u8 {
|
|
183
|
+
return INDEXES[@intFromEnum(self)];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/// The Type corresponding to a random roll of `num` for Conversion 2.
|
|
187
|
+
pub inline fn conversion2(num: u8) Type {
|
|
188
|
+
assert(num != 6);
|
|
189
|
+
assert(num < 10 or num >= 20);
|
|
190
|
+
assert(num <= 27);
|
|
191
|
+
return if (num < 10) CONVERSION_2[num] else @enumFromInt(num - 10);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// The Type corresponding to a Hidden Power `index`.
|
|
195
|
+
pub inline fn hiddenPower(index: u8) Type {
|
|
196
|
+
return HIDDEN_POWER[index];
|
|
197
|
+
}
|
|
101
198
|
};
|
|
102
199
|
|
|
103
200
|
/// Representation of a Pokémon's typing.
|
|
@@ -120,4 +217,9 @@ pub const Types = extern struct {
|
|
|
120
217
|
pub inline fn includes(self: Types, t: Type) bool {
|
|
121
218
|
return self.type1 == t or self.type2 == t;
|
|
122
219
|
}
|
|
220
|
+
|
|
221
|
+
/// Whether this typing is immune to damage from Sandstorm.
|
|
222
|
+
pub inline fn sandstormImmune(self: Types) u8 {
|
|
223
|
+
return self.type1 <= @intFromEnum(Type.Steel) or self.type2 <= @intFromEnum(Type.Steel);
|
|
224
|
+
}
|
|
123
225
|
};
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -5,6 +5,7 @@ const pkmn = @import("../pkmn.zig");
|
|
|
5
5
|
|
|
6
6
|
const data = @import("../common/data.zig");
|
|
7
7
|
const DEBUG = @import("../common/debug.zig").print;
|
|
8
|
+
const protocol = @import("../common/protocol.zig");
|
|
8
9
|
const rng = @import("../common/rng.zig");
|
|
9
10
|
const util = @import("../common/util.zig");
|
|
10
11
|
|
|
@@ -15,6 +16,8 @@ const types = @import("data/types.zig");
|
|
|
15
16
|
|
|
16
17
|
const gen1 = @import("../gen1/data.zig");
|
|
17
18
|
|
|
19
|
+
const calc = @import("calc.zig");
|
|
20
|
+
const chance = @import("chance.zig");
|
|
18
21
|
const mechanics = @import("mechanics.zig");
|
|
19
22
|
|
|
20
23
|
const assert = std.debug.assert;
|
|
@@ -33,6 +36,9 @@ pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
|
33
36
|
else
|
|
34
37
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
35
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
|
+
|
|
36
42
|
const Choice = data.Choice;
|
|
37
43
|
const ID = data.ID;
|
|
38
44
|
const Player = data.Player;
|
|
@@ -45,6 +51,7 @@ const showdown = pkmn.options.showdown;
|
|
|
45
51
|
pub const PRNG = rng.PRNG(2);
|
|
46
52
|
|
|
47
53
|
pub fn Battle(comptime RNG: anytype) type {
|
|
54
|
+
const padding = if (!showdown) 3 else 5;
|
|
48
55
|
return extern struct {
|
|
49
56
|
const Self = @This();
|
|
50
57
|
|
|
@@ -53,6 +60,8 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
53
60
|
turn: u16 = 0,
|
|
54
61
|
field: Field = .{},
|
|
55
62
|
|
|
63
|
+
_: [padding]u8 = .{0} ** padding,
|
|
64
|
+
|
|
56
65
|
pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
57
66
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
58
67
|
return &self.sides[@intFromEnum(player)];
|
|
@@ -77,35 +86,32 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
77
86
|
};
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
// TODO
|
|
81
89
|
test Battle {
|
|
82
|
-
try expectEqual(
|
|
90
|
+
try expectEqual(512, @sizeOf(Battle(PRNG)));
|
|
83
91
|
}
|
|
84
92
|
|
|
85
|
-
const Field = packed struct {
|
|
93
|
+
pub const Field = packed struct(u8) {
|
|
86
94
|
weather: Weather = .None,
|
|
87
95
|
weather_duration: u4 = 0,
|
|
88
|
-
|
|
89
|
-
comptime {
|
|
90
|
-
assert(@sizeOf(Field) == 1);
|
|
91
|
-
}
|
|
92
96
|
};
|
|
93
97
|
|
|
94
|
-
const Weather = enum(u4) {
|
|
98
|
+
pub const Weather = enum(u4) {
|
|
95
99
|
None,
|
|
96
100
|
Rain,
|
|
97
101
|
Sun,
|
|
98
102
|
Sandstorm,
|
|
99
103
|
};
|
|
100
104
|
|
|
101
|
-
const Side = extern struct {
|
|
105
|
+
pub const Side = extern struct {
|
|
102
106
|
pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
|
|
103
|
-
active: ActivePokemon = .{},
|
|
104
|
-
conditions: Conditions,
|
|
107
|
+
active: ActivePokemon align(8) = .{},
|
|
108
|
+
conditions: Conditions = .{},
|
|
109
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
105
110
|
last_used_move: Move = .None,
|
|
106
|
-
_: u8 = 0,
|
|
107
111
|
|
|
108
|
-
|
|
112
|
+
_: [7]u8 = .{0} ** 7,
|
|
113
|
+
|
|
114
|
+
const Conditions = packed struct(u32) {
|
|
109
115
|
Spikes: bool = false,
|
|
110
116
|
Safeguard: bool = false,
|
|
111
117
|
LightScreen: bool = false,
|
|
@@ -115,13 +121,16 @@ const Side = extern struct {
|
|
|
115
121
|
light_screen: u4 = 0,
|
|
116
122
|
reflect: u4 = 0,
|
|
117
123
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
future_sight: FutureSight = .{},
|
|
125
|
+
|
|
126
|
+
const FutureSight = packed struct(u16) {
|
|
127
|
+
damage: u12 = 0,
|
|
128
|
+
count: u4 = 0,
|
|
129
|
+
};
|
|
121
130
|
};
|
|
122
131
|
|
|
123
132
|
comptime {
|
|
124
|
-
assert(@sizeOf(Side) ==
|
|
133
|
+
assert(@sizeOf(Side) == 248);
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
@@ -136,17 +145,30 @@ const Side = extern struct {
|
|
|
136
145
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
137
146
|
return self.get(1);
|
|
138
147
|
}
|
|
148
|
+
|
|
149
|
+
pub inline fn lastMove(self: anytype, encore: bool) Move {
|
|
150
|
+
assert(self.last_used_move != .None or !self.active.volatiles.dirty);
|
|
151
|
+
if (encore) return self.last_used_move;
|
|
152
|
+
return if (self.active.volatiles.dirty) .None else self.last_used_move;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// TODO
|
|
156
|
+
// pub inline fn useMove(self: anytype) void {
|
|
157
|
+
// assert(side.last_selected_move != .None);
|
|
158
|
+
// self.last_used_move = side.last_selected_move;
|
|
159
|
+
// self.active.volatiles.dirty = false;
|
|
160
|
+
// }
|
|
139
161
|
};
|
|
140
162
|
|
|
141
163
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
142
|
-
const ActivePokemon = extern struct {
|
|
143
|
-
volatiles:
|
|
164
|
+
pub const ActivePokemon = extern struct {
|
|
165
|
+
volatiles: Volatiles align(4) = .{},
|
|
144
166
|
stats: Stats(u16) = .{},
|
|
145
167
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
146
168
|
boosts: Boosts = .{},
|
|
169
|
+
types: Types = .{},
|
|
147
170
|
species: Species = .None,
|
|
148
|
-
|
|
149
|
-
last_move: Move = .None,
|
|
171
|
+
|
|
150
172
|
_: u8 = 0,
|
|
151
173
|
|
|
152
174
|
comptime {
|
|
@@ -161,7 +183,7 @@ const ActivePokemon = extern struct {
|
|
|
161
183
|
}
|
|
162
184
|
};
|
|
163
185
|
|
|
164
|
-
const Pokemon = extern struct {
|
|
186
|
+
pub const Pokemon = extern struct {
|
|
165
187
|
stats: Stats(u16) = .{},
|
|
166
188
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
167
189
|
types: Types = .{},
|
|
@@ -192,7 +214,7 @@ pub const Gender = enum(u2) {
|
|
|
192
214
|
Unknown,
|
|
193
215
|
};
|
|
194
216
|
|
|
195
|
-
const DVs = packed struct {
|
|
217
|
+
pub const DVs = packed struct(u16) {
|
|
196
218
|
gender: Gender = .Male,
|
|
197
219
|
pow: u6 = 40,
|
|
198
220
|
type: Type = .Dark,
|
|
@@ -200,7 +222,7 @@ const DVs = packed struct {
|
|
|
200
222
|
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
201
223
|
assert(p >= 1 and p <= 40);
|
|
202
224
|
assert(t != .Normal and t != .@"???");
|
|
203
|
-
return
|
|
225
|
+
return .{ .gender = g, .pow = p, .type = t };
|
|
204
226
|
}
|
|
205
227
|
|
|
206
228
|
pub inline fn power(dvs: DVs) u8 {
|
|
@@ -217,13 +239,8 @@ const DVs = packed struct {
|
|
|
217
239
|
};
|
|
218
240
|
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
219
241
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
220
|
-
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2)
|
|
221
|
-
|
|
222
|
-
return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
comptime {
|
|
226
|
-
assert(@sizeOf(DVs) == 2);
|
|
242
|
+
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
|
|
243
|
+
return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
|
|
227
244
|
}
|
|
228
245
|
};
|
|
229
246
|
|
|
@@ -248,10 +265,12 @@ test DVs {
|
|
|
248
265
|
try expectEqual(Gender.Female, dvs.gender);
|
|
249
266
|
}
|
|
250
267
|
|
|
251
|
-
|
|
268
|
+
/// Representation of a Generation II Pokémon's move slot in a battle.
|
|
269
|
+
pub const MoveSlot = extern struct {
|
|
270
|
+
/// The identifier for the move.
|
|
252
271
|
id: Move = .None,
|
|
253
|
-
|
|
254
|
-
pp:
|
|
272
|
+
/// The remaining PP of the move.
|
|
273
|
+
pp: u8 = 0,
|
|
255
274
|
|
|
256
275
|
comptime {
|
|
257
276
|
assert(@sizeOf(MoveSlot) == 2);
|
|
@@ -260,14 +279,34 @@ const MoveSlot = packed struct {
|
|
|
260
279
|
|
|
261
280
|
pub const Status = gen1.Status;
|
|
262
281
|
|
|
263
|
-
|
|
282
|
+
/// TODO
|
|
283
|
+
pub const TriAttack = enum(u2) {
|
|
284
|
+
PAR,
|
|
285
|
+
FRZ,
|
|
286
|
+
BRN,
|
|
287
|
+
|
|
288
|
+
/// TODO
|
|
289
|
+
pub inline fn status(self: TriAttack) u8 {
|
|
290
|
+
return Status.init(@enumFromInt(6 - @as(u8, @intFromEnum(self))));
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
test TriAttack {
|
|
295
|
+
try expectEqual(Status.init(.PAR), TriAttack.PAR.status());
|
|
296
|
+
try expectEqual(Status.init(.FRZ), TriAttack.FRZ.status());
|
|
297
|
+
try expectEqual(Status.init(.BRN), TriAttack.BRN.status());
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
pub const Volatiles = packed struct(u128) {
|
|
264
301
|
Bide: bool = false,
|
|
265
302
|
Thrashing: bool = false,
|
|
303
|
+
BeatUp: bool = false,
|
|
266
304
|
Flinch: bool = false,
|
|
267
305
|
Charging: bool = false,
|
|
268
306
|
Underground: bool = false,
|
|
269
307
|
Flying: bool = false,
|
|
270
308
|
Confusion: bool = false,
|
|
309
|
+
|
|
271
310
|
Mist: bool = false,
|
|
272
311
|
FocusEnergy: bool = false,
|
|
273
312
|
Substitute: bool = false,
|
|
@@ -278,52 +317,50 @@ const Volatile = packed struct {
|
|
|
278
317
|
Transform: bool = false,
|
|
279
318
|
|
|
280
319
|
Nightmare: bool = false,
|
|
281
|
-
|
|
320
|
+
Curse: bool = false,
|
|
282
321
|
Protect: bool = false,
|
|
283
322
|
Foresight: bool = false,
|
|
284
323
|
PerishSong: bool = false,
|
|
285
324
|
Endure: bool = false,
|
|
286
325
|
Rollout: bool = false,
|
|
287
326
|
Attract: bool = false,
|
|
327
|
+
|
|
288
328
|
DefenseCurl: bool = false,
|
|
289
329
|
Encore: bool = false,
|
|
290
330
|
LockOn: bool = false,
|
|
291
331
|
DestinyBond: bool = false,
|
|
292
|
-
BeatUp: bool = false,
|
|
293
332
|
|
|
294
333
|
trapped: bool = false,
|
|
295
334
|
switched: bool = false,
|
|
296
335
|
switching: bool = false,
|
|
336
|
+
dirty: bool = false,
|
|
297
337
|
|
|
298
|
-
|
|
338
|
+
frozen: bool = false,
|
|
339
|
+
_: u15 = 0,
|
|
299
340
|
|
|
300
|
-
bind: u4 = 0,
|
|
301
|
-
future_sight: FutureSight = .{},
|
|
302
341
|
bide: u16 = 0,
|
|
342
|
+
bind: Bind = .{},
|
|
303
343
|
disabled: Disabled = .{},
|
|
304
344
|
rage: u8 = 0,
|
|
305
345
|
substitute: u8 = 0,
|
|
306
346
|
toxic: u8 = 0,
|
|
307
347
|
confusion: u4 = 0,
|
|
308
348
|
encore: u4 = 0,
|
|
349
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
309
350
|
fury_cutter: u4 = 0,
|
|
310
351
|
perish_song: u4 = 0,
|
|
311
352
|
protect: u4 = 0,
|
|
312
|
-
|
|
353
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
313
354
|
|
|
314
|
-
const
|
|
315
|
-
move: u4 = 0,
|
|
355
|
+
const Bind = packed struct(u8) {
|
|
316
356
|
duration: u4 = 0,
|
|
357
|
+
reason: u4 = 0,
|
|
317
358
|
};
|
|
318
359
|
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
360
|
+
const Disabled = packed struct(u8) {
|
|
361
|
+
move: u4 = 0,
|
|
362
|
+
duration: u4 = 0,
|
|
322
363
|
};
|
|
323
|
-
|
|
324
|
-
comptime {
|
|
325
|
-
assert(@sizeOf(Volatile) == 16);
|
|
326
|
-
}
|
|
327
364
|
};
|
|
328
365
|
|
|
329
366
|
pub fn Stats(comptime T: type) type {
|
|
@@ -358,7 +395,7 @@ test Stats {
|
|
|
358
395
|
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
359
396
|
}
|
|
360
397
|
|
|
361
|
-
pub const Boosts = packed struct {
|
|
398
|
+
pub const Boosts = packed struct(u32) {
|
|
362
399
|
atk: i4 = 0,
|
|
363
400
|
def: i4 = 0,
|
|
364
401
|
spe: i4 = 0,
|
|
@@ -374,7 +411,7 @@ pub const Boosts = packed struct {
|
|
|
374
411
|
};
|
|
375
412
|
|
|
376
413
|
test Boosts {
|
|
377
|
-
const boosts =
|
|
414
|
+
const boosts: Boosts = .{ .spd = -6 };
|
|
378
415
|
try expectEqual(0, boosts.atk);
|
|
379
416
|
try expectEqual(-6, boosts.spd);
|
|
380
417
|
}
|
|
@@ -387,15 +424,10 @@ test Item {
|
|
|
387
424
|
try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
|
|
388
425
|
try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
|
|
389
426
|
|
|
390
|
-
try expect(!Item.
|
|
391
|
-
try expect(Item.
|
|
392
|
-
try expect(Item.
|
|
393
|
-
try expect(!Item.
|
|
394
|
-
|
|
395
|
-
try expect(!Item.berry(.LightBall));
|
|
396
|
-
try expect(Item.berry(.PSNCureBerry));
|
|
397
|
-
try expect(Item.berry(.GoldBerry));
|
|
398
|
-
try expect(!Item.berry(.MasterBall));
|
|
427
|
+
try expect(!Item.isMail(.UpGrade));
|
|
428
|
+
try expect(Item.isMail(.FlowerMail));
|
|
429
|
+
try expect(Item.isMail(.MirageMail));
|
|
430
|
+
try expect(!Item.isMail(.TownMap));
|
|
399
431
|
}
|
|
400
432
|
|
|
401
433
|
pub const Move = moves.Move;
|
|
@@ -404,8 +436,13 @@ test Move {
|
|
|
404
436
|
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
405
437
|
const move = Move.get(.DynamicPunch);
|
|
406
438
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
407
|
-
try expectEqual(
|
|
408
|
-
try expectEqual(
|
|
439
|
+
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
440
|
+
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
441
|
+
try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
|
|
442
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
443
|
+
try expect(!move.effect.isHighCritical());
|
|
444
|
+
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
445
|
+
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
409
446
|
}
|
|
410
447
|
|
|
411
448
|
pub const Species = species.Species;
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -48,7 +48,7 @@ pub const Battle = struct {
|
|
|
48
48
|
comptime rolls: anytype,
|
|
49
49
|
p1: []const Pokemon,
|
|
50
50
|
p2: []const Pokemon,
|
|
51
|
-
) data.Battle(rng.FixedRNG(
|
|
51
|
+
) data.Battle(rng.FixedRNG(2, rolls.len)) {
|
|
52
52
|
return .{
|
|
53
53
|
.rng = .{ .rolls = rolls },
|
|
54
54
|
.sides = .{ Side.init(p1), Side.init(p2) },
|
|
@@ -87,7 +87,7 @@ pub const Side = struct {
|
|
|
87
87
|
|
|
88
88
|
for (0..ps.len) |i| {
|
|
89
89
|
side.pokemon[i] = Pokemon.init(ps[i]);
|
|
90
|
-
|
|
90
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
91
91
|
}
|
|
92
92
|
return side;
|
|
93
93
|
}
|
|
@@ -98,7 +98,7 @@ pub const Side = struct {
|
|
|
98
98
|
|
|
99
99
|
for (0..n) |i| {
|
|
100
100
|
side.pokemon[i] = Pokemon.random(rand, opt);
|
|
101
|
-
side.pokemon[i].position = i + 1;
|
|
101
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
return side;
|
|
@@ -135,12 +135,9 @@ pub const Pokemon = struct {
|
|
|
135
135
|
}
|
|
136
136
|
assert(p.moves.len > 0 and p.moves.len <= 4);
|
|
137
137
|
for (p.moves, 0..) |m, j| {
|
|
138
|
-
pokemon.moves[j] =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
142
|
-
.pp = @intCast(@min(Move.pp(m) / 5 * 8, 61)),
|
|
143
|
-
};
|
|
138
|
+
pokemon.moves[j].id = m;
|
|
139
|
+
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
140
|
+
pokemon.moves[j].pp = @intCast(@min(Move.pp(m) / 5 * 8, 61));
|
|
144
141
|
}
|
|
145
142
|
pokemon.item = p.item;
|
|
146
143
|
if (p.hp) |hp| {
|
|
@@ -151,7 +148,7 @@ pub const Pokemon = struct {
|
|
|
151
148
|
pokemon.status = p.status;
|
|
152
149
|
pokemon.types = species.types;
|
|
153
150
|
pokemon.level = p.level;
|
|
154
|
-
pokemon.dvs = DVs.from(p.dvs);
|
|
151
|
+
pokemon.dvs = DVs.from(p.species, p.dvs);
|
|
155
152
|
return pokemon;
|
|
156
153
|
}
|
|
157
154
|
|
|
@@ -192,14 +189,13 @@ pub const Pokemon = struct {
|
|
|
192
189
|
const max_pp: u8 = @intCast(Move.pp(m) + pp_ups * @min(Move.pp(m) / 5, 7));
|
|
193
190
|
ms[i] = .{
|
|
194
191
|
.id = m,
|
|
195
|
-
.pp_ups = pp_ups,
|
|
196
192
|
.pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
|
|
197
193
|
};
|
|
198
194
|
}
|
|
199
195
|
return .{
|
|
200
196
|
.species = s,
|
|
201
197
|
.types = species.types,
|
|
202
|
-
.dvs = DVs.from(dvs),
|
|
198
|
+
.dvs = DVs.from(s, dvs),
|
|
203
199
|
.level = lvl,
|
|
204
200
|
.stats = stats,
|
|
205
201
|
.hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),
|