@pkmn/engine 0.1.0-dev.89cc1c98 → 0.1.0-dev.8b192003
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 +44 -19
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +4 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +5 -5
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +2 -2
- 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 +100 -102
- package/package.json +31 -23
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +393 -396
- package/src/lib/common/rational.zig +109 -15
- package/src/lib/common/rng.zig +20 -11
- package/src/lib/gen1/calc.zig +478 -92
- package/src/lib/gen1/chance.zig +195 -428
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +21 -21
- package/src/lib/gen1/helpers.zig +88 -534
- package/src/lib/gen1/mechanics.zig +377 -360
- package/src/lib/gen2/chance.zig +25 -457
- package/src/lib/gen2/data.zig +13 -11
- package/src/lib/gen2/generated.zig +27 -78
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +1022 -456
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +27 -12
- 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 -894
- package/src/lib/gen2/README.md +0 -330
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -14,7 +14,7 @@ const util = @import("../common/util.zig");
|
|
|
14
14
|
const Player = @import("../common/data.zig").Player;
|
|
15
15
|
const Optional = @import("../common/optional.zig").Optional;
|
|
16
16
|
|
|
17
|
-
const data = @import("
|
|
17
|
+
const data = @import("data.zig");
|
|
18
18
|
|
|
19
19
|
const enabled = options.chance;
|
|
20
20
|
const showdown = options.showdown;
|
|
@@ -42,12 +42,6 @@ pub const Actions = extern struct {
|
|
|
42
42
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/// Perform a reset by clearing fields which should not persist across updates.
|
|
46
|
-
pub fn reset(self: *Actions) void {
|
|
47
|
-
self.p1.reset();
|
|
48
|
-
self.p2.reset();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
45
|
/// Returns true if `a` is equal to `b`.
|
|
52
46
|
pub fn eql(a: Actions, b: Actions) bool {
|
|
53
47
|
return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b));
|
|
@@ -63,11 +57,6 @@ pub const Actions = extern struct {
|
|
|
63
57
|
const b_val = @field(@field(b, player.name), field.name);
|
|
64
58
|
|
|
65
59
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
66
|
-
.Struct => inline for (@typeInfo(@TypeOf(a_val)).Struct.fields) |f| {
|
|
67
|
-
if ((@field(a_val, f.name) > 0) != (@field(b_val, f.name) > 0)) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
60
|
.Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
72
61
|
return false,
|
|
73
62
|
.Int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
@@ -96,9 +85,6 @@ test Actions {
|
|
|
96
85
|
const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
|
|
97
86
|
const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
|
|
98
87
|
const c: Actions = .{ .p1 = .{ .hit = .true } };
|
|
99
|
-
const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
|
|
100
|
-
const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
101
|
-
const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
102
88
|
|
|
103
89
|
try expect(a.eql(a));
|
|
104
90
|
try expect(!a.eql(b));
|
|
@@ -111,10 +97,10 @@ test Actions {
|
|
|
111
97
|
try expect(b.matches(a));
|
|
112
98
|
try expect(!a.matches(c));
|
|
113
99
|
try expect(!c.matches(a));
|
|
114
|
-
try expect(d.matches(e));
|
|
115
|
-
try expect(!d.matches(f));
|
|
116
100
|
}
|
|
117
101
|
|
|
102
|
+
pub const Observation = enum { started, continuing, ended };
|
|
103
|
+
|
|
118
104
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
119
105
|
/// single player.
|
|
120
106
|
pub const Action = packed struct(u64) {
|
|
@@ -139,8 +125,15 @@ pub const Action = packed struct(u64) {
|
|
|
139
125
|
/// or Rolls.{sleepDuration,disableDuration,confusionDuration,attackingDuration} otherwise.
|
|
140
126
|
duration: u4 = 0,
|
|
141
127
|
|
|
142
|
-
|
|
143
|
-
|
|
128
|
+
// TODO
|
|
129
|
+
sleep: Optional(Observation) = .None,
|
|
130
|
+
confusion: Optional(Observation) = .None,
|
|
131
|
+
disable: Optional(Observation) = .None,
|
|
132
|
+
bide: Optional(Observation) = .None,
|
|
133
|
+
binding: Optional(Observation) = .None,
|
|
134
|
+
thrashing: Optional(Observation) = .None,
|
|
135
|
+
|
|
136
|
+
_: u4 = 0, // TODO
|
|
144
137
|
|
|
145
138
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
146
139
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
@@ -154,15 +147,8 @@ pub const Action = packed struct(u64) {
|
|
|
154
147
|
/// If not None, the Move to return for Rolls.metronome.
|
|
155
148
|
metronome: Move = .None,
|
|
156
149
|
|
|
157
|
-
pub const DURATIONS: u64 = 0x000000FFFF000000;
|
|
158
|
-
|
|
159
150
|
pub const Field = std.meta.FieldEnum(Action);
|
|
160
151
|
|
|
161
|
-
/// Perform a reset by clearing fields which should not persist across updates.
|
|
162
|
-
pub fn reset(self: *Action) void {
|
|
163
|
-
self.* = @bitCast(@as(u64, @bitCast(self.*)) & DURATIONS);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
152
|
pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
|
|
167
153
|
_ = .{ f, o };
|
|
168
154
|
try fmt(a, w, false);
|
|
@@ -174,18 +160,6 @@ pub const Action = packed struct(u64) {
|
|
|
174
160
|
inline for (@typeInfo(Action).Struct.fields) |field| {
|
|
175
161
|
const val = @field(self, field.name);
|
|
176
162
|
switch (@typeInfo(@TypeOf(val))) {
|
|
177
|
-
.Struct => inline for (@typeInfo(@TypeOf(val)).Struct.fields) |f| {
|
|
178
|
-
const v = @field(val, f.name);
|
|
179
|
-
if (v != 0) {
|
|
180
|
-
if (printed) try writer.writeAll(", ");
|
|
181
|
-
if (shape) {
|
|
182
|
-
try writer.print("{s}:?", .{f.name});
|
|
183
|
-
} else {
|
|
184
|
-
try writer.print("{s}:{d}", .{ f.name, v });
|
|
185
|
-
}
|
|
186
|
-
printed = true;
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
163
|
.Enum => if (val != .None) {
|
|
190
164
|
if (printed) try writer.writeAll(", ");
|
|
191
165
|
if (shape) {
|
|
@@ -195,6 +169,19 @@ pub const Action = packed struct(u64) {
|
|
|
195
169
|
if (val == .false) "!" else "",
|
|
196
170
|
field.name,
|
|
197
171
|
});
|
|
172
|
+
} else if (@TypeOf(val) == Optional(Observation)) {
|
|
173
|
+
try writer.print("{s}{s}", .{
|
|
174
|
+
switch (val) {
|
|
175
|
+
.started => "+",
|
|
176
|
+
.continuing => "",
|
|
177
|
+
.ended => if (std.mem.eql(u8, field.name, "thrashing"))
|
|
178
|
+
"~"
|
|
179
|
+
else
|
|
180
|
+
"-",
|
|
181
|
+
else => unreachable,
|
|
182
|
+
},
|
|
183
|
+
field.name,
|
|
184
|
+
});
|
|
198
185
|
} else {
|
|
199
186
|
try writer.print("{s}:{s}", .{ field.name, @tagName(val) });
|
|
200
187
|
}
|
|
@@ -216,43 +203,111 @@ pub const Action = packed struct(u64) {
|
|
|
216
203
|
}
|
|
217
204
|
};
|
|
218
205
|
|
|
219
|
-
test Action {
|
|
220
|
-
var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
|
|
221
|
-
a.reset();
|
|
222
|
-
|
|
223
|
-
try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
206
|
/// TODO
|
|
227
|
-
pub const Durations =
|
|
228
|
-
///
|
|
207
|
+
pub const Durations = struct {
|
|
208
|
+
/// TODO
|
|
229
209
|
p1: Duration = .{},
|
|
230
|
-
///
|
|
210
|
+
/// TODO
|
|
231
211
|
p2: Duration = .{},
|
|
232
212
|
|
|
233
|
-
|
|
234
|
-
|
|
213
|
+
/// TODO
|
|
214
|
+
pub fn get(self: anytype, player: Player) util.PointerType(@TypeOf(self), Duration) {
|
|
215
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Durations);
|
|
216
|
+
return if (player == .P1) &self.p1 else &self.p2;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// TODO
|
|
220
|
+
pub fn update(self: *Durations, battle: anytype, q: anytype, actions: Actions) !void {
|
|
221
|
+
for (&battle.sides, 0..) |*s, i| {
|
|
222
|
+
const p: Player = @enumFromInt(i);
|
|
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
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
pub fn format(
|
|
251
|
+
self: Durations,
|
|
252
|
+
comptime fmt: []const u8,
|
|
253
|
+
opts: std.fmt.FormatOptions,
|
|
254
|
+
writer: anytype,
|
|
255
|
+
) !void {
|
|
256
|
+
_ = .{ fmt, opts };
|
|
257
|
+
try writer.print("[P1 = {}, P2 = {}]", .{ self.p1, self.p2 });
|
|
235
258
|
}
|
|
236
259
|
};
|
|
237
260
|
|
|
238
|
-
///
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
/// The number of turns a Pokémon has been observed to be sleeping.
|
|
242
|
-
sleep: u3 = 0,
|
|
243
|
-
/// The number of turns a Pokémon has been observed to be confused.
|
|
261
|
+
/// TODO
|
|
262
|
+
pub const Duration = struct {
|
|
263
|
+
sleeps: [6]u3 = .{0} ** 6,
|
|
244
264
|
confusion: u3 = 0,
|
|
245
|
-
/// The number of turns a Pokémon has been observed to be disabled.
|
|
246
265
|
disable: u4 = 0,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
/// The number of turns a Pokémon has been observed to be binding their opponent.
|
|
266
|
+
bide: u3 = 0,
|
|
267
|
+
thrashing: u3 = 0,
|
|
250
268
|
binding: u3 = 0,
|
|
251
269
|
|
|
252
|
-
pub
|
|
270
|
+
pub fn format(
|
|
271
|
+
self: Duration,
|
|
272
|
+
comptime fmt: []const u8,
|
|
273
|
+
opts: std.fmt.FormatOptions,
|
|
274
|
+
writer: anytype,
|
|
275
|
+
) !void {
|
|
276
|
+
_ = .{ fmt, opts };
|
|
277
|
+
try writer.writeByte('(');
|
|
278
|
+
var printed = false;
|
|
279
|
+
inline for (@typeInfo(Duration).Struct.fields) |field| {
|
|
280
|
+
const val = @field(self, field.name);
|
|
281
|
+
switch (@typeInfo(@TypeOf(val))) {
|
|
282
|
+
.Int => if (val != 0) {
|
|
283
|
+
if (printed) try writer.writeAll(", ");
|
|
284
|
+
try writer.print("{s}:{d}", .{ field.name, val });
|
|
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(", ");
|
|
292
|
+
try writer.print("{s}:[", .{field.name});
|
|
293
|
+
for (val, 0..) |v, i| {
|
|
294
|
+
if (i != 0) try writer.writeByte(',');
|
|
295
|
+
try writer.print("{d}", .{v});
|
|
296
|
+
}
|
|
297
|
+
try writer.writeAll("]");
|
|
298
|
+
printed = true;
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
else => unreachable,
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
try writer.writeByte(')');
|
|
305
|
+
}
|
|
253
306
|
};
|
|
254
307
|
|
|
255
|
-
pub const Commit = enum { hit, miss
|
|
308
|
+
pub const Commit = enum { hit, miss };
|
|
309
|
+
|
|
310
|
+
const uN = if (options.miss) u8 else u9;
|
|
256
311
|
|
|
257
312
|
/// Tracks chance actions and their associated probability during a Generation I battle update when
|
|
258
313
|
/// `options.chance` is enabled.
|
|
@@ -266,14 +321,6 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
266
321
|
/// RNG events were observed during a battle `update`.
|
|
267
322
|
actions: Actions = .{},
|
|
268
323
|
|
|
269
|
-
// Tracks the observed sleep durations for the Pokémon in both player's parties. Unlike
|
|
270
|
-
// other durations which are all tied to volatiles, sleep's counter persists through
|
|
271
|
-
// switching and so we must store it here. The indices of these arrays correspond to the
|
|
272
|
-
// `order` field of a Side. This information could be stored in actions but size is a
|
|
273
|
-
// concern so this is tracked separately as actions only purports to track RNG events
|
|
274
|
-
// observed during a single `update` (not across updates).
|
|
275
|
-
sleeps: [2][6]u3 = .{ .{0} ** 6, .{0} ** 6 },
|
|
276
|
-
|
|
277
324
|
// In many cases on both the cartridge and on Pokémon Showdown rolls are made even though
|
|
278
325
|
// later checks render their result irrelevant (missing when the target is actually immune,
|
|
279
326
|
// rolling for damage when the move is later revealed to have missed, etc). We can't reorder
|
|
@@ -290,16 +337,12 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
290
337
|
//
|
|
291
338
|
// Finally, because each player's actions are processed sequentially we only need a single
|
|
292
339
|
// shared structure to track this information (though it needs to be cleared appropriately)
|
|
293
|
-
pending: if (showdown) struct {
|
|
294
|
-
hit: bool = false,
|
|
295
|
-
hit_probablity: u8 = 0,
|
|
296
|
-
} else struct {
|
|
340
|
+
pending: if (showdown) struct {} else struct {
|
|
297
341
|
crit: bool = false,
|
|
298
342
|
crit_probablity: u8 = 0,
|
|
299
343
|
damage_roll: u8 = 0,
|
|
300
344
|
hit: bool = false,
|
|
301
|
-
hit_probablity:
|
|
302
|
-
binding: u8 = 0,
|
|
345
|
+
hit_probablity: uN = 0,
|
|
303
346
|
} = .{},
|
|
304
347
|
|
|
305
348
|
/// Possible error returned by operations tracking chance probability.
|
|
@@ -307,25 +350,19 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
307
350
|
|
|
308
351
|
/// Convenience helper to clear fields which typically should be cleared between updates.
|
|
309
352
|
pub fn reset(self: *Self) void {
|
|
353
|
+
if (!enabled) return;
|
|
354
|
+
|
|
310
355
|
self.probability.reset();
|
|
311
|
-
self.actions.
|
|
356
|
+
self.actions = .{};
|
|
312
357
|
self.pending = .{};
|
|
313
358
|
}
|
|
314
359
|
|
|
315
360
|
pub fn commit(self: *Self, player: Player, kind: Commit) Error!void {
|
|
316
|
-
|
|
361
|
+
if (!enabled) return;
|
|
317
362
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (kind != .miss and self.pending.binding != 0) {
|
|
322
|
-
assert(action.duration == 0);
|
|
323
|
-
action.duration = @intCast(self.pending.binding);
|
|
324
|
-
assert(action.durations.binding == 0);
|
|
325
|
-
action.durations.binding = 1;
|
|
326
|
-
}
|
|
327
|
-
if (kind == .binding) return;
|
|
328
|
-
} else assert(kind != .binding);
|
|
363
|
+
assert(!showdown);
|
|
364
|
+
|
|
365
|
+
var action = self.actions.get(player);
|
|
329
366
|
|
|
330
367
|
// Always commit the hit result if we make it here (commit won't be called at all if the
|
|
331
368
|
// target is immune/behind a sub/etc)
|
|
@@ -334,19 +371,19 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
334
371
|
action.hit = if (self.pending.hit) .true else .false;
|
|
335
372
|
}
|
|
336
373
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
// to
|
|
342
|
-
if (kind
|
|
343
|
-
assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
374
|
+
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
375
|
+
// 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 rol for
|
|
377
|
+
// 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
|
+
if (kind != .hit) return assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
344
380
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
381
|
+
if (self.pending.crit_probablity != 0) {
|
|
382
|
+
try self.probability.update(self.pending.crit_probablity, 256);
|
|
383
|
+
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
384
|
+
}
|
|
349
385
|
|
|
386
|
+
if (self.pending.damage_roll > 0) {
|
|
350
387
|
try self.probability.update(1, 39);
|
|
351
388
|
action.damage = self.pending.damage_roll;
|
|
352
389
|
}
|
|
@@ -358,37 +395,6 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
358
395
|
self.pending = .{};
|
|
359
396
|
}
|
|
360
397
|
|
|
361
|
-
pub fn clearDurations(self: *Self, player: Player, haze: ?bool) void {
|
|
362
|
-
if (!enabled) return;
|
|
363
|
-
|
|
364
|
-
var action = self.actions.get(player);
|
|
365
|
-
if (haze) |status| {
|
|
366
|
-
if (status) action.durations.sleep = 0;
|
|
367
|
-
action.durations.confusion = 0;
|
|
368
|
-
action.durations.disable = 0;
|
|
369
|
-
} else {
|
|
370
|
-
action.durations.attacking = 0;
|
|
371
|
-
action.durations.binding = 0;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
pub fn switched(self: *Self, player: Player, in: u8, out: u8) void {
|
|
376
|
-
if (!enabled) return;
|
|
377
|
-
|
|
378
|
-
assert(in >= 1 and in <= 6);
|
|
379
|
-
assert(out >= 1 and out <= 6);
|
|
380
|
-
|
|
381
|
-
var action = self.actions.get(player);
|
|
382
|
-
|
|
383
|
-
const slp = action.durations.sleep;
|
|
384
|
-
action.durations = .{};
|
|
385
|
-
|
|
386
|
-
self.sleeps[@intFromEnum(player)][out - 1] = slp;
|
|
387
|
-
action.durations.sleep = @intCast(self.sleeps[@intFromEnum(player)][in - 1]);
|
|
388
|
-
|
|
389
|
-
self.actions.get(player.foe()).durations.binding = 0;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
398
|
pub fn speedTie(self: *Self, p1: bool) Error!void {
|
|
393
399
|
if (!enabled) return;
|
|
394
400
|
|
|
@@ -397,12 +403,17 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
397
403
|
self.actions.p2.speed_tie = self.actions.p1.speed_tie;
|
|
398
404
|
}
|
|
399
405
|
|
|
400
|
-
pub fn hit(self: *Self, ok: bool, accuracy:
|
|
406
|
+
pub fn hit(self: *Self, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
401
407
|
if (!enabled) return;
|
|
402
408
|
|
|
403
409
|
const p = if (ok) accuracy else @as(u8, @intCast(256 - @as(u9, accuracy)));
|
|
404
|
-
|
|
405
|
-
|
|
410
|
+
if (showdown) {
|
|
411
|
+
try self.probability.update(p, 256);
|
|
412
|
+
self.actions.get(player).hit = if (ok) .true else .false;
|
|
413
|
+
} else {
|
|
414
|
+
self.pending.hit = ok;
|
|
415
|
+
self.pending.hit_probablity = p;
|
|
416
|
+
}
|
|
406
417
|
}
|
|
407
418
|
|
|
408
419
|
pub fn criticalHit(self: *Self, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -480,109 +491,21 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
480
491
|
self.actions.get(player).multi_hit = n;
|
|
481
492
|
}
|
|
482
493
|
|
|
483
|
-
pub fn duration(
|
|
484
|
-
self: *Self,
|
|
485
|
-
comptime field: Duration.Field,
|
|
486
|
-
player: Player,
|
|
487
|
-
target: Player,
|
|
488
|
-
turns: u4,
|
|
489
|
-
) void {
|
|
490
|
-
if (!enabled) return;
|
|
491
|
-
|
|
492
|
-
if (!showdown and field == .binding) {
|
|
493
|
-
self.pending.binding = if (options.key) 1 else turns;
|
|
494
|
-
} else {
|
|
495
|
-
var durations = &self.actions.get(target).durations;
|
|
496
|
-
assert(@field(durations, @tagName(field)) == 0 or
|
|
497
|
-
(field == .confusion and player == target));
|
|
498
|
-
@field(durations, @tagName(field)) = 1;
|
|
499
|
-
self.actions.get(player).duration = if (options.key) 1 else turns;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
pub fn sleep(self: *Self, player: Player, turns: u4) Error!void {
|
|
504
|
-
if (!enabled) return;
|
|
505
|
-
|
|
506
|
-
var durations = &self.actions.get(player).durations;
|
|
507
|
-
const n = durations.sleep;
|
|
508
|
-
if (turns == 0) {
|
|
509
|
-
assert(n >= 1 and n <= 7);
|
|
510
|
-
if (n != 7) try self.probability.update(1, 8 - @as(u4, n));
|
|
511
|
-
durations.sleep = 0;
|
|
512
|
-
} else {
|
|
513
|
-
assert(n >= 1 and n < 7);
|
|
514
|
-
try self.probability.update(8 - @as(u4, n) - 1, 8 - @as(u4, n));
|
|
515
|
-
durations.sleep += 1;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
|
|
494
|
+
pub fn duration(self: *Self, player: Player, turns: u4) void {
|
|
520
495
|
if (!enabled) return;
|
|
521
496
|
|
|
522
|
-
|
|
523
|
-
const n = durations.confusion;
|
|
524
|
-
if (turns == 0) {
|
|
525
|
-
assert(n >= 2 and n <= 5);
|
|
526
|
-
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
527
|
-
durations.confusion = 0;
|
|
528
|
-
} else {
|
|
529
|
-
assert(n >= 1 and n < 5);
|
|
530
|
-
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
531
|
-
durations.confusion += 1;
|
|
532
|
-
}
|
|
497
|
+
self.actions.get(player).duration = if (options.key) 1 else turns;
|
|
533
498
|
}
|
|
534
499
|
|
|
535
|
-
pub fn
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
assert(n >= 1 and n <= 8);
|
|
542
|
-
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
543
|
-
durations.disable = 0;
|
|
544
|
-
} else {
|
|
545
|
-
assert(n >= 1 and n < 8);
|
|
546
|
-
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
547
|
-
durations.disable += 1;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
|
|
552
|
-
if (!enabled) return;
|
|
553
|
-
|
|
554
|
-
var durations = &self.actions.get(player).durations;
|
|
555
|
-
const n = durations.attacking;
|
|
556
|
-
if (turns == 0) {
|
|
557
|
-
assert(n >= 2 and n <= 3);
|
|
558
|
-
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
559
|
-
durations.attacking = 0;
|
|
560
|
-
} else {
|
|
561
|
-
assert(n >= 1 and n < 3);
|
|
562
|
-
if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
|
|
563
|
-
durations.attacking += 1;
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
pub fn binding(self: *Self, player: Player, turns: u4) Error!void {
|
|
500
|
+
pub fn durations(
|
|
501
|
+
self: *Self,
|
|
502
|
+
comptime f: Action.Field,
|
|
503
|
+
player: Player,
|
|
504
|
+
observation: Optional(Observation),
|
|
505
|
+
) void {
|
|
568
506
|
if (!enabled) return;
|
|
569
507
|
|
|
570
|
-
|
|
571
|
-
const n = durations.binding;
|
|
572
|
-
|
|
573
|
-
assert(n > 0);
|
|
574
|
-
const p: u4 = if (n < 3) 3 else 1;
|
|
575
|
-
const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
|
|
576
|
-
|
|
577
|
-
if (turns == 0) {
|
|
578
|
-
assert(n >= 1 and n <= 4);
|
|
579
|
-
if (n != 4) try self.probability.update(p, q);
|
|
580
|
-
durations.binding = 0;
|
|
581
|
-
} else {
|
|
582
|
-
assert(n >= 1 and n < 4);
|
|
583
|
-
try self.probability.update(q - p, q);
|
|
584
|
-
durations.binding += 1;
|
|
585
|
-
}
|
|
508
|
+
@field(self.actions.get(player), @tagName(f)) = observation;
|
|
586
509
|
}
|
|
587
510
|
|
|
588
511
|
pub fn psywave(self: *Self, player: Player, power: u8, max: u8) Error!void {
|
|
@@ -620,19 +543,25 @@ test "Chance.speedTie" {
|
|
|
620
543
|
test "Chance.hit" {
|
|
621
544
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
622
545
|
|
|
623
|
-
chance.hit(true, 229);
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
546
|
+
try chance.hit(.P1, true, 229);
|
|
547
|
+
if (!showdown) {
|
|
548
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
549
|
+
try expectValue(Optional(bool).None, chance.actions.p1.hit);
|
|
550
|
+
|
|
551
|
+
try chance.commit(.P1, .hit);
|
|
552
|
+
}
|
|
627
553
|
try expectValue(Optional(bool).true, chance.actions.p1.hit);
|
|
628
554
|
try expectProbability(&chance.probability, 229, 256);
|
|
629
555
|
|
|
630
556
|
chance.reset();
|
|
631
557
|
|
|
632
|
-
chance.hit(false, 255);
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
558
|
+
try chance.hit(.P2, false, 255);
|
|
559
|
+
if (!showdown) {
|
|
560
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
561
|
+
try expectValue(Optional(bool).None, chance.actions.p2.hit);
|
|
562
|
+
|
|
563
|
+
try chance.commit(.P2, .miss);
|
|
564
|
+
}
|
|
636
565
|
try expectValue(Optional(bool).false, chance.actions.p2.hit);
|
|
637
566
|
try expectProbability(&chance.probability, 1, 256);
|
|
638
567
|
}
|
|
@@ -648,26 +577,18 @@ test "Chance.criticalHit" {
|
|
|
648
577
|
try expectProbability(&chance.probability, 1, 1);
|
|
649
578
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
650
579
|
|
|
651
|
-
try chance.
|
|
652
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
653
|
-
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
580
|
+
try chance.hit(.P1, true, 128);
|
|
654
581
|
|
|
655
|
-
chance.
|
|
656
|
-
|
|
657
|
-
try chance.commit(.P1, .hit);
|
|
658
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
582
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
659
583
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
660
584
|
|
|
661
|
-
chance.probability.reset();
|
|
662
585
|
try chance.damage(.P1, 217);
|
|
663
586
|
|
|
664
|
-
try chance.
|
|
665
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
587
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
666
588
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
667
589
|
|
|
668
|
-
chance.probability.reset();
|
|
669
|
-
|
|
670
590
|
try chance.commit(.P1, .hit);
|
|
591
|
+
|
|
671
592
|
try expectProbability(&chance.probability, 17, 19968); // (1/2) * (17/256) * (1/39)
|
|
672
593
|
try expectValue(Optional(bool).true, chance.actions.p1.critical_hit);
|
|
673
594
|
}
|
|
@@ -682,20 +603,14 @@ test "Chance.criticalHit" {
|
|
|
682
603
|
try expectProbability(&chance.probability, 1, 1);
|
|
683
604
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
684
605
|
|
|
685
|
-
try chance.
|
|
686
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
687
|
-
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
688
|
-
|
|
689
|
-
chance.hit(true, 128);
|
|
606
|
+
try chance.hit(.P1, true, 128);
|
|
690
607
|
|
|
691
|
-
try chance.
|
|
692
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
608
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
693
609
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
694
610
|
|
|
695
|
-
chance.probability.reset();
|
|
696
611
|
try chance.damage(.P1, 217);
|
|
697
|
-
|
|
698
612
|
try chance.commit(.P1, .hit);
|
|
613
|
+
|
|
699
614
|
try expectProbability(&chance.probability, 251, 19968); // (1/2) * (251/256) * (1/39)
|
|
700
615
|
try expectValue(Optional(bool).false, chance.actions.p1.critical_hit);
|
|
701
616
|
}
|
|
@@ -706,19 +621,16 @@ test "Chance.damage" {
|
|
|
706
621
|
|
|
707
622
|
try chance.damage(.P1, 217);
|
|
708
623
|
if (showdown) {
|
|
709
|
-
try expectValue(
|
|
624
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
710
625
|
try expectProbability(&chance.probability, 1, 39);
|
|
711
626
|
} else {
|
|
712
627
|
try expectProbability(&chance.probability, 1, 1);
|
|
713
|
-
try expectValue(
|
|
714
|
-
|
|
715
|
-
try chance.commit(.P1, .miss);
|
|
716
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
717
|
-
try expectValue(@as(u8, 0), chance.actions.p1.damage);
|
|
628
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
718
629
|
|
|
719
630
|
try chance.commit(.P1, .hit);
|
|
631
|
+
|
|
720
632
|
try expectProbability(&chance.probability, 1, 39);
|
|
721
|
-
try expectValue(
|
|
633
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
722
634
|
}
|
|
723
635
|
}
|
|
724
636
|
|
|
@@ -770,13 +682,13 @@ test "Chance.moveSlot" {
|
|
|
770
682
|
|
|
771
683
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
772
684
|
try expectProbability(&chance.probability, 1, 2);
|
|
773
|
-
try expectValue(
|
|
685
|
+
try expectValue(2, chance.actions.p2.move_slot);
|
|
774
686
|
|
|
775
687
|
chance.reset();
|
|
776
688
|
|
|
777
689
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
778
690
|
try expectProbability(&chance.probability, 1, 3);
|
|
779
|
-
try expectValue(
|
|
691
|
+
try expectValue(1, chance.actions.p1.move_slot);
|
|
780
692
|
}
|
|
781
693
|
|
|
782
694
|
test "Chance.multiHit" {
|
|
@@ -784,149 +696,25 @@ test "Chance.multiHit" {
|
|
|
784
696
|
|
|
785
697
|
try chance.multiHit(.P1, 3);
|
|
786
698
|
try expectProbability(&chance.probability, 3, 8);
|
|
787
|
-
try expectValue(
|
|
699
|
+
try expectValue(3, chance.actions.p1.multi_hit);
|
|
788
700
|
|
|
789
701
|
chance.reset();
|
|
790
702
|
|
|
791
703
|
try chance.multiHit(.P2, 5);
|
|
792
704
|
try expectProbability(&chance.probability, 1, 8);
|
|
793
|
-
try expectValue(
|
|
705
|
+
try expectValue(5, chance.actions.p2.multi_hit);
|
|
794
706
|
}
|
|
795
707
|
|
|
796
708
|
test "Chance.duration" {
|
|
797
709
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
798
710
|
|
|
799
|
-
chance.duration(.
|
|
800
|
-
try expectValue(
|
|
801
|
-
try expectValue(@as(u3, 1), chance.actions.p2.durations.sleep);
|
|
711
|
+
chance.duration(.P1, 2);
|
|
712
|
+
try expectValue(2, chance.actions.p1.duration);
|
|
802
713
|
|
|
803
714
|
chance.reset();
|
|
804
715
|
|
|
805
|
-
chance.duration(.
|
|
806
|
-
|
|
807
|
-
try expectValue(@as(u4, 0), chance.actions.p2.duration);
|
|
808
|
-
try expectValue(@as(u3, 0), chance.actions.p2.durations.binding);
|
|
809
|
-
try chance.commit(.P2, .hit);
|
|
810
|
-
}
|
|
811
|
-
try expectValue(@as(u4, 4), chance.actions.p2.duration);
|
|
812
|
-
try expectValue(@as(u3, 1), chance.actions.p2.durations.binding);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
test "Chance.sleep" {
|
|
816
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
817
|
-
|
|
818
|
-
for ([_]u8{ 7, 6, 5, 4, 3, 2, 1 }, 1..8) |d, i| {
|
|
819
|
-
chance.actions.p1.durations.sleep = @intCast(i);
|
|
820
|
-
try chance.sleep(.P1, 0);
|
|
821
|
-
try expectProbability(&chance.probability, 1, d);
|
|
822
|
-
try expectValue(@as(u3, 0), chance.actions.p1.durations.sleep);
|
|
823
|
-
|
|
824
|
-
chance.reset();
|
|
825
|
-
|
|
826
|
-
if (i < 7) {
|
|
827
|
-
chance.actions.p1.durations.sleep = @intCast(i);
|
|
828
|
-
try chance.sleep(.P1, 1);
|
|
829
|
-
try expectProbability(&chance.probability, d - 1, d);
|
|
830
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p1.durations.sleep);
|
|
831
|
-
|
|
832
|
-
chance.reset();
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
test "Chance.confusion" {
|
|
838
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
839
|
-
|
|
840
|
-
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
841
|
-
if (i > 1) {
|
|
842
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
843
|
-
try chance.confusion(.P2, 0);
|
|
844
|
-
try expectProbability(&chance.probability, 1, d);
|
|
845
|
-
try expectValue(@as(u3, 0), chance.actions.p2.durations.confusion);
|
|
846
|
-
|
|
847
|
-
chance.reset();
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
if (i < 5) {
|
|
851
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
852
|
-
try chance.confusion(.P2, 1);
|
|
853
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
854
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.confusion);
|
|
855
|
-
|
|
856
|
-
chance.reset();
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
test "Chance.disable" {
|
|
862
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
863
|
-
|
|
864
|
-
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
865
|
-
chance.actions.p1.durations.disable = @intCast(i);
|
|
866
|
-
try chance.disable(.P1, 0);
|
|
867
|
-
try expectProbability(&chance.probability, 1, d);
|
|
868
|
-
try expectValue(@as(u4, 0), chance.actions.p1.durations.disable);
|
|
869
|
-
|
|
870
|
-
chance.reset();
|
|
871
|
-
|
|
872
|
-
if (i < 8) {
|
|
873
|
-
chance.actions.p1.durations.disable = @intCast(i);
|
|
874
|
-
try chance.disable(.P1, 1);
|
|
875
|
-
try expectProbability(&chance.probability, d - 1, d);
|
|
876
|
-
try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p1.durations.disable);
|
|
877
|
-
|
|
878
|
-
chance.reset();
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
test "Chance.attacking" {
|
|
884
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
885
|
-
|
|
886
|
-
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
887
|
-
if (i > 1) {
|
|
888
|
-
chance.actions.p2.durations.attacking = @intCast(i);
|
|
889
|
-
try chance.attacking(.P2, 0);
|
|
890
|
-
try expectProbability(&chance.probability, 1, d);
|
|
891
|
-
try expectValue(@as(u3, 0), chance.actions.p2.durations.attacking);
|
|
892
|
-
|
|
893
|
-
chance.reset();
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
if (i < 3) {
|
|
897
|
-
chance.actions.p2.durations.attacking = @intCast(i);
|
|
898
|
-
try chance.attacking(.P2, 1);
|
|
899
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
900
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.attacking);
|
|
901
|
-
|
|
902
|
-
chance.reset();
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
test "Chance.binding" {
|
|
908
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
909
|
-
|
|
910
|
-
const ps = [_]u8{ 3, 3, 1, 1 };
|
|
911
|
-
const qs = [_]u8{ 8, 5, 2, 1 };
|
|
912
|
-
|
|
913
|
-
for (ps, qs, 1..5) |p, q, i| {
|
|
914
|
-
chance.actions.p1.durations.binding = @intCast(i);
|
|
915
|
-
try chance.binding(.P1, 0);
|
|
916
|
-
try expectProbability(&chance.probability, p, q);
|
|
917
|
-
try expectValue(@as(u3, 0), chance.actions.p1.durations.binding);
|
|
918
|
-
|
|
919
|
-
chance.reset();
|
|
920
|
-
|
|
921
|
-
if (i < 4) {
|
|
922
|
-
chance.actions.p1.durations.binding = @intCast(i);
|
|
923
|
-
try chance.binding(.P1, 1);
|
|
924
|
-
try expectProbability(&chance.probability, q - p, q);
|
|
925
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p1.durations.binding);
|
|
926
|
-
|
|
927
|
-
chance.reset();
|
|
928
|
-
}
|
|
929
|
-
}
|
|
716
|
+
chance.duration(.P2, 4);
|
|
717
|
+
try expectValue(4, chance.actions.p2.duration);
|
|
930
718
|
}
|
|
931
719
|
|
|
932
720
|
test "Chance.psywave" {
|
|
@@ -934,7 +722,7 @@ test "Chance.psywave" {
|
|
|
934
722
|
|
|
935
723
|
try chance.psywave(.P2, 100, 150);
|
|
936
724
|
try expectProbability(&chance.probability, 1, 150);
|
|
937
|
-
try expectValue(
|
|
725
|
+
try expectValue(101, chance.actions.p2.psywave);
|
|
938
726
|
}
|
|
939
727
|
|
|
940
728
|
test "Chance.metronome" {
|
|
@@ -976,10 +764,6 @@ const Null = struct {
|
|
|
976
764
|
_ = .{self};
|
|
977
765
|
}
|
|
978
766
|
|
|
979
|
-
pub fn clearDurations(self: Null, player: Player, haze: ?bool) void {
|
|
980
|
-
_ = .{ self, player, haze };
|
|
981
|
-
}
|
|
982
|
-
|
|
983
767
|
pub fn switched(self: Null, player: Player, in: u8, out: u8) void {
|
|
984
768
|
_ = .{ self, player, in, out };
|
|
985
769
|
}
|
|
@@ -988,8 +772,8 @@ const Null = struct {
|
|
|
988
772
|
_ = .{ self, p1 };
|
|
989
773
|
}
|
|
990
774
|
|
|
991
|
-
pub fn hit(self: Null, ok: bool, accuracy:
|
|
992
|
-
_ = .{ self, ok, accuracy };
|
|
775
|
+
pub fn hit(self: Null, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
776
|
+
_ = .{ self, player, ok, accuracy };
|
|
993
777
|
}
|
|
994
778
|
|
|
995
779
|
pub fn criticalHit(self: Null, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -1020,33 +804,16 @@ const Null = struct {
|
|
|
1020
804
|
_ = .{ self, player, n };
|
|
1021
805
|
}
|
|
1022
806
|
|
|
1023
|
-
pub fn
|
|
807
|
+
pub fn durations(
|
|
1024
808
|
self: Null,
|
|
1025
|
-
comptime
|
|
809
|
+
comptime f: Action.Field,
|
|
1026
810
|
player: Player,
|
|
1027
|
-
|
|
1028
|
-
turns: u4,
|
|
811
|
+
observation: Optional(Observation),
|
|
1029
812
|
) void {
|
|
1030
|
-
_ = .{ self,
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
pub fn sleep(self: Null, player: Player, turns: u4) Error!void {
|
|
1034
|
-
_ = .{ self, player, turns };
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
pub fn disable(self: Null, player: Player, turns: u4) Error!void {
|
|
1038
|
-
_ = .{ self, player, turns };
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
pub fn confusion(self: Null, player: Player, turns: u4) Error!void {
|
|
1042
|
-
_ = .{ self, player, turns };
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
pub fn attacking(self: Null, player: Player, turns: u4) Error!void {
|
|
1046
|
-
_ = .{ self, player, turns };
|
|
813
|
+
_ = .{ self, f, player, observation };
|
|
1047
814
|
}
|
|
1048
815
|
|
|
1049
|
-
pub fn
|
|
816
|
+
pub fn duration(self: Null, player: Player, turns: u4) void {
|
|
1050
817
|
_ = .{ self, player, turns };
|
|
1051
818
|
}
|
|
1052
819
|
|