@pkmn/engine 0.1.0-dev.a324c50f → 0.1.0-dev.a77ab8e2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -3
- package/README.md +87 -52
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +2 -2
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +4 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +2 -2
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +3 -2
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +61 -42
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +8 -3
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +5 -4
- package/build/tools/debug.js.map +1 -1
- package/build.zig +259 -117
- package/package.json +57 -25
- package/src/bin/install-pkmn-engine +31 -21
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +12 -24
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/js.zig +120 -0
- package/src/lib/common/optional.zig +71 -0
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +810 -327
- package/src/lib/common/rational.zig +447 -0
- package/src/lib/common/rng.zig +22 -13
- package/src/lib/common/util.zig +61 -0
- package/src/lib/gen1/calc.zig +806 -0
- package/src/lib/gen1/chance.zig +1167 -0
- package/src/lib/gen1/data/moves.zig +192 -180
- package/src/lib/gen1/data/species.zig +3 -3
- package/src/lib/gen1/data/types.zig +7 -7
- package/src/lib/gen1/data.zig +58 -64
- package/src/lib/gen1/helpers.zig +95 -397
- package/src/lib/gen1/mechanics.zig +587 -550
- package/src/lib/gen2/calc.zig +161 -0
- package/src/lib/gen2/chance.zig +905 -0
- package/src/lib/gen2/data/items.zig +12 -18
- package/src/lib/gen2/data/moves.zig +663 -569
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +134 -31
- package/src/lib/gen2/data.zig +107 -79
- package/src/lib/gen2/generated.zig +806 -0
- package/src/lib/gen2/helpers.zig +49 -21
- package/src/lib/gen2/mechanics.zig +3563 -9
- package/src/lib/{binding/node.zig → node.zig} +10 -13
- package/src/lib/pkmn.zig +32 -15
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -814
- package/src/lib/gen2/README.md +0 -309
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Code generated by `tools/generate` - manual edits will be overwritten
|
|
1
|
+
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
3
|
const std = @import("std");
|
|
4
4
|
|
|
@@ -1091,8 +1091,8 @@ pub const Species = enum(u8) {
|
|
|
1091
1091
|
/// The number of Pokémon species in this generation.
|
|
1092
1092
|
pub const size = 151;
|
|
1093
1093
|
|
|
1094
|
-
/// The Pokémon's critical hit
|
|
1095
|
-
pub
|
|
1094
|
+
/// The Pokémon's critical hit ratio out of 256.
|
|
1095
|
+
pub fn chance(id: Species) u8 {
|
|
1096
1096
|
assert(id != .None);
|
|
1097
1097
|
return CHANCES[@intFromEnum(id) - 1];
|
|
1098
1098
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Code generated by `tools/generate` - manual edits will be overwritten
|
|
1
|
+
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
3
|
const std = @import("std");
|
|
4
4
|
|
|
@@ -30,7 +30,7 @@ pub const Type = enum(u4) {
|
|
|
30
30
|
Ice,
|
|
31
31
|
Dragon,
|
|
32
32
|
|
|
33
|
-
const CHART
|
|
33
|
+
const CHART: [15][15]Effectiveness = .{
|
|
34
34
|
[_]Effectiveness{ N, N, N, N, N, R, N, I, N, N, N, N, N, N, N }, // Normal
|
|
35
35
|
[_]Effectiveness{ S, N, R, R, N, S, R, I, N, N, N, N, R, S, N }, // Fighting
|
|
36
36
|
[_]Effectiveness{ N, S, N, N, N, R, S, N, N, N, S, R, N, N, N }, // Flying
|
|
@@ -90,16 +90,16 @@ pub const Type = enum(u4) {
|
|
|
90
90
|
pub const size = 15;
|
|
91
91
|
|
|
92
92
|
/// Whether or not this type is considered to be special as opposed to physical.
|
|
93
|
-
pub
|
|
93
|
+
pub fn special(self: Type) bool {
|
|
94
94
|
return @intFromEnum(self) >= @intFromEnum(Type.Fire);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/// The `Effectiveness` of type `t2` vs. type `t1`.
|
|
98
|
-
pub
|
|
98
|
+
pub fn effectiveness(t1: Type, t2: Type) Effectiveness {
|
|
99
99
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
/// The precedence order of
|
|
102
|
+
/// The precedence order of Type `t2` vs. Type `t1`.
|
|
103
103
|
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
104
104
|
for (PRECEDENCE, 0..) |matchup, i| {
|
|
105
105
|
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
|
|
@@ -120,12 +120,12 @@ pub const Types = packed struct {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/// Whether this typing is immune to type `t`.
|
|
123
|
-
pub
|
|
123
|
+
pub fn immune(self: Types, t: Type) bool {
|
|
124
124
|
return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/// Whether this typing includes type `t`.
|
|
128
|
-
pub
|
|
128
|
+
pub fn includes(self: Types, t: Type) bool {
|
|
129
129
|
return self.type1 == t or self.type2 == t;
|
|
130
130
|
}
|
|
131
131
|
};
|
package/src/lib/gen1/data.zig
CHANGED
|
@@ -22,11 +22,11 @@ const expect = std.testing.expect;
|
|
|
22
22
|
|
|
23
23
|
/// The minimum size in bytes required to hold all Generation I choice options.
|
|
24
24
|
pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
|
|
25
|
-
/// The
|
|
26
|
-
/// At least as large as MAX_CHOICES.
|
|
25
|
+
/// The maximum number of bytes possibly logged by a single Generation I update.
|
|
27
26
|
pub const MAX_LOGS: usize = 180;
|
|
28
27
|
|
|
29
|
-
/// The
|
|
28
|
+
/// The optimal size in bytes required to hold all Generation I choice options.
|
|
29
|
+
/// At least as large as MAX_CHOICES.
|
|
30
30
|
pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
31
31
|
MAX_CHOICES
|
|
32
32
|
else
|
|
@@ -47,6 +47,7 @@ const Player = data.Player;
|
|
|
47
47
|
const Result = data.Result;
|
|
48
48
|
|
|
49
49
|
const PointerType = util.PointerType;
|
|
50
|
+
const isPointerTo = util.isPointerTo;
|
|
50
51
|
|
|
51
52
|
const showdown = pkmn.options.showdown;
|
|
52
53
|
|
|
@@ -65,27 +66,36 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
65
66
|
/// The last damage dealt by either side.
|
|
66
67
|
last_damage: u16 = 0,
|
|
67
68
|
/// Details about the last move selected/used by each player required to detect desyncs.
|
|
68
|
-
last_moves: MoveDetails = .{},
|
|
69
|
+
last_moves: [2]MoveDetails = [_]MoveDetails{.{}} ** 2,
|
|
69
70
|
/// The pseudo random number generator.
|
|
70
71
|
rng: RNG,
|
|
71
72
|
|
|
72
73
|
/// Returns the `Side` for the given `player`.
|
|
73
|
-
pub
|
|
74
|
-
assert(
|
|
74
|
+
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
75
|
+
assert(isPointerTo(self, Self));
|
|
75
76
|
return &self.sides[@intFromEnum(player)];
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
/// Returns the `Side` of the opponent for the given `player`
|
|
79
|
-
pub
|
|
80
|
-
assert(
|
|
80
|
+
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
81
|
+
assert(isPointerTo(self, Self));
|
|
81
82
|
return &self.sides[@intFromEnum(player.foe())];
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
/// Returns an identifier for the active Pokémon of `player`.
|
|
85
|
-
pub
|
|
86
|
+
pub fn active(self: *const Self, player: Player) ID {
|
|
86
87
|
return player.ident(@intCast(self.side(player).order[0]));
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
/// Returns the `MoveDetails` for the given `player`.
|
|
91
|
+
pub fn lastMove(
|
|
92
|
+
self: anytype,
|
|
93
|
+
player: Player,
|
|
94
|
+
) PointerType(@TypeOf(self), MoveDetails) {
|
|
95
|
+
assert(isPointerTo(self, Self));
|
|
96
|
+
return &self.last_moves[@intFromEnum(player)];
|
|
97
|
+
}
|
|
98
|
+
|
|
89
99
|
/// Returns the result of applying Player 1's choice `c1` and Player 2's choice `c2` to the
|
|
90
100
|
/// battle, optionally writing protocol message logs to `options.log` if `pkmn.options.log`
|
|
91
101
|
/// is enabled.
|
|
@@ -103,7 +113,7 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
103
113
|
/// make (i.e. on the cartridge a soft-lock occurs).
|
|
104
114
|
///
|
|
105
115
|
/// This function will always return choices > 0 if `pkmn.options.showdown` is true.
|
|
106
|
-
pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
116
|
+
pub fn choices(self: *const Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
107
117
|
return mechanics.choices(self, player, request, out);
|
|
108
118
|
}
|
|
109
119
|
};
|
|
@@ -132,8 +142,8 @@ pub const Side = extern struct {
|
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
/// Returns the stored `Pokemon` corresponding to the one-indexed party `slot`.
|
|
135
|
-
pub
|
|
136
|
-
assert(
|
|
145
|
+
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
146
|
+
assert(isPointerTo(self, Side));
|
|
137
147
|
assert(slot > 0 and slot <= 6);
|
|
138
148
|
const id = self.order[slot - 1];
|
|
139
149
|
assert(id > 0 and id <= 6);
|
|
@@ -141,8 +151,8 @@ pub const Side = extern struct {
|
|
|
141
151
|
}
|
|
142
152
|
|
|
143
153
|
/// Returns the stored `Pokemon` corresponding to the `active` Pokémon (slot 1).
|
|
144
|
-
pub
|
|
145
|
-
assert(
|
|
154
|
+
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
155
|
+
assert(isPointerTo(self, Side));
|
|
146
156
|
return self.get(1);
|
|
147
157
|
}
|
|
148
158
|
};
|
|
@@ -167,8 +177,8 @@ pub const ActivePokemon = extern struct {
|
|
|
167
177
|
}
|
|
168
178
|
|
|
169
179
|
/// Returns the active Pokémon's current move slot located at the one-indexed `mslot`.
|
|
170
|
-
pub
|
|
171
|
-
assert(
|
|
180
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
181
|
+
assert(isPointerTo(self, ActivePokemon));
|
|
172
182
|
assert(mslot > 0 and mslot <= 4);
|
|
173
183
|
assert(self.moves[mslot - 1].id != .None);
|
|
174
184
|
return &self.moves[mslot - 1];
|
|
@@ -197,15 +207,15 @@ pub const Pokemon = extern struct {
|
|
|
197
207
|
}
|
|
198
208
|
|
|
199
209
|
/// Returns the Pokémon's original move slot located at the one-indexed `mslot`.
|
|
200
|
-
pub
|
|
201
|
-
assert(
|
|
210
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
211
|
+
assert(isPointerTo(self, Pokemon));
|
|
202
212
|
assert(mslot > 0 and mslot <= 4);
|
|
203
213
|
assert(self.moves[mslot - 1].id != .None);
|
|
204
214
|
return &self.moves[mslot - 1];
|
|
205
215
|
}
|
|
206
216
|
};
|
|
207
217
|
|
|
208
|
-
/// Representation of a Generation I
|
|
218
|
+
/// Representation of a Generation I Pokémon's move slot in a battle.
|
|
209
219
|
pub const MoveSlot = extern struct {
|
|
210
220
|
/// The identifier for the move.
|
|
211
221
|
id: Move = .None,
|
|
@@ -213,24 +223,16 @@ pub const MoveSlot = extern struct {
|
|
|
213
223
|
pp: u8 = 0,
|
|
214
224
|
|
|
215
225
|
comptime {
|
|
216
|
-
assert(@sizeOf(MoveSlot) ==
|
|
226
|
+
assert(@sizeOf(MoveSlot) == 2);
|
|
217
227
|
}
|
|
218
228
|
};
|
|
219
229
|
|
|
220
230
|
/// Details required to detect desyncs based on the move last selected/executed by players.
|
|
221
|
-
pub const MoveDetails = packed struct {
|
|
222
|
-
/// The move slot index of the last move selected in the battle menu
|
|
223
|
-
|
|
224
|
-
/// Whether the last move executed
|
|
225
|
-
|
|
226
|
-
/// The move slot index of the last move selected in the battle menu by Player 2
|
|
227
|
-
p2_index: if (showdown) u8 else u4 = 0,
|
|
228
|
-
/// Whether the last move executed by Player 2 was counterable.
|
|
229
|
-
p2_counterable: if (showdown) u8 else u4 = 0,
|
|
230
|
-
|
|
231
|
-
comptime {
|
|
232
|
-
assert(@sizeOf(MoveDetails) == if (showdown) 4 else 2);
|
|
233
|
-
}
|
|
231
|
+
pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
|
|
232
|
+
/// The move slot index of the last move selected in the battle menu.
|
|
233
|
+
index: if (showdown) u8 else u4 = 0,
|
|
234
|
+
/// Whether the last move executed was counterable.
|
|
235
|
+
counterable: if (showdown) u8 else u4 = 0,
|
|
234
236
|
};
|
|
235
237
|
|
|
236
238
|
/// Bitfield representation of a Generation I & II Pokémon's major status condition.
|
|
@@ -257,36 +259,36 @@ pub const Status = enum(u8) {
|
|
|
257
259
|
const SLP = 0b111;
|
|
258
260
|
|
|
259
261
|
/// Whether or not the status `num` is the same as `status`.
|
|
260
|
-
pub
|
|
262
|
+
pub fn is(num: u8, status: Status) bool {
|
|
261
263
|
if (status == .SLP) return Status.duration(num) > 0;
|
|
262
264
|
return ((num >> @intCast(@intFromEnum(status))) & 1) != 0;
|
|
263
265
|
}
|
|
264
266
|
|
|
265
267
|
/// Initializes a non-sleep `status`. Use `slp` or `slf` to initialize a sleep status.
|
|
266
|
-
pub
|
|
268
|
+
pub fn init(status: Status) u8 {
|
|
267
269
|
assert(status != .SLP and status != .EXT);
|
|
268
270
|
return @as(u8, 1) << @intCast(@intFromEnum(status));
|
|
269
271
|
}
|
|
270
272
|
|
|
271
273
|
/// Initializes a non self-inflicted sleep status with duration `dur`.
|
|
272
|
-
pub
|
|
274
|
+
pub fn slp(dur: u3) u8 {
|
|
273
275
|
assert(dur > 0);
|
|
274
276
|
return @as(u8, dur);
|
|
275
277
|
}
|
|
276
278
|
|
|
277
279
|
/// Initializes a self-inflicted sleep status with duration `dur`.
|
|
278
|
-
pub
|
|
280
|
+
pub fn slf(dur: u3) u8 {
|
|
279
281
|
assert(dur > 0);
|
|
280
282
|
return 0x80 | slp(dur);
|
|
281
283
|
}
|
|
282
284
|
|
|
283
285
|
/// Returns the duration of a sleep status.
|
|
284
|
-
pub
|
|
286
|
+
pub fn duration(num: u8) u3 {
|
|
285
287
|
return @intCast(num & SLP);
|
|
286
288
|
}
|
|
287
289
|
|
|
288
290
|
/// Returns whether `num` reflects any status.
|
|
289
|
-
pub
|
|
291
|
+
pub fn any(num: u8) bool {
|
|
290
292
|
return num > 0;
|
|
291
293
|
}
|
|
292
294
|
|
|
@@ -324,7 +326,7 @@ test Status {
|
|
|
324
326
|
}
|
|
325
327
|
|
|
326
328
|
/// Bitfield representation of volatile statuses and associated data in Generation I.
|
|
327
|
-
pub const Volatiles = packed struct {
|
|
329
|
+
pub const Volatiles = packed struct(u64) {
|
|
328
330
|
/// Whether the "Bide" volatile status is present.
|
|
329
331
|
Bide: bool = false,
|
|
330
332
|
/// Whether the "Thrashing" volatile status is present.
|
|
@@ -377,19 +379,15 @@ pub const Volatiles = packed struct {
|
|
|
377
379
|
/// The identity of whom the active Pokémon is transformed into.
|
|
378
380
|
transform: u4 = 0,
|
|
379
381
|
/// The remaining turns the move is disabled.
|
|
380
|
-
|
|
382
|
+
disable_duration: u4 = 0,
|
|
381
383
|
/// The move slot (1-4) that is disabled.
|
|
382
|
-
|
|
384
|
+
disable_move: u3 = 0,
|
|
383
385
|
/// The number of turns toxic damage has been accumulating.
|
|
384
386
|
toxic: u5 = 0,
|
|
385
|
-
|
|
386
|
-
comptime {
|
|
387
|
-
assert(@sizeOf(Volatiles) == 8);
|
|
388
|
-
}
|
|
389
387
|
};
|
|
390
388
|
|
|
391
389
|
test Volatiles {
|
|
392
|
-
var volatiles =
|
|
390
|
+
var volatiles: Volatiles = .{};
|
|
393
391
|
volatiles.Confusion = true;
|
|
394
392
|
volatiles.confusion = 2;
|
|
395
393
|
volatiles.Thrashing = true;
|
|
@@ -398,8 +396,8 @@ test Volatiles {
|
|
|
398
396
|
volatiles.Substitute = true;
|
|
399
397
|
volatiles.substitute = 42;
|
|
400
398
|
volatiles.toxic = 4;
|
|
401
|
-
volatiles.
|
|
402
|
-
volatiles.
|
|
399
|
+
volatiles.disable_move = 2;
|
|
400
|
+
volatiles.disable_duration = 4;
|
|
403
401
|
|
|
404
402
|
try expect(volatiles.Confusion);
|
|
405
403
|
try expect(volatiles.Thrashing);
|
|
@@ -410,14 +408,14 @@ test Volatiles {
|
|
|
410
408
|
|
|
411
409
|
try expectEqual(@as(u16, 235), volatiles.state);
|
|
412
410
|
try expectEqual(@as(u8, 42), volatiles.substitute);
|
|
413
|
-
try expectEqual(@as(u4, 2), volatiles.
|
|
414
|
-
try expectEqual(@as(u4, 4), volatiles.
|
|
415
|
-
try expectEqual(@as(
|
|
411
|
+
try expectEqual(@as(u4, 2), volatiles.disable_move);
|
|
412
|
+
try expectEqual(@as(u4, 4), volatiles.disable_duration);
|
|
413
|
+
try expectEqual(@as(u3, 2), volatiles.confusion);
|
|
416
414
|
try expectEqual(@as(u5, 4), volatiles.toxic);
|
|
417
|
-
try expectEqual(@as(
|
|
415
|
+
try expectEqual(@as(u3, 3), volatiles.attacks);
|
|
418
416
|
}
|
|
419
417
|
|
|
420
|
-
///
|
|
418
|
+
/// Representation of a Pokémon's stats in Generation I.
|
|
421
419
|
pub fn Stats(comptime T: type) type {
|
|
422
420
|
return extern struct {
|
|
423
421
|
hp: T = 0,
|
|
@@ -451,7 +449,7 @@ test Stats {
|
|
|
451
449
|
}
|
|
452
450
|
|
|
453
451
|
/// Representation of a Pokémon's boosts in Generation I.
|
|
454
|
-
pub const Boosts = packed struct {
|
|
452
|
+
pub const Boosts = packed struct(u32) {
|
|
455
453
|
/// A Pokémon's Attack boosts.
|
|
456
454
|
atk: i4 = 0,
|
|
457
455
|
/// A Pokémon's Defense boosts.
|
|
@@ -466,14 +464,10 @@ pub const Boosts = packed struct {
|
|
|
466
464
|
evasion: i4 = 0,
|
|
467
465
|
|
|
468
466
|
_: u8 = 0,
|
|
469
|
-
|
|
470
|
-
comptime {
|
|
471
|
-
assert(@sizeOf(Boosts) == 4);
|
|
472
|
-
}
|
|
473
467
|
};
|
|
474
468
|
|
|
475
469
|
test Boosts {
|
|
476
|
-
const boosts =
|
|
470
|
+
const boosts: Boosts = .{ .spc = -6 };
|
|
477
471
|
try expectEqual(0, boosts.atk);
|
|
478
472
|
try expectEqual(-6, boosts.spc);
|
|
479
473
|
}
|
|
@@ -485,7 +479,7 @@ test Move {
|
|
|
485
479
|
try expectEqual(2, @intFromEnum(Move.KarateChop));
|
|
486
480
|
const move = Move.get(.Fissure);
|
|
487
481
|
try expectEqual(Move.Effect.OHKO, move.effect);
|
|
488
|
-
try expectEqual(
|
|
482
|
+
try expectEqual(rng.Gen12.percent(30), move.accuracy);
|
|
489
483
|
try expectEqual(Type.Ground, move.type);
|
|
490
484
|
|
|
491
485
|
try expect(!Move.Effect.onBegin(.None));
|
|
@@ -623,16 +617,16 @@ pub const DVs = struct {
|
|
|
623
617
|
};
|
|
624
618
|
|
|
625
619
|
test DVs {
|
|
626
|
-
var dvs =
|
|
620
|
+
var dvs: DVs = .{ .spc = 15, .spe = 15 };
|
|
627
621
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
628
|
-
dvs =
|
|
622
|
+
dvs = .{
|
|
629
623
|
.atk = 5,
|
|
630
624
|
.def = 15,
|
|
631
625
|
.spe = 13,
|
|
632
626
|
.spc = 13,
|
|
633
627
|
};
|
|
634
628
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
635
|
-
dvs =
|
|
629
|
+
dvs = .{
|
|
636
630
|
.def = 3,
|
|
637
631
|
.spe = 10,
|
|
638
632
|
.spc = 11,
|