@pkmn/engine 0.1.0-dev.8b192003 → 0.1.0-dev.8cf7e679
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 +9 -6
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +44 -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 +97 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +44 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +25 -3
- package/build/pkg/addon.js +34 -65
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/data.d.ts +23 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +13 -17
- package/build/pkg/gen1.js +6 -10
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +38 -90
- package/build/pkg/index.js +6 -63
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +105 -103
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +35 -69
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +141 -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 +254 -77
- package/package.json +50 -134
- package/src/bin/install-pkmn-engine +9 -7
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +45 -44
- package/src/lib/common/rational.zig +53 -51
- package/src/lib/common/rng.zig +5 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +295 -154
- package/src/lib/gen1/chance.zig +477 -119
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +1 -3
- package/src/lib/gen1/data/types.zig +1 -2
- package/src/lib/gen1/data.zig +35 -38
- package/src/lib/gen1/helpers.zig +17 -27
- package/src/lib/gen1/mechanics.zig +248 -148
- package/src/lib/gen2/calc.zig +37 -27
- package/src/lib/gen2/chance.zig +87 -67
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +1 -2
- package/src/lib/gen2/data.zig +26 -29
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +9 -18
- package/src/lib/gen2/mechanics.zig +81 -81
- package/src/lib/node.zig +2 -4
- package/src/lib/pkmn.zig +11 -5
- package/src/lib/wasm.zig +36 -1
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const assert = std.debug.assert;
|
|
4
|
-
const print = std.debug.print;
|
|
5
|
-
|
|
6
|
-
const expect = std.testing.expect;
|
|
7
|
-
const expectEqual = std.testing.expectEqual;
|
|
8
|
-
|
|
9
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
2
|
+
|
|
3
|
+
const array = @import("../common/array.zig");
|
|
4
|
+
const common = @import("../common/data.zig");
|
|
5
|
+
const data = @import("data.zig");
|
|
6
|
+
const optional = @import("../common/optional.zig");
|
|
10
7
|
const options = @import("../common/options.zig");
|
|
11
8
|
const rational = @import("../common/rational.zig");
|
|
9
|
+
const std = @import("std");
|
|
12
10
|
const util = @import("../common/util.zig");
|
|
13
11
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const data = @import("data.zig");
|
|
18
|
-
|
|
12
|
+
const Array = array.Array;
|
|
13
|
+
const assert = std.debug.assert;
|
|
19
14
|
const enabled = options.chance;
|
|
20
|
-
const
|
|
21
|
-
|
|
15
|
+
const expect = std.testing.expect;
|
|
16
|
+
const expectEqual = std.testing.expectEqual;
|
|
17
|
+
const isPointerTo = util.isPointerTo;
|
|
22
18
|
const Move = data.Move;
|
|
23
19
|
const MoveSlot = data.MoveSlot;
|
|
20
|
+
const Optional = optional.Optional;
|
|
21
|
+
const Player = common.Player;
|
|
22
|
+
const PointerType = util.PointerType;
|
|
23
|
+
const print = std.debug.print;
|
|
24
|
+
const showdown = options.showdown;
|
|
25
|
+
|
|
26
|
+
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
27
|
+
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
28
|
+
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
24
29
|
|
|
25
30
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
26
31
|
/// were observed during a Generation I battle `update`. This can additionally be provided as input
|
|
@@ -37,8 +42,8 @@ pub const Actions = extern struct {
|
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
/// Returns the `Action` for the given `player`.
|
|
40
|
-
pub fn get(self: anytype, player: Player)
|
|
41
|
-
assert(
|
|
45
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Action) {
|
|
46
|
+
assert(isPointerTo(self, Actions));
|
|
42
47
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
43
48
|
}
|
|
44
49
|
|
|
@@ -51,15 +56,15 @@ pub const Actions = extern struct {
|
|
|
51
56
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
52
57
|
/// set to the same value).
|
|
53
58
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
54
|
-
inline for (@typeInfo(Actions)
|
|
55
|
-
inline for (@typeInfo(Action)
|
|
59
|
+
inline for (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
|
|
60
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
56
61
|
const a_val = @field(@field(a, player.name), field.name);
|
|
57
62
|
const b_val = @field(@field(b, player.name), field.name);
|
|
58
63
|
|
|
59
64
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
60
|
-
|
|
65
|
+
Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
61
66
|
return false,
|
|
62
|
-
|
|
67
|
+
Int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
63
68
|
else => unreachable,
|
|
64
69
|
}
|
|
65
70
|
}
|
|
@@ -67,6 +72,25 @@ pub const Actions = extern struct {
|
|
|
67
72
|
return true;
|
|
68
73
|
}
|
|
69
74
|
|
|
75
|
+
/// TODO
|
|
76
|
+
pub fn matchesAny(a: Actions, bs: []Actions, i: usize) bool {
|
|
77
|
+
for (bs, 0..) |b, j| {
|
|
78
|
+
// TODO: is skipping this redundant check worth it?
|
|
79
|
+
if (i == j) continue;
|
|
80
|
+
if (b.matches(a)) return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// TODO
|
|
86
|
+
pub fn relax(actions: Actions) Actions {
|
|
87
|
+
if (!options.overwrite) return actions;
|
|
88
|
+
var a = actions;
|
|
89
|
+
if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
|
|
90
|
+
if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
|
|
91
|
+
return a;
|
|
92
|
+
}
|
|
93
|
+
|
|
70
94
|
pub fn fmt(self: Actions, writer: anytype, shape: bool) !void {
|
|
71
95
|
try writer.writeAll("<P1 = ");
|
|
72
96
|
try self.p1.fmt(writer, shape);
|
|
@@ -99,7 +123,19 @@ test Actions {
|
|
|
99
123
|
try expect(!c.matches(a));
|
|
100
124
|
}
|
|
101
125
|
|
|
126
|
+
/// Observation made about a duration - whether the duration has started, been continued, or ended.
|
|
102
127
|
pub const Observation = enum { started, continuing, ended };
|
|
128
|
+
/// An extension of Observation to account for "hidden" confusion.
|
|
129
|
+
pub const Confusion = enum {
|
|
130
|
+
started,
|
|
131
|
+
continuing,
|
|
132
|
+
ended,
|
|
133
|
+
/// An indicator that an existing confusion duration has been overwritten by a new confusion
|
|
134
|
+
/// duration due to a Thrashing move ending. This is not immediately observable to an opponent,
|
|
135
|
+
/// to compute probabilities from an opponent's point of view based solely on public information
|
|
136
|
+
/// extra work must be done to correct for the information leak
|
|
137
|
+
overwritten,
|
|
138
|
+
};
|
|
103
139
|
|
|
104
140
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
105
141
|
/// single player.
|
|
@@ -125,19 +161,25 @@ pub const Action = packed struct(u64) {
|
|
|
125
161
|
/// or Rolls.{sleepDuration,disableDuration,confusionDuration,attackingDuration} otherwise.
|
|
126
162
|
duration: u4 = 0,
|
|
127
163
|
|
|
128
|
-
|
|
164
|
+
/// TODO
|
|
129
165
|
sleep: Optional(Observation) = .None,
|
|
130
|
-
|
|
166
|
+
/// TODO
|
|
167
|
+
confusion: Optional(Confusion) = .None,
|
|
168
|
+
/// TODO
|
|
131
169
|
disable: Optional(Observation) = .None,
|
|
132
|
-
|
|
170
|
+
/// TODO
|
|
171
|
+
attacking: Optional(Observation) = .None,
|
|
172
|
+
/// TODO
|
|
133
173
|
binding: Optional(Observation) = .None,
|
|
134
|
-
thrashing: Optional(Observation) = .None,
|
|
135
174
|
|
|
136
|
-
_:
|
|
175
|
+
_: u1 = 0,
|
|
137
176
|
|
|
138
177
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
139
178
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
140
179
|
move_slot: u4 = 0,
|
|
180
|
+
|
|
181
|
+
/// TODO
|
|
182
|
+
pp: u4 = 0,
|
|
141
183
|
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
142
184
|
multi_hit: u4 = 0,
|
|
143
185
|
|
|
@@ -157,10 +199,10 @@ pub const Action = packed struct(u64) {
|
|
|
157
199
|
pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
|
|
158
200
|
try writer.writeByte('(');
|
|
159
201
|
var printed = false;
|
|
160
|
-
inline for (@typeInfo(Action)
|
|
202
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
161
203
|
const val = @field(self, field.name);
|
|
162
204
|
switch (@typeInfo(@TypeOf(val))) {
|
|
163
|
-
|
|
205
|
+
Enum => if (val != .None) {
|
|
164
206
|
if (printed) try writer.writeAll(", ");
|
|
165
207
|
if (shape) {
|
|
166
208
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -169,17 +211,11 @@ pub const Action = packed struct(u64) {
|
|
|
169
211
|
if (val == .false) "!" else "",
|
|
170
212
|
field.name,
|
|
171
213
|
});
|
|
172
|
-
} else if (@TypeOf(val) == Optional(Observation)
|
|
214
|
+
} else if (@TypeOf(val) == Optional(Observation) or
|
|
215
|
+
@TypeOf(val) == Optional(Confusion))
|
|
216
|
+
{
|
|
173
217
|
try writer.print("{s}{s}", .{
|
|
174
|
-
|
|
175
|
-
.started => "+",
|
|
176
|
-
.continuing => "",
|
|
177
|
-
.ended => if (std.mem.eql(u8, field.name, "thrashing"))
|
|
178
|
-
"~"
|
|
179
|
-
else
|
|
180
|
-
"-",
|
|
181
|
-
else => unreachable,
|
|
182
|
-
},
|
|
218
|
+
([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
|
|
183
219
|
field.name,
|
|
184
220
|
});
|
|
185
221
|
} else {
|
|
@@ -187,7 +223,7 @@ pub const Action = packed struct(u64) {
|
|
|
187
223
|
}
|
|
188
224
|
printed = true;
|
|
189
225
|
},
|
|
190
|
-
|
|
226
|
+
Int => if (val != 0) {
|
|
191
227
|
if (printed) try writer.writeAll(", ");
|
|
192
228
|
if (shape) {
|
|
193
229
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -204,47 +240,20 @@ pub const Action = packed struct(u64) {
|
|
|
204
240
|
};
|
|
205
241
|
|
|
206
242
|
/// TODO
|
|
207
|
-
pub const Durations = struct {
|
|
243
|
+
pub const Durations = extern struct {
|
|
208
244
|
/// TODO
|
|
209
245
|
p1: Duration = .{},
|
|
210
246
|
/// TODO
|
|
211
247
|
p2: Duration = .{},
|
|
212
248
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
assert(@typeInfo(@TypeOf(self)).Pointer.child == Durations);
|
|
216
|
-
return if (player == .P1) &self.p1 else &self.p2;
|
|
249
|
+
comptime {
|
|
250
|
+
assert(@sizeOf(Durations) == 8);
|
|
217
251
|
}
|
|
218
252
|
|
|
219
253
|
/// TODO
|
|
220
|
-
pub fn
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const action = actions.get(p);
|
|
224
|
-
var duration = self.get(p);
|
|
225
|
-
|
|
226
|
-
// TODO sleeps
|
|
227
|
-
|
|
228
|
-
duration.confusion =
|
|
229
|
-
if (@intFromEnum(action.confusion) > 1) duration.confusion + 1 else 0;
|
|
230
|
-
duration.disable =
|
|
231
|
-
if (@intFromEnum(action.disable) > 1) duration.disable + 1 else 0;
|
|
232
|
-
duration.bide =
|
|
233
|
-
if (@intFromEnum(action.bide) > 1) duration.bide + 1 else 0;
|
|
234
|
-
duration.binding =
|
|
235
|
-
if (@intFromEnum(action.binding) > 1) duration.binding + 1 else 0;
|
|
236
|
-
|
|
237
|
-
// NOTE: this relies on private information!
|
|
238
|
-
if (action.thrashing == .ended) {
|
|
239
|
-
duration.thrashing =
|
|
240
|
-
if (s.active.volatiles.Thrashing) duration.thrashing + 1 else 0;
|
|
241
|
-
} else {
|
|
242
|
-
duration.thrashing =
|
|
243
|
-
if (@intFromEnum(action.thrashing) > 1) duration.thrashing + 1 else 0;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
_ = q;
|
|
247
|
-
}
|
|
254
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Duration) {
|
|
255
|
+
assert(isPointerTo(self, Durations));
|
|
256
|
+
return if (player == .P1) &self.p1 else &self.p2;
|
|
248
257
|
}
|
|
249
258
|
|
|
250
259
|
pub fn format(
|
|
@@ -258,15 +267,18 @@ pub const Durations = struct {
|
|
|
258
267
|
}
|
|
259
268
|
};
|
|
260
269
|
|
|
270
|
+
pub const Sleeps = Array(6, u3);
|
|
271
|
+
|
|
261
272
|
/// TODO
|
|
262
|
-
pub const Duration = struct {
|
|
263
|
-
sleeps:
|
|
273
|
+
pub const Duration = packed struct(u32) {
|
|
274
|
+
sleeps: Sleeps.T = 0,
|
|
264
275
|
confusion: u3 = 0,
|
|
265
276
|
disable: u4 = 0,
|
|
266
|
-
|
|
267
|
-
thrashing: u3 = 0,
|
|
277
|
+
attacking: u3 = 0,
|
|
268
278
|
binding: u3 = 0,
|
|
269
279
|
|
|
280
|
+
_: u1 = 0,
|
|
281
|
+
|
|
270
282
|
pub fn format(
|
|
271
283
|
self: Duration,
|
|
272
284
|
comptime fmt: []const u8,
|
|
@@ -276,27 +288,22 @@ pub const Duration = struct {
|
|
|
276
288
|
_ = .{ fmt, opts };
|
|
277
289
|
try writer.writeByte('(');
|
|
278
290
|
var printed = false;
|
|
279
|
-
inline for (@typeInfo(Duration)
|
|
291
|
+
inline for (@field(@typeInfo(Duration), @tagName(Struct)).fields) |field| {
|
|
280
292
|
const val = @field(self, field.name);
|
|
281
293
|
switch (@typeInfo(@TypeOf(val))) {
|
|
282
|
-
|
|
294
|
+
Int => if (val != 0) {
|
|
283
295
|
if (printed) try writer.writeAll(", ");
|
|
284
|
-
|
|
285
|
-
printed = true;
|
|
286
|
-
},
|
|
287
|
-
.Array => {
|
|
288
|
-
var total: usize = 0;
|
|
289
|
-
for (val) |v| total += v;
|
|
290
|
-
if (total > 0) {
|
|
291
|
-
if (printed) try writer.writeAll(", ");
|
|
296
|
+
if (comptime std.mem.eql(u8, field.name, "sleeps")) {
|
|
292
297
|
try writer.print("{s}:[", .{field.name});
|
|
293
|
-
for (
|
|
298
|
+
for (0..6) |i| {
|
|
294
299
|
if (i != 0) try writer.writeByte(',');
|
|
295
|
-
try writer.print("{d}", .{
|
|
300
|
+
try writer.print("{d}", .{Sleeps.get(val, i)});
|
|
296
301
|
}
|
|
297
302
|
try writer.writeAll("]");
|
|
298
|
-
|
|
303
|
+
} else {
|
|
304
|
+
try writer.print("{s}:{d}", .{ field.name, val });
|
|
299
305
|
}
|
|
306
|
+
printed = true;
|
|
300
307
|
},
|
|
301
308
|
else => unreachable,
|
|
302
309
|
}
|
|
@@ -305,7 +312,7 @@ pub const Duration = struct {
|
|
|
305
312
|
}
|
|
306
313
|
};
|
|
307
314
|
|
|
308
|
-
pub const Commit = enum { hit, miss };
|
|
315
|
+
pub const Commit = enum { hit, miss, binding, err, glitch };
|
|
309
316
|
|
|
310
317
|
const uN = if (options.miss) u8 else u9;
|
|
311
318
|
|
|
@@ -320,6 +327,8 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
320
327
|
/// The actions taken by a hypothetical "chance player" that convey information about which
|
|
321
328
|
/// RNG events were observed during a battle `update`.
|
|
322
329
|
actions: Actions = .{},
|
|
330
|
+
/// TODO
|
|
331
|
+
durations: Durations = .{},
|
|
323
332
|
|
|
324
333
|
// In many cases on both the cartridge and on Pokémon Showdown rolls are made even though
|
|
325
334
|
// later checks render their result irrelevant (missing when the target is actually immune,
|
|
@@ -343,6 +352,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
343
352
|
damage_roll: u8 = 0,
|
|
344
353
|
hit: bool = false,
|
|
345
354
|
hit_probablity: uN = 0,
|
|
355
|
+
glitch: bool = false,
|
|
346
356
|
} = .{},
|
|
347
357
|
|
|
348
358
|
/// Possible error returned by operations tracking chance probability.
|
|
@@ -362,10 +372,18 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
362
372
|
|
|
363
373
|
assert(!showdown);
|
|
364
374
|
|
|
375
|
+
if (kind == .glitch and !self.pending.glitch) return;
|
|
376
|
+
|
|
365
377
|
var action = self.actions.get(player);
|
|
366
378
|
|
|
379
|
+
if (kind == .err and self.pending.crit_probablity != 0) {
|
|
380
|
+
try self.probability.update(self.pending.crit_probablity, 256);
|
|
381
|
+
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
367
385
|
// Always commit the hit result if we make it here (commit won't be called at all if the
|
|
368
|
-
// target is immune
|
|
386
|
+
// target is immune*/behind a sub/etc)
|
|
369
387
|
if (self.pending.hit_probablity != 0) {
|
|
370
388
|
try self.probability.update(self.pending.hit_probablity, 256);
|
|
371
389
|
action.hit = if (self.pending.hit) .true else .false;
|
|
@@ -373,28 +391,61 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
373
391
|
|
|
374
392
|
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
375
393
|
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
376
|
-
// "critical hit" was spurious or not as moves that do 1 or less damage don't
|
|
394
|
+
// "critical hit" was spurious or not as moves that do 1 or less damage don't roll for
|
|
377
395
|
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
378
|
-
// passed to the critical hit helper)
|
|
379
|
-
|
|
396
|
+
// passed to the critical hit helper).
|
|
397
|
+
//
|
|
398
|
+
// There is a special edge case with the division-by-zero freeze where we need to commit
|
|
399
|
+
// the status of critical hit roll the occured before the move missed to avoid missing
|
|
400
|
+
// out on the possibility that the alternative branch encountered an error
|
|
401
|
+
if (kind != .hit and !self.pending.glitch) {
|
|
402
|
+
return assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
403
|
+
}
|
|
380
404
|
|
|
381
405
|
if (self.pending.crit_probablity != 0) {
|
|
382
406
|
try self.probability.update(self.pending.crit_probablity, 256);
|
|
383
407
|
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
384
|
-
}
|
|
408
|
+
} else assert(!self.pending.glitch);
|
|
385
409
|
|
|
386
|
-
if (self.pending.damage_roll > 0) {
|
|
410
|
+
if (self.pending.damage_roll > 0 and (!self.pending.glitch or self.pending.hit)) {
|
|
387
411
|
try self.probability.update(1, 39);
|
|
388
412
|
action.damage = self.pending.damage_roll;
|
|
389
413
|
}
|
|
390
414
|
}
|
|
391
415
|
|
|
416
|
+
pub fn glitch(self: *Self) void {
|
|
417
|
+
if (!enabled) return;
|
|
418
|
+
|
|
419
|
+
assert(!showdown);
|
|
420
|
+
self.pending.glitch = true;
|
|
421
|
+
}
|
|
422
|
+
|
|
392
423
|
pub fn clearPending(self: *Self) void {
|
|
393
424
|
if (!enabled) return;
|
|
394
425
|
|
|
426
|
+
assert(!showdown);
|
|
395
427
|
self.pending = .{};
|
|
396
428
|
}
|
|
397
429
|
|
|
430
|
+
pub fn switched(self: *Self, player: Player, slot: u8) void {
|
|
431
|
+
if (!enabled) return;
|
|
432
|
+
|
|
433
|
+
assert(slot >= 1 and slot <= 6);
|
|
434
|
+
|
|
435
|
+
var d = self.durations.get(player);
|
|
436
|
+
|
|
437
|
+
const out = Sleeps.get(d.sleeps, 0);
|
|
438
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, Sleeps.get(d.sleeps, slot - 1));
|
|
439
|
+
d.sleeps = Sleeps.set(d.sleeps, slot - 1, out);
|
|
440
|
+
|
|
441
|
+
d.confusion = 0;
|
|
442
|
+
d.disable = 0;
|
|
443
|
+
d.attacking = 0;
|
|
444
|
+
d.binding = 0;
|
|
445
|
+
|
|
446
|
+
self.durations.get(player.foe()).binding = 0;
|
|
447
|
+
}
|
|
448
|
+
|
|
398
449
|
pub fn speedTie(self: *Self, p1: bool) Error!void {
|
|
399
450
|
if (!enabled) return;
|
|
400
451
|
|
|
@@ -494,18 +545,170 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
494
545
|
pub fn duration(self: *Self, player: Player, turns: u4) void {
|
|
495
546
|
if (!enabled) return;
|
|
496
547
|
|
|
497
|
-
self.actions.get(player).duration = if (options.key)
|
|
548
|
+
self.actions.get(player).duration = if (options.key) 2 else turns;
|
|
498
549
|
}
|
|
499
550
|
|
|
500
|
-
pub fn
|
|
551
|
+
pub fn observe(
|
|
501
552
|
self: *Self,
|
|
502
|
-
comptime
|
|
553
|
+
comptime field: Action.Field,
|
|
503
554
|
player: Player,
|
|
504
|
-
|
|
555
|
+
obs: Optional(Confusion),
|
|
505
556
|
) void {
|
|
506
557
|
if (!enabled) return;
|
|
507
558
|
|
|
508
|
-
|
|
559
|
+
var a = self.actions.get(player);
|
|
560
|
+
const val = @field(a, @tagName(field));
|
|
561
|
+
switch (obs) {
|
|
562
|
+
.overwritten => {
|
|
563
|
+
assert(field == .confusion);
|
|
564
|
+
assert(val == .started or val == .continuing);
|
|
565
|
+
assert(a.duration > 0);
|
|
566
|
+
a.confusion = obs;
|
|
567
|
+
self.durations.get(player).confusion = 1;
|
|
568
|
+
},
|
|
569
|
+
.None => {
|
|
570
|
+
@field(a, @tagName(field)) = .None;
|
|
571
|
+
if (field == .sleep) {
|
|
572
|
+
var d = self.durations.get(player);
|
|
573
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
574
|
+
} else {
|
|
575
|
+
@field(self.durations.get(player), @tagName(field)) = 0;
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
.started => {
|
|
579
|
+
assert(val == .None or val == .ended);
|
|
580
|
+
assert(switch (field) {
|
|
581
|
+
.confusion => a.duration > 0 or self.actions.get(player.foe()).duration > 0,
|
|
582
|
+
.sleep, .disable => self.actions.get(player.foe()).duration > 0,
|
|
583
|
+
else => a.duration > 0,
|
|
584
|
+
});
|
|
585
|
+
@field(a, @tagName(field)) = .started;
|
|
586
|
+
if (field == .sleep) {
|
|
587
|
+
var d = self.durations.get(player);
|
|
588
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 1);
|
|
589
|
+
} else {
|
|
590
|
+
@field(self.durations.get(player), @tagName(field)) = 1;
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
else => unreachable,
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
598
|
+
if (!enabled) return;
|
|
599
|
+
|
|
600
|
+
var a = self.actions.get(player);
|
|
601
|
+
assert(a.sleep == .None or a.sleep == .started);
|
|
602
|
+
a.sleep = obs;
|
|
603
|
+
|
|
604
|
+
var d = self.durations.get(player);
|
|
605
|
+
const n = Sleeps.get(d.sleeps, 0);
|
|
606
|
+
|
|
607
|
+
if (obs == .None) {
|
|
608
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
609
|
+
} else if (obs == .ended) {
|
|
610
|
+
assert(n >= 1 and n <= 7);
|
|
611
|
+
if (n != 7) try self.probability.update(1, 8 - @as(u4, n));
|
|
612
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
613
|
+
} else {
|
|
614
|
+
assert(obs == .continuing);
|
|
615
|
+
assert(n >= 1 and n < 7);
|
|
616
|
+
try self.probability.update(8 - @as(u4, n) - 1, 8 - @as(u4, n));
|
|
617
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, n + 1);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
pub fn confusion(self: *Self, player: Player, obs: Optional(Confusion)) Error!void {
|
|
622
|
+
if (!enabled) return;
|
|
623
|
+
|
|
624
|
+
var a = self.actions.get(player);
|
|
625
|
+
assert(a.confusion == .None or a.confusion == .started);
|
|
626
|
+
a.confusion = obs;
|
|
627
|
+
|
|
628
|
+
var d = self.durations.get(player);
|
|
629
|
+
const n = d.confusion;
|
|
630
|
+
|
|
631
|
+
if (obs == .ended) {
|
|
632
|
+
assert(n >= 2 and n <= 5);
|
|
633
|
+
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
634
|
+
d.confusion = 0;
|
|
635
|
+
} else {
|
|
636
|
+
assert(obs == .continuing);
|
|
637
|
+
assert(n >= 1 and n < 5);
|
|
638
|
+
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
639
|
+
d.confusion += 1;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
pub fn disable(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
644
|
+
if (!enabled) return;
|
|
645
|
+
|
|
646
|
+
var a = self.actions.get(player);
|
|
647
|
+
assert(a.disable == .None or a.disable == .started);
|
|
648
|
+
a.disable = obs;
|
|
649
|
+
|
|
650
|
+
var d = self.durations.get(player);
|
|
651
|
+
const n = d.disable;
|
|
652
|
+
|
|
653
|
+
if (obs == .ended) {
|
|
654
|
+
assert(n >= 1 and n <= 8);
|
|
655
|
+
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
656
|
+
d.disable = 0;
|
|
657
|
+
} else {
|
|
658
|
+
assert(obs == .continuing);
|
|
659
|
+
assert(n >= 1 and n < 8);
|
|
660
|
+
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
661
|
+
d.disable += 1;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
pub fn attacking(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
666
|
+
if (!enabled) return;
|
|
667
|
+
|
|
668
|
+
var a = self.actions.get(player);
|
|
669
|
+
assert(a.attacking == .None or a.attacking == .started);
|
|
670
|
+
a.attacking = obs;
|
|
671
|
+
|
|
672
|
+
var d = self.durations.get(player);
|
|
673
|
+
const n = d.attacking;
|
|
674
|
+
if (obs == .ended) {
|
|
675
|
+
// "3-4 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
676
|
+
assert(n >= 2 and n <= 3);
|
|
677
|
+
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
678
|
+
d.attacking = 0;
|
|
679
|
+
} else {
|
|
680
|
+
assert(obs == .continuing);
|
|
681
|
+
assert(n >= 1 and n < 3);
|
|
682
|
+
if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
|
|
683
|
+
d.attacking += 1;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
pub fn binding(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
688
|
+
if (!enabled) return;
|
|
689
|
+
|
|
690
|
+
var a = self.actions.get(player);
|
|
691
|
+
assert(a.binding == .None or a.binding == .started);
|
|
692
|
+
a.binding = obs;
|
|
693
|
+
|
|
694
|
+
var d = self.durations.get(player);
|
|
695
|
+
const n = d.binding;
|
|
696
|
+
|
|
697
|
+
assert(n > 0);
|
|
698
|
+
const p: u4 = if (n < 3) 3 else 1;
|
|
699
|
+
const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
|
|
700
|
+
|
|
701
|
+
if (obs == .ended) {
|
|
702
|
+
// "2-5 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
703
|
+
assert(n >= 1 and n <= 4);
|
|
704
|
+
if (n != 4) try self.probability.update(p, q);
|
|
705
|
+
d.binding = 0;
|
|
706
|
+
} else {
|
|
707
|
+
assert(obs == .continuing);
|
|
708
|
+
assert(n >= 1 and n < 4);
|
|
709
|
+
try self.probability.update(q - p, q);
|
|
710
|
+
d.binding += 1;
|
|
711
|
+
}
|
|
509
712
|
}
|
|
510
713
|
|
|
511
714
|
pub fn psywave(self: *Self, player: Player, power: u8, max: u8) Error!void {
|
|
@@ -521,6 +724,15 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
521
724
|
try self.probability.update(1, Move.METRONOME.len);
|
|
522
725
|
self.actions.get(player).metronome = move;
|
|
523
726
|
}
|
|
727
|
+
|
|
728
|
+
pub fn pp(self: *Self, player: Player, n: u4) bool {
|
|
729
|
+
if (!enabled) return true;
|
|
730
|
+
|
|
731
|
+
var a = self.actions.get(player);
|
|
732
|
+
if (n > 1 and a.pp == n) return false;
|
|
733
|
+
if (a.metronome != .None) a.pp += n;
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
524
736
|
};
|
|
525
737
|
}
|
|
526
738
|
|
|
@@ -621,16 +833,16 @@ test "Chance.damage" {
|
|
|
621
833
|
|
|
622
834
|
try chance.damage(.P1, 217);
|
|
623
835
|
if (showdown) {
|
|
624
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
836
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
625
837
|
try expectProbability(&chance.probability, 1, 39);
|
|
626
838
|
} else {
|
|
627
839
|
try expectProbability(&chance.probability, 1, 1);
|
|
628
|
-
try expectValue(0, chance.actions.p1.damage);
|
|
840
|
+
try expectValue(@as(u8, 0), chance.actions.p1.damage);
|
|
629
841
|
|
|
630
842
|
try chance.commit(.P1, .hit);
|
|
631
843
|
|
|
632
844
|
try expectProbability(&chance.probability, 1, 39);
|
|
633
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
845
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
634
846
|
}
|
|
635
847
|
}
|
|
636
848
|
|
|
@@ -682,13 +894,13 @@ test "Chance.moveSlot" {
|
|
|
682
894
|
|
|
683
895
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
684
896
|
try expectProbability(&chance.probability, 1, 2);
|
|
685
|
-
try expectValue(2, chance.actions.p2.move_slot);
|
|
897
|
+
try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
|
|
686
898
|
|
|
687
899
|
chance.reset();
|
|
688
900
|
|
|
689
901
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
690
902
|
try expectProbability(&chance.probability, 1, 3);
|
|
691
|
-
try expectValue(1, chance.actions.p1.move_slot);
|
|
903
|
+
try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
|
|
692
904
|
}
|
|
693
905
|
|
|
694
906
|
test "Chance.multiHit" {
|
|
@@ -696,25 +908,142 @@ test "Chance.multiHit" {
|
|
|
696
908
|
|
|
697
909
|
try chance.multiHit(.P1, 3);
|
|
698
910
|
try expectProbability(&chance.probability, 3, 8);
|
|
699
|
-
try expectValue(3, chance.actions.p1.multi_hit);
|
|
911
|
+
try expectValue(@as(u4, 3), chance.actions.p1.multi_hit);
|
|
700
912
|
|
|
701
913
|
chance.reset();
|
|
702
914
|
|
|
703
915
|
try chance.multiHit(.P2, 5);
|
|
704
916
|
try expectProbability(&chance.probability, 1, 8);
|
|
705
|
-
try expectValue(5, chance.actions.p2.multi_hit);
|
|
917
|
+
try expectValue(@as(u4, 5), chance.actions.p2.multi_hit);
|
|
706
918
|
}
|
|
707
919
|
|
|
708
920
|
test "Chance.duration" {
|
|
709
921
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
710
922
|
|
|
711
923
|
chance.duration(.P1, 2);
|
|
712
|
-
try expectValue(2, chance.actions.p1.duration);
|
|
924
|
+
try expectValue(@as(u4, 2), chance.actions.p1.duration);
|
|
713
925
|
|
|
714
926
|
chance.reset();
|
|
715
927
|
|
|
716
928
|
chance.duration(.P2, 4);
|
|
717
|
-
try expectValue(4, chance.actions.p2.duration);
|
|
929
|
+
try expectValue(@as(u4, 4), chance.actions.p2.duration);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
test "Chance.sleep" {
|
|
933
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
934
|
+
|
|
935
|
+
for ([_]u8{ 7, 6, 5, 4, 3, 2, 1 }, 1..8) |d, i| {
|
|
936
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
937
|
+
try chance.sleep(.P1, .ended);
|
|
938
|
+
try expectProbability(&chance.probability, 1, d);
|
|
939
|
+
try expectValue(@as(u3, 0), Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
940
|
+
|
|
941
|
+
chance.reset();
|
|
942
|
+
|
|
943
|
+
if (i < 7) {
|
|
944
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
945
|
+
try chance.sleep(.P1, .continuing);
|
|
946
|
+
try expectProbability(&chance.probability, d - 1, d);
|
|
947
|
+
try expectValue(@as(u3, @intCast(i)) + 1, Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
948
|
+
|
|
949
|
+
chance.reset();
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
test "Chance.confusion" {
|
|
955
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
956
|
+
|
|
957
|
+
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
958
|
+
if (i > 1) {
|
|
959
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
960
|
+
try chance.confusion(.P2, .ended);
|
|
961
|
+
try expectProbability(&chance.probability, 1, d);
|
|
962
|
+
try expectValue(@as(u3, 0), chance.durations.p2.confusion);
|
|
963
|
+
|
|
964
|
+
chance.reset();
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
if (i < 5) {
|
|
968
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
969
|
+
try chance.confusion(.P2, .continuing);
|
|
970
|
+
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
971
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.confusion);
|
|
972
|
+
|
|
973
|
+
chance.reset();
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
test "Chance.disable" {
|
|
979
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
980
|
+
|
|
981
|
+
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
982
|
+
chance.durations.p1.disable = @intCast(i);
|
|
983
|
+
try chance.disable(.P1, .ended);
|
|
984
|
+
try expectProbability(&chance.probability, 1, d);
|
|
985
|
+
try expectValue(@as(u4, 0), chance.durations.p1.disable);
|
|
986
|
+
|
|
987
|
+
chance.reset();
|
|
988
|
+
|
|
989
|
+
if (i < 8) {
|
|
990
|
+
chance.durations.p1.disable = @intCast(i);
|
|
991
|
+
try chance.disable(.P1, .continuing);
|
|
992
|
+
try expectProbability(&chance.probability, d - 1, d);
|
|
993
|
+
try expectValue(@as(u4, @intCast(i)) + 1, chance.durations.p1.disable);
|
|
994
|
+
|
|
995
|
+
chance.reset();
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
test "Chance.attacking" {
|
|
1001
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1002
|
+
|
|
1003
|
+
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
1004
|
+
if (i > 1) {
|
|
1005
|
+
chance.durations.p2.attacking = @intCast(i);
|
|
1006
|
+
try chance.attacking(.P2, .ended);
|
|
1007
|
+
try expectProbability(&chance.probability, 1, d);
|
|
1008
|
+
try expectValue(@as(u3, 0), chance.durations.p2.attacking);
|
|
1009
|
+
|
|
1010
|
+
chance.reset();
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
if (i < 3) {
|
|
1014
|
+
chance.durations.p2.attacking = @intCast(i);
|
|
1015
|
+
try chance.attacking(.P2, .continuing);
|
|
1016
|
+
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1017
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.attacking);
|
|
1018
|
+
|
|
1019
|
+
chance.reset();
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
test "Chance.binding" {
|
|
1025
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1026
|
+
|
|
1027
|
+
const ps = [_]u8{ 3, 3, 1, 1 };
|
|
1028
|
+
const qs = [_]u8{ 8, 5, 2, 1 };
|
|
1029
|
+
|
|
1030
|
+
for (ps, qs, 1..5) |p, q, i| {
|
|
1031
|
+
chance.durations.p1.binding = @intCast(i);
|
|
1032
|
+
try chance.binding(.P1, .ended);
|
|
1033
|
+
try expectProbability(&chance.probability, p, q);
|
|
1034
|
+
try expectValue(@as(u3, 0), chance.durations.p1.binding);
|
|
1035
|
+
|
|
1036
|
+
chance.reset();
|
|
1037
|
+
|
|
1038
|
+
if (i < 4) {
|
|
1039
|
+
chance.durations.p1.binding = @intCast(i);
|
|
1040
|
+
try chance.binding(.P1, .continuing);
|
|
1041
|
+
try expectProbability(&chance.probability, q - p, q);
|
|
1042
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.binding);
|
|
1043
|
+
|
|
1044
|
+
chance.reset();
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
718
1047
|
}
|
|
719
1048
|
|
|
720
1049
|
test "Chance.psywave" {
|
|
@@ -722,7 +1051,7 @@ test "Chance.psywave" {
|
|
|
722
1051
|
|
|
723
1052
|
try chance.psywave(.P2, 100, 150);
|
|
724
1053
|
try expectProbability(&chance.probability, 1, 150);
|
|
725
|
-
try expectValue(101, chance.actions.p2.psywave);
|
|
1054
|
+
try expectValue(@as(u8, 101), chance.actions.p2.psywave);
|
|
726
1055
|
}
|
|
727
1056
|
|
|
728
1057
|
test "Chance.metronome" {
|
|
@@ -760,12 +1089,16 @@ const Null = struct {
|
|
|
760
1089
|
_ = .{ self, player, kind };
|
|
761
1090
|
}
|
|
762
1091
|
|
|
1092
|
+
pub fn glitch(self: Null) void {
|
|
1093
|
+
_ = .{self};
|
|
1094
|
+
}
|
|
1095
|
+
|
|
763
1096
|
pub fn clearPending(self: Null) void {
|
|
764
1097
|
_ = .{self};
|
|
765
1098
|
}
|
|
766
1099
|
|
|
767
|
-
pub fn switched(self: Null, player: Player,
|
|
768
|
-
_ = .{ self, player,
|
|
1100
|
+
pub fn switched(self: Null, player: Player, slot: u8) void {
|
|
1101
|
+
_ = .{ self, player, slot };
|
|
769
1102
|
}
|
|
770
1103
|
|
|
771
1104
|
pub fn speedTie(self: Null, p1: bool) Error!void {
|
|
@@ -804,17 +1137,37 @@ const Null = struct {
|
|
|
804
1137
|
_ = .{ self, player, n };
|
|
805
1138
|
}
|
|
806
1139
|
|
|
807
|
-
pub fn
|
|
1140
|
+
pub fn duration(self: Null, player: Player, turns: u4) void {
|
|
1141
|
+
_ = .{ self, player, turns };
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
pub fn observe(
|
|
808
1145
|
self: Null,
|
|
809
|
-
comptime
|
|
1146
|
+
comptime field: Action.Field,
|
|
810
1147
|
player: Player,
|
|
811
|
-
|
|
1148
|
+
obs: Optional(Confusion),
|
|
812
1149
|
) void {
|
|
813
|
-
_ = .{ self,
|
|
1150
|
+
_ = .{ self, field, player, obs };
|
|
814
1151
|
}
|
|
815
1152
|
|
|
816
|
-
pub fn
|
|
817
|
-
_ = .{ self, player,
|
|
1153
|
+
pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1154
|
+
_ = .{ self, player, obs };
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
pub fn confusion(self: Null, player: Player, obs: Optional(Confusion)) Error!void {
|
|
1158
|
+
_ = .{ self, player, obs };
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
pub fn disable(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1162
|
+
_ = .{ self, player, obs };
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
pub fn attacking(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1166
|
+
_ = .{ self, player, obs };
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
pub fn binding(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1170
|
+
_ = .{ self, player, obs };
|
|
818
1171
|
}
|
|
819
1172
|
|
|
820
1173
|
pub fn psywave(self: Null, player: Player, power: u8, max: u8) Error!void {
|
|
@@ -824,4 +1177,9 @@ const Null = struct {
|
|
|
824
1177
|
pub fn metronome(self: Null, player: Player, move: Move) Error!void {
|
|
825
1178
|
_ = .{ self, player, move };
|
|
826
1179
|
}
|
|
1180
|
+
|
|
1181
|
+
pub fn pp(self: Null, player: Player, n: u4) bool {
|
|
1182
|
+
_ = .{ self, player, n };
|
|
1183
|
+
return true;
|
|
1184
|
+
}
|
|
827
1185
|
};
|