@pkmn/engine 0.1.0-dev.02b7f810
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 +20 -0
- package/README.md +332 -0
- package/build/pkg/addon.d.ts +7 -0
- package/build/pkg/addon.js +111 -0
- package/build/pkg/addon.js.map +1 -0
- package/build/pkg/data/ids.json +240 -0
- package/build/pkg/data/layout.json +86 -0
- package/build/pkg/data/protocol.json +173 -0
- package/build/pkg/data.d.ts +91 -0
- package/build/pkg/data.js +150 -0
- package/build/pkg/data.js.map +1 -0
- package/build/pkg/gen1.d.ts +109 -0
- package/build/pkg/gen1.js +668 -0
- package/build/pkg/gen1.js.map +1 -0
- package/build/pkg/index.d.ts +383 -0
- package/build/pkg/index.js +122 -0
- package/build/pkg/index.js.map +1 -0
- package/build/pkg/protocol.d.ts +71 -0
- package/build/pkg/protocol.js +452 -0
- package/build/pkg/protocol.js.map +1 -0
- package/build/test/display.js +382 -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 +346 -0
- package/package.json +202 -0
- package/src/bin/install-pkmn-engine +391 -0
- package/src/bin/pkmn-debug +62 -0
- package/src/lib/binding/node.zig +229 -0
- package/src/lib/binding/wasm.zig +10 -0
- package/src/lib/common/data.zig +129 -0
- package/src/lib/common/debug.zig +64 -0
- package/src/lib/common/options.zig +40 -0
- package/src/lib/common/protocol.zig +1297 -0
- package/src/lib/common/rng.zig +234 -0
- package/src/lib/gen1/README.md +791 -0
- package/src/lib/gen1/data/moves.zig +1854 -0
- package/src/lib/gen1/data/species.zig +1105 -0
- package/src/lib/gen1/data/types.zig +131 -0
- package/src/lib/gen1/data.zig +620 -0
- package/src/lib/gen1/helpers.zig +234 -0
- package/src/lib/gen1/mechanics.zig +2819 -0
- package/src/lib/gen2/README.md +309 -0
- package/src/lib/gen2/data/items.zig +236 -0
- package/src/lib/gen2/data/moves.zig +2784 -0
- package/src/lib/gen2/data/species.zig +1798 -0
- package/src/lib/gen2/data/types.zig +123 -0
- package/src/lib/gen2/data.zig +425 -0
- package/src/lib/gen2/helpers.zig +225 -0
- package/src/lib/gen2/mechanics.zig +53 -0
- package/src/lib/pkmn.zig +54 -0
- package/src/test/display/pkmn.html.tmpl +368 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
//! Code generated by `tools/generate` - manual edits will be overwritten
|
|
2
|
+
|
|
3
|
+
const std = @import("std");
|
|
4
|
+
|
|
5
|
+
const gen1 = @import("../../gen1/data.zig");
|
|
6
|
+
|
|
7
|
+
const assert = std.debug.assert;
|
|
8
|
+
const Effectiveness = gen1.Effectiveness;
|
|
9
|
+
|
|
10
|
+
const S = Effectiveness.Super;
|
|
11
|
+
const N = Effectiveness.Neutral;
|
|
12
|
+
const R = Effectiveness.Resisted;
|
|
13
|
+
const I = Effectiveness.Immune;
|
|
14
|
+
|
|
15
|
+
/// Representation of a Generation II type in Pokémon.
|
|
16
|
+
pub const Type = enum(u8) {
|
|
17
|
+
Normal,
|
|
18
|
+
Fighting,
|
|
19
|
+
Flying,
|
|
20
|
+
Poison,
|
|
21
|
+
Ground,
|
|
22
|
+
Rock,
|
|
23
|
+
Bug,
|
|
24
|
+
Ghost,
|
|
25
|
+
Steel,
|
|
26
|
+
@"???",
|
|
27
|
+
Fire,
|
|
28
|
+
Water,
|
|
29
|
+
Grass,
|
|
30
|
+
Electric,
|
|
31
|
+
Psychic,
|
|
32
|
+
Ice,
|
|
33
|
+
Dragon,
|
|
34
|
+
Dark,
|
|
35
|
+
|
|
36
|
+
const CHART = [18][18]Effectiveness{
|
|
37
|
+
[_]Effectiveness{ N, N, N, N, N, R, N, I, R, N, N, N, N, N, N, N, N, N }, // Normal
|
|
38
|
+
[_]Effectiveness{ S, N, R, R, N, S, R, I, S, N, N, N, N, N, R, S, N, S }, // Fighting
|
|
39
|
+
[_]Effectiveness{ N, S, N, N, N, R, S, N, R, N, N, N, S, R, N, N, N, N }, // Flying
|
|
40
|
+
[_]Effectiveness{ N, N, N, R, R, R, N, R, I, N, N, N, S, N, N, N, N, N }, // Poison
|
|
41
|
+
[_]Effectiveness{ N, N, I, S, N, S, R, N, S, N, S, N, R, S, N, N, N, N }, // Ground
|
|
42
|
+
[_]Effectiveness{ N, R, S, N, R, N, S, N, R, N, S, N, N, N, N, S, N, N }, // Rock
|
|
43
|
+
[_]Effectiveness{ N, R, R, R, N, N, N, R, R, N, R, N, S, N, S, N, N, S }, // Bug
|
|
44
|
+
[_]Effectiveness{ I, N, N, N, N, N, N, S, R, N, N, N, N, N, S, N, N, R }, // Ghost
|
|
45
|
+
[_]Effectiveness{ N, N, N, N, N, S, N, N, R, N, R, R, N, R, N, S, N, N }, // Steel
|
|
46
|
+
[_]Effectiveness{ N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N }, // ???
|
|
47
|
+
[_]Effectiveness{ N, N, N, N, N, R, S, N, S, N, R, R, S, N, N, S, R, N }, // Fire
|
|
48
|
+
[_]Effectiveness{ N, N, N, N, S, S, N, N, N, N, S, R, R, N, N, N, R, N }, // Water
|
|
49
|
+
[_]Effectiveness{ N, N, R, R, S, S, R, N, R, N, R, S, R, N, N, N, R, N }, // Grass
|
|
50
|
+
[_]Effectiveness{ N, N, S, N, I, N, N, N, N, N, N, S, R, R, N, N, R, N }, // Electric
|
|
51
|
+
[_]Effectiveness{ N, S, N, S, N, N, N, N, R, N, N, N, N, N, R, N, N, I }, // Psychic
|
|
52
|
+
[_]Effectiveness{ N, N, S, N, S, N, N, N, R, N, R, R, S, N, N, R, S, N }, // Ice
|
|
53
|
+
[_]Effectiveness{ N, N, N, N, N, N, N, N, R, N, N, N, N, N, N, N, S, N }, // Dragon
|
|
54
|
+
[_]Effectiveness{ N, R, N, N, N, N, N, S, R, N, N, N, N, N, S, N, N, R }, // Dark
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const PRECEDENCE = [_]u8{
|
|
58
|
+
1, // Normal
|
|
59
|
+
7, // Fighting
|
|
60
|
+
10, // Flying
|
|
61
|
+
8, // Poison
|
|
62
|
+
9, // Ground
|
|
63
|
+
13, // Rock
|
|
64
|
+
12, // Bug
|
|
65
|
+
14, // Ghost
|
|
66
|
+
17, // Steel
|
|
67
|
+
0, // ???
|
|
68
|
+
2, // Fire
|
|
69
|
+
3, // Water
|
|
70
|
+
5, // Grass
|
|
71
|
+
4, // Electric
|
|
72
|
+
11, // Psychic
|
|
73
|
+
6, // Ice
|
|
74
|
+
15, // Dragon
|
|
75
|
+
16, // Dark
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
comptime {
|
|
79
|
+
assert(@bitSizeOf(Type) == 8);
|
|
80
|
+
assert(@sizeOf(@TypeOf(CHART)) == 324);
|
|
81
|
+
assert(@sizeOf(@TypeOf(PRECEDENCE)) == 18);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// The number of types in this generation.
|
|
85
|
+
pub const size = 18;
|
|
86
|
+
|
|
87
|
+
/// Whether or not this type is considered to be special as opposed to physical.
|
|
88
|
+
pub inline fn special(self: Type) bool {
|
|
89
|
+
return @enumToInt(self) >= @enumToInt(Type.Fire);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// The `Effectiveness` of type `t2` vs. type `t1`.
|
|
93
|
+
pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
|
|
94
|
+
return CHART[@enumToInt(t1)][@enumToInt(t2)];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// The precedence order of type type.
|
|
98
|
+
pub inline fn precedence(self: Type) u8 {
|
|
99
|
+
return PRECEDENCE[@enumToInt(self)];
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/// Representation of a Pokémon's typing.
|
|
104
|
+
pub const Types = extern struct {
|
|
105
|
+
/// A Pokémon's primary type.
|
|
106
|
+
type1: Type = .Normal,
|
|
107
|
+
/// A Pokémon's secondary type (may be identical to its primary type).
|
|
108
|
+
type2: Type = .Normal,
|
|
109
|
+
|
|
110
|
+
comptime {
|
|
111
|
+
assert(@sizeOf(Types) == 2);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// Whether this typing is immune to type `t`.
|
|
115
|
+
pub inline fn immune(self: Types, t: Type) bool {
|
|
116
|
+
return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Whether this typing includes type `t`.
|
|
120
|
+
pub inline fn includes(self: Types, t: Type) bool {
|
|
121
|
+
return self.type1 == t or self.type2 == t;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
3
|
+
|
|
4
|
+
const data = @import("../common/data.zig");
|
|
5
|
+
const DEBUG = @import("../common/debug.zig").print;
|
|
6
|
+
const options = @import("../common/options.zig");
|
|
7
|
+
const rng = @import("../common/rng.zig");
|
|
8
|
+
|
|
9
|
+
const items = @import("data/items.zig");
|
|
10
|
+
const moves = @import("data/moves.zig");
|
|
11
|
+
const species = @import("data/species.zig");
|
|
12
|
+
const types = @import("data/types.zig");
|
|
13
|
+
|
|
14
|
+
const gen1 = @import("../gen1/data.zig");
|
|
15
|
+
|
|
16
|
+
const mechanics = @import("mechanics.zig");
|
|
17
|
+
|
|
18
|
+
const assert = std.debug.assert;
|
|
19
|
+
const expectEqual = std.testing.expectEqual;
|
|
20
|
+
const expect = std.testing.expect;
|
|
21
|
+
|
|
22
|
+
pub const MAX_OPTIONS: usize = 9; // move 1..4, switch 2..6
|
|
23
|
+
pub const MAX_LOGS: usize = 180; // TODO
|
|
24
|
+
|
|
25
|
+
pub const OPTIONS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
26
|
+
MAX_OPTIONS
|
|
27
|
+
else
|
|
28
|
+
std.math.ceilPowerOfTwo(usize, MAX_OPTIONS) catch unreachable;
|
|
29
|
+
pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
30
|
+
MAX_LOGS
|
|
31
|
+
else
|
|
32
|
+
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
33
|
+
|
|
34
|
+
const Choice = data.Choice;
|
|
35
|
+
const ID = data.ID;
|
|
36
|
+
const Player = data.Player;
|
|
37
|
+
const Result = data.Result;
|
|
38
|
+
|
|
39
|
+
const showdown = options.showdown;
|
|
40
|
+
|
|
41
|
+
pub const PRNG = rng.PRNG(2);
|
|
42
|
+
|
|
43
|
+
pub fn Battle(comptime RNG: anytype) type {
|
|
44
|
+
return extern struct {
|
|
45
|
+
const Self = @This();
|
|
46
|
+
|
|
47
|
+
sides: [2]Side,
|
|
48
|
+
rng: RNG,
|
|
49
|
+
turn: u16 = 0,
|
|
50
|
+
field: Field = .{},
|
|
51
|
+
|
|
52
|
+
pub inline fn side(self: *Self, player: Player) *Side {
|
|
53
|
+
return &self.sides[@enumToInt(player)];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pub inline fn foe(self: *Self, player: Player) *Side {
|
|
57
|
+
return &self.sides[@enumToInt(player.foe())];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
pub inline fn active(self: *Self, player: Player) ID {
|
|
61
|
+
return player.ident(@intCast(u3, self.side(player).pokemon[0].position));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
pub fn update(self: *Self, c1: Choice, c2: Choice, log: anytype) !Result {
|
|
65
|
+
return mechanics.update(self, c1, c2, log);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
69
|
+
return mechanics.choices(self, player, request, out);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// TODO
|
|
75
|
+
test Battle {
|
|
76
|
+
try expectEqual(496, @sizeOf(Battle(PRNG)));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const Field = packed struct {
|
|
80
|
+
weather: Weather = .None,
|
|
81
|
+
weather_duration: u4 = 0,
|
|
82
|
+
|
|
83
|
+
comptime {
|
|
84
|
+
assert(@sizeOf(Field) == 1);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const Weather = enum(u4) {
|
|
89
|
+
None,
|
|
90
|
+
Rain,
|
|
91
|
+
Sun,
|
|
92
|
+
Sandstorm,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const Side = extern struct {
|
|
96
|
+
pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
|
|
97
|
+
active: ActivePokemon = .{},
|
|
98
|
+
conditions: Conditions,
|
|
99
|
+
last_used_move: Move = .None,
|
|
100
|
+
_: u8 = 0,
|
|
101
|
+
|
|
102
|
+
const Conditions = packed struct {
|
|
103
|
+
Spikes: bool = false,
|
|
104
|
+
Safeguard: bool = false,
|
|
105
|
+
LightScreen: bool = false,
|
|
106
|
+
Reflect: bool = false,
|
|
107
|
+
|
|
108
|
+
safeguard: u4 = 0,
|
|
109
|
+
light_screen: u4 = 0,
|
|
110
|
+
reflect: u4 = 0,
|
|
111
|
+
|
|
112
|
+
comptime {
|
|
113
|
+
assert(@sizeOf(Conditions) == 2);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
comptime {
|
|
118
|
+
assert(@sizeOf(Side) == 240);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
pub inline fn get(self: *const Side, slot: u8) *Pokemon {
|
|
122
|
+
assert(slot > 0 and slot <= 6);
|
|
123
|
+
const id = self.pokemon[slot - 1].position;
|
|
124
|
+
assert(id > 0 and id <= 6);
|
|
125
|
+
return &self.pokemon[id - 1];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
pub inline fn stored(self: *Side) *Pokemon {
|
|
129
|
+
return self.get(1);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
134
|
+
const ActivePokemon = extern struct {
|
|
135
|
+
volatiles: Volatile align(4) = .{},
|
|
136
|
+
stats: Stats(u16) = .{},
|
|
137
|
+
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
138
|
+
boosts: Boosts = .{},
|
|
139
|
+
species: Species = .None,
|
|
140
|
+
item: Item = .None,
|
|
141
|
+
last_move: Move = .None,
|
|
142
|
+
_: u8 = 0,
|
|
143
|
+
|
|
144
|
+
comptime {
|
|
145
|
+
assert(@sizeOf(ActivePokemon) == 44);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
pub inline fn move(self: *ActivePokemon, mslot: u8) *MoveSlot {
|
|
149
|
+
assert(mslot > 0 and mslot <= 4);
|
|
150
|
+
assert(self.moves[mslot - 1].id != .None);
|
|
151
|
+
return &self.moves[mslot - 1];
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const Pokemon = extern struct {
|
|
156
|
+
stats: Stats(u16) = .{},
|
|
157
|
+
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
158
|
+
types: Types = .{},
|
|
159
|
+
dvs: DVs = .{},
|
|
160
|
+
hp: u16 = 0,
|
|
161
|
+
status: u8 = 0,
|
|
162
|
+
species: Species = .None,
|
|
163
|
+
level: u8 = 100,
|
|
164
|
+
item: Item = .None,
|
|
165
|
+
happiness: u8 = 255,
|
|
166
|
+
position: u8 = 0,
|
|
167
|
+
|
|
168
|
+
comptime {
|
|
169
|
+
assert(@sizeOf(Pokemon) == 32);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
pub inline fn move(self: *Pokemon, mslot: u8) *MoveSlot {
|
|
173
|
+
assert(mslot > 0 and mslot <= 4);
|
|
174
|
+
assert(self.moves[mslot - 1].id != .None);
|
|
175
|
+
return &self.moves[mslot - 1];
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
pub const Gender = enum(u2) {
|
|
180
|
+
Male,
|
|
181
|
+
Female,
|
|
182
|
+
Unknown,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const DVs = packed struct {
|
|
186
|
+
gender: Gender = .Male,
|
|
187
|
+
pow: u6 = 40,
|
|
188
|
+
type: Type = .Dark,
|
|
189
|
+
|
|
190
|
+
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
191
|
+
assert(p >= 1 and p <= 40);
|
|
192
|
+
assert(t != .Normal and t != .@"???");
|
|
193
|
+
return DVs{ .gender = g, .pow = p, .type = t };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
pub inline fn power(dvs: DVs) u8 {
|
|
197
|
+
return @as(u8, dvs.pow) + 30;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
pub fn from(specie: Species, dvs: gen1.DVs) DVs {
|
|
201
|
+
const ratio = Species.get(specie).ratio;
|
|
202
|
+
const g: Gender = switch (ratio) {
|
|
203
|
+
0x00 => .Male,
|
|
204
|
+
0xFE => .Female,
|
|
205
|
+
0xFF => .Unknown,
|
|
206
|
+
else => if ((@as(u8, dvs.atk) << 4 | dvs.spe) < ratio) Gender.Female else Gender.Male,
|
|
207
|
+
};
|
|
208
|
+
const p = @intCast(u6, 1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
209
|
+
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
210
|
+
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
|
|
211
|
+
if (t >= @enumToInt(Type.@"???")) t = t + 1;
|
|
212
|
+
return DVs{ .gender = g, .pow = p, .type = @intToEnum(Type, t) };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
comptime {
|
|
216
|
+
assert(@sizeOf(DVs) == 2);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
test DVs {
|
|
221
|
+
var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
|
|
222
|
+
try expectEqual(Gender.Unknown, dvs.gender);
|
|
223
|
+
try expectEqual(Type.Ice, dvs.type);
|
|
224
|
+
try expectEqual(@as(u8, 70), dvs.power());
|
|
225
|
+
|
|
226
|
+
dvs = DVs.from(.Pikachu, .{ .spc = 14 });
|
|
227
|
+
try expectEqual(Gender.Male, dvs.gender);
|
|
228
|
+
try expectEqual(Type.Dark, dvs.type);
|
|
229
|
+
try expectEqual(@as(u8, 69), dvs.power());
|
|
230
|
+
|
|
231
|
+
dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
|
|
232
|
+
try expectEqual(Gender.Female, dvs.gender);
|
|
233
|
+
dvs = DVs.from(.Cyndaquil, .{ .atk = 14, .def = 7, .spe = 11, .spc = 2 });
|
|
234
|
+
try expectEqual(Gender.Male, dvs.gender);
|
|
235
|
+
dvs = DVs.from(.Sandshrew, .{ .atk = 7, .def = 10, .spe = 10, .spc = 10 });
|
|
236
|
+
try expectEqual(Gender.Female, dvs.gender);
|
|
237
|
+
dvs = DVs.from(.Sandshrew, .{ .atk = 6, .def = 15, .spe = 7, .spc = 5 });
|
|
238
|
+
try expectEqual(Gender.Female, dvs.gender);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const MoveSlot = packed struct {
|
|
242
|
+
id: Move = .None,
|
|
243
|
+
pp_ups: u2 = 3,
|
|
244
|
+
pp: u6 = 0,
|
|
245
|
+
|
|
246
|
+
comptime {
|
|
247
|
+
assert(@sizeOf(MoveSlot) == 2);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
pub const Status = gen1.Status;
|
|
252
|
+
|
|
253
|
+
const Volatile = packed struct {
|
|
254
|
+
Bide: bool = false,
|
|
255
|
+
Thrashing: bool = false,
|
|
256
|
+
Flinch: bool = false,
|
|
257
|
+
Charging: bool = false,
|
|
258
|
+
Underground: bool = false,
|
|
259
|
+
Flying: bool = false,
|
|
260
|
+
Confusion: bool = false,
|
|
261
|
+
Mist: bool = false,
|
|
262
|
+
FocusEnergy: bool = false,
|
|
263
|
+
Substitute: bool = false,
|
|
264
|
+
Recharging: bool = false,
|
|
265
|
+
Rage: bool = false,
|
|
266
|
+
LeechSeed: bool = false,
|
|
267
|
+
Toxic: bool = false,
|
|
268
|
+
Transform: bool = false,
|
|
269
|
+
|
|
270
|
+
Nightmare: bool = false,
|
|
271
|
+
Cure: bool = false,
|
|
272
|
+
Protect: bool = false,
|
|
273
|
+
Foresight: bool = false,
|
|
274
|
+
PerishSong: bool = false,
|
|
275
|
+
Endure: bool = false,
|
|
276
|
+
Rollout: bool = false,
|
|
277
|
+
Attract: bool = false,
|
|
278
|
+
DefenseCurl: bool = false,
|
|
279
|
+
Encore: bool = false,
|
|
280
|
+
LockOn: bool = false,
|
|
281
|
+
DestinyBond: bool = false,
|
|
282
|
+
BeatUp: bool = false,
|
|
283
|
+
|
|
284
|
+
trapped: bool = false,
|
|
285
|
+
switched: bool = false,
|
|
286
|
+
switching: bool = false,
|
|
287
|
+
|
|
288
|
+
_: u5 = 0,
|
|
289
|
+
|
|
290
|
+
bind: u4 = 0,
|
|
291
|
+
future_sight: FutureSight = .{},
|
|
292
|
+
bide: u16 = 0,
|
|
293
|
+
disabled: Disabled = .{},
|
|
294
|
+
rage: u8 = 0,
|
|
295
|
+
substitute: u8 = 0,
|
|
296
|
+
toxic: u8 = 0,
|
|
297
|
+
confusion: u4 = 0,
|
|
298
|
+
encore: u4 = 0,
|
|
299
|
+
fury_cutter: u4 = 0,
|
|
300
|
+
perish_song: u4 = 0,
|
|
301
|
+
protect: u4 = 0,
|
|
302
|
+
rollout: u4 = 0,
|
|
303
|
+
|
|
304
|
+
const Disabled = packed struct {
|
|
305
|
+
move: u4 = 0,
|
|
306
|
+
duration: u4 = 0,
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const FutureSight = packed struct {
|
|
310
|
+
damage: u12 = 0,
|
|
311
|
+
count: u4 = 0,
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
comptime {
|
|
315
|
+
assert(@sizeOf(Volatile) == 16);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
pub fn Stats(comptime T: type) type {
|
|
320
|
+
return extern struct {
|
|
321
|
+
hp: T = 0,
|
|
322
|
+
atk: T = 0,
|
|
323
|
+
def: T = 0,
|
|
324
|
+
spe: T = 0,
|
|
325
|
+
spa: T = 0,
|
|
326
|
+
spd: T = 0,
|
|
327
|
+
|
|
328
|
+
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
329
|
+
assert(level > 0 and level <= 100);
|
|
330
|
+
const evs = @min(255, @floatToInt(u16, @ceil(@sqrt(@intToFloat(f32, exp)))));
|
|
331
|
+
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
332
|
+
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
333
|
+
return @intCast(T, core *% @as(u32, level) / 100 +% factor);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
test Stats {
|
|
339
|
+
try expectEqual(12, @sizeOf(Stats(u16)));
|
|
340
|
+
const stats = Stats(u8){ .spd = 2, .spe = 3 };
|
|
341
|
+
try expectEqual(2, stats.spd);
|
|
342
|
+
try expectEqual(0, stats.def);
|
|
343
|
+
|
|
344
|
+
var base = Species.get(.Gyarados).stats;
|
|
345
|
+
try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
346
|
+
base = Species.get(.Pidgeot).stats;
|
|
347
|
+
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
pub const Boosts = packed struct {
|
|
351
|
+
atk: i4 = 0,
|
|
352
|
+
def: i4 = 0,
|
|
353
|
+
spe: i4 = 0,
|
|
354
|
+
spa: i4 = 0,
|
|
355
|
+
spd: i4 = 0,
|
|
356
|
+
accuracy: i4 = 0,
|
|
357
|
+
evasion: i4 = 0,
|
|
358
|
+
_: i4 = 0,
|
|
359
|
+
|
|
360
|
+
comptime {
|
|
361
|
+
assert(@sizeOf(Boosts) == 4);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
test Boosts {
|
|
366
|
+
const boosts = Boosts{ .spd = -6 };
|
|
367
|
+
try expectEqual(0, boosts.atk);
|
|
368
|
+
try expectEqual(-6, boosts.spd);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
pub const Item = items.Item;
|
|
372
|
+
|
|
373
|
+
test Item {
|
|
374
|
+
try expect(Item.boost(.MasterBall) == null);
|
|
375
|
+
try expectEqual(Type.Normal, Item.boost(.PinkBow).?);
|
|
376
|
+
try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
|
|
377
|
+
try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
|
|
378
|
+
|
|
379
|
+
try expect(!Item.mail(.UpGrade));
|
|
380
|
+
try expect(Item.mail(.FlowerMail));
|
|
381
|
+
try expect(Item.mail(.MirageMail));
|
|
382
|
+
try expect(!Item.mail(.TownMap));
|
|
383
|
+
|
|
384
|
+
try expect(!Item.berry(.LightBall));
|
|
385
|
+
try expect(Item.berry(.PSNCureBerry));
|
|
386
|
+
try expect(Item.berry(.GoldBerry));
|
|
387
|
+
try expect(!Item.berry(.MasterBall));
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
pub const Move = moves.Move;
|
|
391
|
+
|
|
392
|
+
test Move {
|
|
393
|
+
try expectEqual(251, @enumToInt(Move.BeatUp));
|
|
394
|
+
const move = Move.get(.DynamicPunch);
|
|
395
|
+
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
396
|
+
try expectEqual(@as(u8, 50), move.accuracy);
|
|
397
|
+
try expectEqual(@as(u8, 5), move.pp);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
pub const Species = species.Species;
|
|
401
|
+
|
|
402
|
+
test Species {
|
|
403
|
+
try expectEqual(152, @enumToInt(Species.Chikorita));
|
|
404
|
+
try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
pub const Type = types.Type;
|
|
408
|
+
pub const Types = types.Types;
|
|
409
|
+
pub const Effectiveness = gen1.Effectiveness;
|
|
410
|
+
|
|
411
|
+
test Types {
|
|
412
|
+
try expectEqual(13, @enumToInt(Type.Electric));
|
|
413
|
+
|
|
414
|
+
try expect(!Type.Steel.special());
|
|
415
|
+
try expect(Type.Dark.special());
|
|
416
|
+
|
|
417
|
+
try expectEqual(@as(u8, 3), Type.Water.precedence());
|
|
418
|
+
try expect(Type.Bug.precedence() > Type.Poison.precedence());
|
|
419
|
+
|
|
420
|
+
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|
|
421
|
+
try expectEqual(Effectiveness.Super, Type.effectiveness(.Water, .Fire));
|
|
422
|
+
try expectEqual(Effectiveness.Resisted, Type.effectiveness(.Fire, .Water));
|
|
423
|
+
try expectEqual(Effectiveness.Neutral, Type.effectiveness(.Normal, .Grass));
|
|
424
|
+
try expectEqual(Effectiveness.Immune, Type.effectiveness(.Poison, .Steel));
|
|
425
|
+
}
|