@pkmn/engine 0.1.0-dev.c5fe6d07 → 0.1.0-dev.ce4c0b2a
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 +46 -19
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +43 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +96 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon.d.ts +2 -0
- package/build/pkg/addon.js +35 -58
- 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 +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +3 -3
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +24 -10
- package/build/pkg/index.js +15 -10
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +123 -102
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +36 -68
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +128 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +132 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +230 -99
- package/package.json +63 -30
- package/src/bin/install-pkmn-engine +29 -19
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +12 -24
- package/src/lib/common/debug.zig +11 -6
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +21 -10
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +807 -327
- package/src/lib/common/rational.zig +125 -31
- package/src/lib/common/rng.zig +22 -13
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +660 -137
- package/src/lib/gen1/chance.zig +409 -284
- 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 +48 -59
- package/src/lib/gen1/helpers.zig +94 -552
- package/src/lib/gen1/mechanics.zig +580 -518
- package/src/lib/gen2/calc.zig +161 -0
- package/src/lib/gen2/chance.zig +902 -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 -25
- package/src/lib/gen2/mechanics.zig +3560 -12
- 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/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +351 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/gen1/README.md +0 -892
- package/src/lib/gen2/README.md +0 -308
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -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
|
|
|
@@ -70,28 +71,28 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
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
|
|
|
89
90
|
/// Returns the `MoveDetails` for the given `player`.
|
|
90
|
-
pub
|
|
91
|
+
pub fn lastMove(
|
|
91
92
|
self: anytype,
|
|
92
93
|
player: Player,
|
|
93
94
|
) PointerType(@TypeOf(self), MoveDetails) {
|
|
94
|
-
assert(
|
|
95
|
+
assert(isPointerTo(self, Self));
|
|
95
96
|
return &self.last_moves[@intFromEnum(player)];
|
|
96
97
|
}
|
|
97
98
|
|
|
@@ -112,7 +113,7 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
112
113
|
/// make (i.e. on the cartridge a soft-lock occurs).
|
|
113
114
|
///
|
|
114
115
|
/// This function will always return choices > 0 if `pkmn.options.showdown` is true.
|
|
115
|
-
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 {
|
|
116
117
|
return mechanics.choices(self, player, request, out);
|
|
117
118
|
}
|
|
118
119
|
};
|
|
@@ -141,8 +142,8 @@ pub const Side = extern struct {
|
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
/// Returns the stored `Pokemon` corresponding to the one-indexed party `slot`.
|
|
144
|
-
pub
|
|
145
|
-
assert(
|
|
145
|
+
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
146
|
+
assert(isPointerTo(self, Side));
|
|
146
147
|
assert(slot > 0 and slot <= 6);
|
|
147
148
|
const id = self.order[slot - 1];
|
|
148
149
|
assert(id > 0 and id <= 6);
|
|
@@ -150,8 +151,8 @@ pub const Side = extern struct {
|
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
/// Returns the stored `Pokemon` corresponding to the `active` Pokémon (slot 1).
|
|
153
|
-
pub
|
|
154
|
-
assert(
|
|
154
|
+
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
155
|
+
assert(isPointerTo(self, Side));
|
|
155
156
|
return self.get(1);
|
|
156
157
|
}
|
|
157
158
|
};
|
|
@@ -176,8 +177,8 @@ pub const ActivePokemon = extern struct {
|
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
/// Returns the active Pokémon's current move slot located at the one-indexed `mslot`.
|
|
179
|
-
pub
|
|
180
|
-
assert(
|
|
180
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
181
|
+
assert(isPointerTo(self, ActivePokemon));
|
|
181
182
|
assert(mslot > 0 and mslot <= 4);
|
|
182
183
|
assert(self.moves[mslot - 1].id != .None);
|
|
183
184
|
return &self.moves[mslot - 1];
|
|
@@ -206,15 +207,15 @@ pub const Pokemon = extern struct {
|
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
/// Returns the Pokémon's original move slot located at the one-indexed `mslot`.
|
|
209
|
-
pub
|
|
210
|
-
assert(
|
|
210
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
211
|
+
assert(isPointerTo(self, Pokemon));
|
|
211
212
|
assert(mslot > 0 and mslot <= 4);
|
|
212
213
|
assert(self.moves[mslot - 1].id != .None);
|
|
213
214
|
return &self.moves[mslot - 1];
|
|
214
215
|
}
|
|
215
216
|
};
|
|
216
217
|
|
|
217
|
-
/// Representation of a Generation I
|
|
218
|
+
/// Representation of a Generation I Pokémon's move slot in a battle.
|
|
218
219
|
pub const MoveSlot = extern struct {
|
|
219
220
|
/// The identifier for the move.
|
|
220
221
|
id: Move = .None,
|
|
@@ -222,20 +223,16 @@ pub const MoveSlot = extern struct {
|
|
|
222
223
|
pp: u8 = 0,
|
|
223
224
|
|
|
224
225
|
comptime {
|
|
225
|
-
assert(@sizeOf(MoveSlot) ==
|
|
226
|
+
assert(@sizeOf(MoveSlot) == 2);
|
|
226
227
|
}
|
|
227
228
|
};
|
|
228
229
|
|
|
229
230
|
/// Details required to detect desyncs based on the move last selected/executed by players.
|
|
230
|
-
pub const MoveDetails = packed struct {
|
|
231
|
+
pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
|
|
231
232
|
/// The move slot index of the last move selected in the battle menu.
|
|
232
233
|
index: if (showdown) u8 else u4 = 0,
|
|
233
234
|
/// Whether the last move executed was counterable.
|
|
234
235
|
counterable: if (showdown) u8 else u4 = 0,
|
|
235
|
-
|
|
236
|
-
comptime {
|
|
237
|
-
assert(@sizeOf(MoveDetails) == if (showdown) 2 else 1);
|
|
238
|
-
}
|
|
239
236
|
};
|
|
240
237
|
|
|
241
238
|
/// Bitfield representation of a Generation I & II Pokémon's major status condition.
|
|
@@ -259,39 +256,39 @@ pub const Status = enum(u8) {
|
|
|
259
256
|
|
|
260
257
|
pub const TOX = 0b10001000;
|
|
261
258
|
|
|
262
|
-
const
|
|
259
|
+
const SLEEP = 0b111;
|
|
263
260
|
|
|
264
261
|
/// Whether or not the status `num` is the same as `status`.
|
|
265
|
-
pub
|
|
262
|
+
pub fn is(num: u8, status: Status) bool {
|
|
266
263
|
if (status == .SLP) return Status.duration(num) > 0;
|
|
267
264
|
return ((num >> @intCast(@intFromEnum(status))) & 1) != 0;
|
|
268
265
|
}
|
|
269
266
|
|
|
270
267
|
/// Initializes a non-sleep `status`. Use `slp` or `slf` to initialize a sleep status.
|
|
271
|
-
pub
|
|
268
|
+
pub fn init(status: Status) u8 {
|
|
272
269
|
assert(status != .SLP and status != .EXT);
|
|
273
270
|
return @as(u8, 1) << @intCast(@intFromEnum(status));
|
|
274
271
|
}
|
|
275
272
|
|
|
276
273
|
/// Initializes a non self-inflicted sleep status with duration `dur`.
|
|
277
|
-
pub
|
|
274
|
+
pub fn slp(dur: u3) u8 {
|
|
278
275
|
assert(dur > 0);
|
|
279
276
|
return @as(u8, dur);
|
|
280
277
|
}
|
|
281
278
|
|
|
282
279
|
/// Initializes a self-inflicted sleep status with duration `dur`.
|
|
283
|
-
pub
|
|
280
|
+
pub fn slf(dur: u3) u8 {
|
|
284
281
|
assert(dur > 0);
|
|
285
282
|
return 0x80 | slp(dur);
|
|
286
283
|
}
|
|
287
284
|
|
|
288
285
|
/// Returns the duration of a sleep status.
|
|
289
|
-
pub
|
|
290
|
-
return @intCast(num &
|
|
286
|
+
pub fn duration(num: u8) u3 {
|
|
287
|
+
return @intCast(num & SLEEP);
|
|
291
288
|
}
|
|
292
289
|
|
|
293
290
|
/// Returns whether `num` reflects any status.
|
|
294
|
-
pub
|
|
291
|
+
pub fn any(num: u8) bool {
|
|
295
292
|
return num > 0;
|
|
296
293
|
}
|
|
297
294
|
|
|
@@ -329,7 +326,7 @@ test Status {
|
|
|
329
326
|
}
|
|
330
327
|
|
|
331
328
|
/// Bitfield representation of volatile statuses and associated data in Generation I.
|
|
332
|
-
pub const Volatiles = packed struct {
|
|
329
|
+
pub const Volatiles = packed struct(u64) {
|
|
333
330
|
/// Whether the "Bide" volatile status is present.
|
|
334
331
|
Bide: bool = false,
|
|
335
332
|
/// Whether the "Thrashing" volatile status is present.
|
|
@@ -382,19 +379,15 @@ pub const Volatiles = packed struct {
|
|
|
382
379
|
/// The identity of whom the active Pokémon is transformed into.
|
|
383
380
|
transform: u4 = 0,
|
|
384
381
|
/// The remaining turns the move is disabled.
|
|
385
|
-
|
|
382
|
+
disable_duration: u4 = 0,
|
|
386
383
|
/// The move slot (1-4) that is disabled.
|
|
387
|
-
|
|
384
|
+
disable_move: u3 = 0,
|
|
388
385
|
/// The number of turns toxic damage has been accumulating.
|
|
389
386
|
toxic: u5 = 0,
|
|
390
|
-
|
|
391
|
-
comptime {
|
|
392
|
-
assert(@sizeOf(Volatiles) == 8);
|
|
393
|
-
}
|
|
394
387
|
};
|
|
395
388
|
|
|
396
389
|
test Volatiles {
|
|
397
|
-
var volatiles =
|
|
390
|
+
var volatiles: Volatiles = .{};
|
|
398
391
|
volatiles.Confusion = true;
|
|
399
392
|
volatiles.confusion = 2;
|
|
400
393
|
volatiles.Thrashing = true;
|
|
@@ -403,8 +396,8 @@ test Volatiles {
|
|
|
403
396
|
volatiles.Substitute = true;
|
|
404
397
|
volatiles.substitute = 42;
|
|
405
398
|
volatiles.toxic = 4;
|
|
406
|
-
volatiles.
|
|
407
|
-
volatiles.
|
|
399
|
+
volatiles.disable_move = 2;
|
|
400
|
+
volatiles.disable_duration = 4;
|
|
408
401
|
|
|
409
402
|
try expect(volatiles.Confusion);
|
|
410
403
|
try expect(volatiles.Thrashing);
|
|
@@ -415,14 +408,14 @@ test Volatiles {
|
|
|
415
408
|
|
|
416
409
|
try expectEqual(@as(u16, 235), volatiles.state);
|
|
417
410
|
try expectEqual(@as(u8, 42), volatiles.substitute);
|
|
418
|
-
try expectEqual(@as(u4, 2), volatiles.
|
|
419
|
-
try expectEqual(@as(u4, 4), volatiles.
|
|
420
|
-
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);
|
|
421
414
|
try expectEqual(@as(u5, 4), volatiles.toxic);
|
|
422
|
-
try expectEqual(@as(
|
|
415
|
+
try expectEqual(@as(u3, 3), volatiles.attacks);
|
|
423
416
|
}
|
|
424
417
|
|
|
425
|
-
///
|
|
418
|
+
/// Representation of a Pokémon's stats in Generation I.
|
|
426
419
|
pub fn Stats(comptime T: type) type {
|
|
427
420
|
return extern struct {
|
|
428
421
|
hp: T = 0,
|
|
@@ -456,7 +449,7 @@ test Stats {
|
|
|
456
449
|
}
|
|
457
450
|
|
|
458
451
|
/// Representation of a Pokémon's boosts in Generation I.
|
|
459
|
-
pub const Boosts = packed struct {
|
|
452
|
+
pub const Boosts = packed struct(u32) {
|
|
460
453
|
/// A Pokémon's Attack boosts.
|
|
461
454
|
atk: i4 = 0,
|
|
462
455
|
/// A Pokémon's Defense boosts.
|
|
@@ -471,14 +464,10 @@ pub const Boosts = packed struct {
|
|
|
471
464
|
evasion: i4 = 0,
|
|
472
465
|
|
|
473
466
|
_: u8 = 0,
|
|
474
|
-
|
|
475
|
-
comptime {
|
|
476
|
-
assert(@sizeOf(Boosts) == 4);
|
|
477
|
-
}
|
|
478
467
|
};
|
|
479
468
|
|
|
480
469
|
test Boosts {
|
|
481
|
-
const boosts =
|
|
470
|
+
const boosts: Boosts = .{ .spc = -6 };
|
|
482
471
|
try expectEqual(0, boosts.atk);
|
|
483
472
|
try expectEqual(-6, boosts.spc);
|
|
484
473
|
}
|
|
@@ -490,7 +479,7 @@ test Move {
|
|
|
490
479
|
try expectEqual(2, @intFromEnum(Move.KarateChop));
|
|
491
480
|
const move = Move.get(.Fissure);
|
|
492
481
|
try expectEqual(Move.Effect.OHKO, move.effect);
|
|
493
|
-
try expectEqual(
|
|
482
|
+
try expectEqual(rng.Gen12.percent(30), move.accuracy);
|
|
494
483
|
try expectEqual(Type.Ground, move.type);
|
|
495
484
|
|
|
496
485
|
try expect(!Move.Effect.onBegin(.None));
|
|
@@ -628,16 +617,16 @@ pub const DVs = struct {
|
|
|
628
617
|
};
|
|
629
618
|
|
|
630
619
|
test DVs {
|
|
631
|
-
var dvs =
|
|
620
|
+
var dvs: DVs = .{ .spc = 15, .spe = 15 };
|
|
632
621
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
633
|
-
dvs =
|
|
622
|
+
dvs = .{
|
|
634
623
|
.atk = 5,
|
|
635
624
|
.def = 15,
|
|
636
625
|
.spe = 13,
|
|
637
626
|
.spc = 13,
|
|
638
627
|
};
|
|
639
628
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
640
|
-
dvs =
|
|
629
|
+
dvs = .{
|
|
641
630
|
.def = 3,
|
|
642
631
|
.spe = 10,
|
|
643
632
|
.spc = 11,
|