@pkmn/engine 0.1.0-dev.02b7f810
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +332 -0
- package/build/pkg/addon.d.ts +7 -0
- package/build/pkg/addon.js +111 -0
- package/build/pkg/addon.js.map +1 -0
- package/build/pkg/data/ids.json +240 -0
- package/build/pkg/data/layout.json +86 -0
- package/build/pkg/data/protocol.json +173 -0
- package/build/pkg/data.d.ts +91 -0
- package/build/pkg/data.js +150 -0
- package/build/pkg/data.js.map +1 -0
- package/build/pkg/gen1.d.ts +109 -0
- package/build/pkg/gen1.js +668 -0
- package/build/pkg/gen1.js.map +1 -0
- package/build/pkg/index.d.ts +383 -0
- package/build/pkg/index.js +122 -0
- package/build/pkg/index.js.map +1 -0
- package/build/pkg/protocol.d.ts +71 -0
- package/build/pkg/protocol.js +452 -0
- package/build/pkg/protocol.js.map +1 -0
- package/build/test/display.js +382 -0
- package/build/test/display.js.map +1 -0
- package/build/tools/debug.js +133 -0
- package/build/tools/debug.js.map +1 -0
- package/build.zig +346 -0
- package/package.json +202 -0
- package/src/bin/install-pkmn-engine +391 -0
- package/src/bin/pkmn-debug +62 -0
- package/src/lib/binding/node.zig +229 -0
- package/src/lib/binding/wasm.zig +10 -0
- package/src/lib/common/data.zig +129 -0
- package/src/lib/common/debug.zig +64 -0
- package/src/lib/common/options.zig +40 -0
- package/src/lib/common/protocol.zig +1297 -0
- package/src/lib/common/rng.zig +234 -0
- package/src/lib/gen1/README.md +791 -0
- package/src/lib/gen1/data/moves.zig +1854 -0
- package/src/lib/gen1/data/species.zig +1105 -0
- package/src/lib/gen1/data/types.zig +131 -0
- package/src/lib/gen1/data.zig +620 -0
- package/src/lib/gen1/helpers.zig +234 -0
- package/src/lib/gen1/mechanics.zig +2819 -0
- package/src/lib/gen2/README.md +309 -0
- package/src/lib/gen2/data/items.zig +236 -0
- package/src/lib/gen2/data/moves.zig +2784 -0
- package/src/lib/gen2/data/species.zig +1798 -0
- package/src/lib/gen2/data/types.zig +123 -0
- package/src/lib/gen2/data.zig +425 -0
- package/src/lib/gen2/helpers.zig +225 -0
- package/src/lib/gen2/mechanics.zig +53 -0
- package/src/lib/pkmn.zig +54 -0
- package/src/test/display/pkmn.html.tmpl +368 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const common = @import("../common/data.zig");
|
|
4
|
+
const options = @import("../common/options.zig");
|
|
5
|
+
const protocol = @import("../common/protocol.zig");
|
|
6
|
+
const rng = @import("../common/rng.zig");
|
|
7
|
+
|
|
8
|
+
const gen1 = @import("../gen1/data.zig");
|
|
9
|
+
|
|
10
|
+
const data = @import("data.zig");
|
|
11
|
+
|
|
12
|
+
const assert = std.debug.assert;
|
|
13
|
+
|
|
14
|
+
const expectEqual = std.testing.expectEqual;
|
|
15
|
+
|
|
16
|
+
const Choice = common.Choice;
|
|
17
|
+
|
|
18
|
+
const showdown = options.showdown;
|
|
19
|
+
|
|
20
|
+
const PSRNG = rng.PSRNG;
|
|
21
|
+
|
|
22
|
+
const DVs = data.DVs;
|
|
23
|
+
const Item = data.Item;
|
|
24
|
+
const Move = data.Move;
|
|
25
|
+
const MoveSlot = data.MoveSlot;
|
|
26
|
+
const Species = data.Species;
|
|
27
|
+
const Stats = data.Stats;
|
|
28
|
+
const Status = data.Status;
|
|
29
|
+
|
|
30
|
+
pub const Options = struct {
|
|
31
|
+
cleric: bool = showdown,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
pub const Battle = struct {
|
|
35
|
+
pub fn init(
|
|
36
|
+
seed: u64,
|
|
37
|
+
p1: []const Pokemon,
|
|
38
|
+
p2: []const Pokemon,
|
|
39
|
+
) data.Battle(data.PRNG) {
|
|
40
|
+
var rand = PSRNG.init(seed);
|
|
41
|
+
return .{
|
|
42
|
+
.rng = prng(&rand),
|
|
43
|
+
.sides = .{ Side.init(p1), Side.init(p2) },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
pub fn fixed(
|
|
48
|
+
comptime rolls: anytype,
|
|
49
|
+
p1: []const Pokemon,
|
|
50
|
+
p2: []const Pokemon,
|
|
51
|
+
) data.Battle(rng.FixedRNG(1, rolls.len)) {
|
|
52
|
+
return .{
|
|
53
|
+
.rng = .{ .rolls = rolls },
|
|
54
|
+
.sides = .{ Side.init(p1), Side.init(p2) },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pub fn random(rand: *PSRNG, opt: Options) data.Battle(data.PRNG) {
|
|
59
|
+
return .{
|
|
60
|
+
.rng = prng(rand),
|
|
61
|
+
.turn = 0,
|
|
62
|
+
.sides = .{ Side.random(rand, opt), Side.random(rand, opt) },
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
pub fn prng(rand: *PSRNG) data.PRNG {
|
|
68
|
+
// FIXME: what is max range in gen2?
|
|
69
|
+
return .{
|
|
70
|
+
.src = .{
|
|
71
|
+
.seed = if (showdown)
|
|
72
|
+
rand.newSeed()
|
|
73
|
+
else
|
|
74
|
+
.{
|
|
75
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
76
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
77
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
78
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
79
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
pub const Side = struct {
|
|
86
|
+
pub fn init(ps: []const Pokemon) data.Side {
|
|
87
|
+
assert(ps.len > 0 and ps.len <= 6);
|
|
88
|
+
var side = data.Side{};
|
|
89
|
+
|
|
90
|
+
for (0..ps.len) |i| {
|
|
91
|
+
side.pokemon[i] = Pokemon.init(ps[i]);
|
|
92
|
+
// FIXME side.order[i] = i + 1;
|
|
93
|
+
}
|
|
94
|
+
return side;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
pub fn random(rand: *PSRNG, opt: Options) data.Side {
|
|
98
|
+
const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 5 + 1) else 6;
|
|
99
|
+
var side = data.Side{};
|
|
100
|
+
|
|
101
|
+
for (0..n) |i| {
|
|
102
|
+
side.pokemon[i] = Pokemon.random(rand, opt);
|
|
103
|
+
side.pokemon[i].position = i + 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return side;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
pub const EXP = 0xFFFF;
|
|
111
|
+
|
|
112
|
+
pub const Pokemon = struct {
|
|
113
|
+
species: Species,
|
|
114
|
+
moves: []const Move,
|
|
115
|
+
item: Item = .None,
|
|
116
|
+
hp: ?u16 = null,
|
|
117
|
+
status: u8 = 0,
|
|
118
|
+
level: u8 = 100,
|
|
119
|
+
dvs: gen1.DVs = .{},
|
|
120
|
+
stats: Stats(u16) = .{ .hp = EXP, .atk = EXP, .def = EXP, .spe = EXP, .spa = EXP, .spd = EXP },
|
|
121
|
+
|
|
122
|
+
pub fn init(p: Pokemon) data.Pokemon {
|
|
123
|
+
var pokemon = data.Pokemon{};
|
|
124
|
+
pokemon.species = p.species;
|
|
125
|
+
const species = Species.get(p.species);
|
|
126
|
+
inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
|
|
127
|
+
const hp = comptime std.mem.eql(u8, field.name, "hp");
|
|
128
|
+
const spc =
|
|
129
|
+
comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
130
|
+
@field(pokemon.stats, field.name) = Stats(u16).calc(
|
|
131
|
+
field.name,
|
|
132
|
+
@field(species.stats, field.name),
|
|
133
|
+
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field.name),
|
|
134
|
+
@field(p.stats, field.name),
|
|
135
|
+
p.level,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
assert(p.moves.len > 0 and p.moves.len <= 4);
|
|
139
|
+
for (p.moves, 0..) |m, j| {
|
|
140
|
+
pokemon.moves[j] = .{
|
|
141
|
+
.id = m,
|
|
142
|
+
.pp_ups = 3,
|
|
143
|
+
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
144
|
+
.pp = @intCast(u8, @min(Move.pp(m) / 5 * 8, 61)),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
pokemon.item = p.item;
|
|
148
|
+
if (p.hp) |hp| {
|
|
149
|
+
pokemon.hp = hp;
|
|
150
|
+
} else {
|
|
151
|
+
pokemon.hp = pokemon.stats.hp;
|
|
152
|
+
}
|
|
153
|
+
pokemon.status = p.status;
|
|
154
|
+
pokemon.types = species.types;
|
|
155
|
+
pokemon.level = p.level;
|
|
156
|
+
pokemon.dvs = DVs.from(p.dvs);
|
|
157
|
+
return pokemon;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
|
|
161
|
+
const s = @intToEnum(Species, rand.range(u8, 1, Species.size + 1));
|
|
162
|
+
const species = Species.get(s);
|
|
163
|
+
const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
|
|
164
|
+
// Pokémon Showdown does not support most items without in-battle held item effects
|
|
165
|
+
const max_item = @enumToInt(if (showdown) Item.FlowerMail else Item.TM50);
|
|
166
|
+
const item = if (rand.chance(u8, 1, 10)) 0 else rand.range(u8, 1, max_item + 1);
|
|
167
|
+
|
|
168
|
+
var stats: Stats(u16) = .{};
|
|
169
|
+
const dvs = gen1.DVs.random(rand);
|
|
170
|
+
inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
|
|
171
|
+
const hp = std.mem.eql(u8, field.name, "hp");
|
|
172
|
+
const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
173
|
+
@field(stats, field.name) = Stats(u16).calc(
|
|
174
|
+
field.name,
|
|
175
|
+
@field(species.stats, field.name),
|
|
176
|
+
if (hp) dvs.hp() else if (spc) dvs.spc else @field(dvs, field.name),
|
|
177
|
+
if (rand.chance(u8, 1, 20)) rand.range(u8, 0, 255 + 1) else 255,
|
|
178
|
+
lvl,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
var ms = [_]MoveSlot{.{}} ** 4;
|
|
183
|
+
const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
|
|
184
|
+
for (0..n) |i| {
|
|
185
|
+
var m: Move = .None;
|
|
186
|
+
sample: while (true) {
|
|
187
|
+
m = @intToEnum(Move, rand.range(u8, 1, 164 + 1));
|
|
188
|
+
for (0..i) |j| if (ms[j].id == m) continue :sample;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
const pp_ups =
|
|
192
|
+
if (!opt.cleric and rand.chance(u8, 1, 10)) rand.range(u2, 0, 2 + 1) else 3;
|
|
193
|
+
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
194
|
+
const max_pp = @intCast(u8, Move.pp(m) + @as(u8, pp_ups) * @min(Move.pp(m) / 5, 7));
|
|
195
|
+
ms[i] = .{
|
|
196
|
+
.id = m,
|
|
197
|
+
.pp_ups = pp_ups,
|
|
198
|
+
.pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return .{
|
|
202
|
+
.species = s,
|
|
203
|
+
.types = species.types,
|
|
204
|
+
.dvs = DVs.from(dvs),
|
|
205
|
+
.level = lvl,
|
|
206
|
+
.stats = stats,
|
|
207
|
+
.hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),
|
|
208
|
+
.status = if (!opt.cleric and rand.chance(u8, 1, 6 + 1))
|
|
209
|
+
0 | (@as(u8, 1) << rand.range(u3, 1, 6 + 1))
|
|
210
|
+
else
|
|
211
|
+
0,
|
|
212
|
+
.happiness = if (rand.chance(u8, 1, 10 + 1)) rand.range(u8, 0, 255) else 255,
|
|
213
|
+
.item = item,
|
|
214
|
+
.moves = ms,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
pub fn move(slot: u4) Choice {
|
|
220
|
+
return .{ .type = .Move, .data = slot };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
pub fn swtch(slot: u4) Choice {
|
|
224
|
+
return .{ .type = .Switch, .data = slot };
|
|
225
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const common = @import("../common/data.zig");
|
|
4
|
+
const DEBUG = @import("../common/debug.zig").print;
|
|
5
|
+
const options = @import("../common/options.zig");
|
|
6
|
+
const protocol = @import("../common/protocol.zig");
|
|
7
|
+
const rng = @import("../common/rng.zig");
|
|
8
|
+
|
|
9
|
+
const data = @import("data.zig");
|
|
10
|
+
|
|
11
|
+
const assert = std.debug.assert;
|
|
12
|
+
|
|
13
|
+
const expectEqual = std.testing.expectEqual;
|
|
14
|
+
|
|
15
|
+
const Choice = common.Choice;
|
|
16
|
+
const ID = common.ID;
|
|
17
|
+
const Player = common.Player;
|
|
18
|
+
const Result = common.Result;
|
|
19
|
+
|
|
20
|
+
const showdown = options.showdown;
|
|
21
|
+
|
|
22
|
+
const Damage = protocol.Damage;
|
|
23
|
+
const Heal = protocol.Heal;
|
|
24
|
+
|
|
25
|
+
const Gen12 = rng.Gen12;
|
|
26
|
+
|
|
27
|
+
const ActivePokemon = data.ActivePokemon;
|
|
28
|
+
const Effectiveness = data.Effectiveness;
|
|
29
|
+
const Move = data.Move;
|
|
30
|
+
const MoveSlot = data.MoveSlot;
|
|
31
|
+
const Pokemon = data.Pokemon;
|
|
32
|
+
const Side = data.Side;
|
|
33
|
+
const Species = data.Species;
|
|
34
|
+
const Stats = data.Stats;
|
|
35
|
+
const Status = data.Status;
|
|
36
|
+
|
|
37
|
+
pub fn update(battle: anytype, c1: Choice, c2: Choice, log: anytype) !Result {
|
|
38
|
+
_ = battle;
|
|
39
|
+
_ = c1;
|
|
40
|
+
_ = c2;
|
|
41
|
+
_ = log;
|
|
42
|
+
|
|
43
|
+
return Result.Default;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
47
|
+
_ = battle;
|
|
48
|
+
_ = player;
|
|
49
|
+
_ = request;
|
|
50
|
+
_ = out;
|
|
51
|
+
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
package/src/lib/pkmn.zig
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/// Configurated Options for the pkmn package.
|
|
2
|
+
pub const options = @import("common/options.zig");
|
|
3
|
+
/// Configures the behavior of the pkmn package.
|
|
4
|
+
pub const Options = options.Options;
|
|
5
|
+
|
|
6
|
+
/// The minimum size in bytes required to hold all choice options.
|
|
7
|
+
pub const MAX_OPTIONS = gen1.MAX_OPTIONS;
|
|
8
|
+
/// The optimal size in bytes required to hold all choice options.
|
|
9
|
+
/// At least as large as `MAX_OPTIONS`.
|
|
10
|
+
pub const OPTIONS_SIZE = gen1.OPTIONS_SIZE;
|
|
11
|
+
/// The maximum number of bytes possibly logged by a single update.
|
|
12
|
+
pub const MAX_LOGS = gen1.MAX_LOGS;
|
|
13
|
+
/// The optimal size in bytes required to hold the largest amount of log data
|
|
14
|
+
/// possible from a single update. At least as large as `MAX_LOGS`.
|
|
15
|
+
pub const LOGS_SIZE = gen1.LOGS_SIZE;
|
|
16
|
+
|
|
17
|
+
/// Representation of one of the battle's participants.
|
|
18
|
+
pub const Player = @import("common/data.zig").Player;
|
|
19
|
+
/// A choice made by a player during battle.
|
|
20
|
+
pub const Choice = @import("common/data.zig").Choice;
|
|
21
|
+
/// The result of the battle - all results other than 'None' should be
|
|
22
|
+
/// considered terminal.
|
|
23
|
+
pub const Result = @import("common/data.zig").Result;
|
|
24
|
+
|
|
25
|
+
/// Pokémon Showdown's RNG (backed by a Generation V & VI RNG).
|
|
26
|
+
pub const PSRNG = @import("common/rng.zig").PSRNG;
|
|
27
|
+
|
|
28
|
+
/// Namespace for helpers related to protocol trace logging.
|
|
29
|
+
pub const protocol = if (options.internal) struct {
|
|
30
|
+
pub usingnamespace @import("common/protocol.zig");
|
|
31
|
+
} else struct {
|
|
32
|
+
/// Logs protocol information to its `Writer` during a battle update when
|
|
33
|
+
/// `options.trace` is enabled.
|
|
34
|
+
pub const Log = @import("common/protocol.zig").Log;
|
|
35
|
+
/// Stripped down version of `std.io.FixedBufferStream` optimized for
|
|
36
|
+
/// efficiently writing the individual protocol bytes. Note that the
|
|
37
|
+
/// `ByteStream.Writer` is **not** a `std.io.Writer` and should not be
|
|
38
|
+
/// used for general purpose writing.
|
|
39
|
+
pub const ByteStream = @import("common/protocol.zig").ByteStream;
|
|
40
|
+
/// `Log` type backed by the optimized `ByteStream.Writer`. Intended to be
|
|
41
|
+
/// intialized with a `LOGS_SIZE`-sized buffer.
|
|
42
|
+
pub const FixedLog = @import("common/protocol.zig").FixedLog;
|
|
43
|
+
/// Null object pattern implementation of `Log` backed by a
|
|
44
|
+
/// `std.io.null_writer`. Ignores anything sent to it, though trace logging
|
|
45
|
+
/// should addtionally be turned off entirely by using `options.trace`.
|
|
46
|
+
pub const NULL = @import("common/protocol.zig").NULL;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/// Namespace for Generation I Pokémon
|
|
50
|
+
pub const gen1 = struct {
|
|
51
|
+
pub usingnamespace @import("gen1/data.zig");
|
|
52
|
+
/// Provides helpers for initializing Generation I Pokémon battles.
|
|
53
|
+
pub const helpers = @import("gen1/helpers.zig");
|
|
54
|
+
};
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang=en>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<link rel="icon" href="https://pkmn.cc/favicon.ico">
|
|
7
|
+
<title>@pkmn/engine</title>
|
|
8
|
+
<style>
|
|
9
|
+
#content {
|
|
10
|
+
font-family: "Roboto", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
|
11
|
+
margin: 4em auto;
|
|
12
|
+
max-width: 1300px;
|
|
13
|
+
line-height: 1.4em;
|
|
14
|
+
--hp-bar: 151px;
|
|
15
|
+
--hp-text: 32px;
|
|
16
|
+
}
|
|
17
|
+
details > summary {
|
|
18
|
+
list-style: none;
|
|
19
|
+
}
|
|
20
|
+
details > summary::-webkit-details-marker {
|
|
21
|
+
display: none;
|
|
22
|
+
}
|
|
23
|
+
.battle > .details {
|
|
24
|
+
background: #eee;
|
|
25
|
+
border-bottom: 3px solid black;
|
|
26
|
+
padding: 0 0.5em;
|
|
27
|
+
display: flex;
|
|
28
|
+
justify-content: space-between;
|
|
29
|
+
}
|
|
30
|
+
.inner {
|
|
31
|
+
display: flex;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
}
|
|
35
|
+
.side > .details {
|
|
36
|
+
padding: 0.5em;
|
|
37
|
+
display: flex;
|
|
38
|
+
justify-content: space-between;
|
|
39
|
+
text-align: center;
|
|
40
|
+
flex: 0 1 auto;
|
|
41
|
+
}
|
|
42
|
+
.active {
|
|
43
|
+
flex: 1 1 auto;
|
|
44
|
+
}
|
|
45
|
+
details {
|
|
46
|
+
flex: 0 1 auto;
|
|
47
|
+
}
|
|
48
|
+
.sides {
|
|
49
|
+
display: flex;
|
|
50
|
+
}
|
|
51
|
+
.side {
|
|
52
|
+
flex: 1;
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-flow: column;
|
|
55
|
+
}
|
|
56
|
+
.side.p1 {
|
|
57
|
+
border-right: 1px solid black;
|
|
58
|
+
}
|
|
59
|
+
.battle {
|
|
60
|
+
border: 3px solid black;
|
|
61
|
+
}
|
|
62
|
+
summary {
|
|
63
|
+
border-top: 1px solid black;
|
|
64
|
+
text-align: center;
|
|
65
|
+
padding: 0.25em;
|
|
66
|
+
}
|
|
67
|
+
.good {
|
|
68
|
+
color: green;
|
|
69
|
+
}
|
|
70
|
+
.bad {
|
|
71
|
+
color: red;
|
|
72
|
+
}
|
|
73
|
+
.pokemon {
|
|
74
|
+
position: relative;
|
|
75
|
+
padding: 0.5em;
|
|
76
|
+
border-top: 1px solid black;
|
|
77
|
+
display: flex;
|
|
78
|
+
}
|
|
79
|
+
table {
|
|
80
|
+
border-collapse: collapse;
|
|
81
|
+
border-style: hidden;
|
|
82
|
+
border-spacing: 0px;
|
|
83
|
+
line-height: 1.5em;
|
|
84
|
+
}
|
|
85
|
+
th, td {
|
|
86
|
+
border: 1px solid black;
|
|
87
|
+
padding: 0 0.5em;
|
|
88
|
+
}
|
|
89
|
+
.statbar {
|
|
90
|
+
display: none;
|
|
91
|
+
width: calc(var(--hp-bar) + var(--hp-text));
|
|
92
|
+
padding: 2px 0;
|
|
93
|
+
line-height: normal;
|
|
94
|
+
}
|
|
95
|
+
.statbar .name {
|
|
96
|
+
display: block;
|
|
97
|
+
text-align: center;
|
|
98
|
+
line-height: 1.25em;
|
|
99
|
+
}
|
|
100
|
+
.statbar strong {
|
|
101
|
+
color: #222222;
|
|
102
|
+
text-shadow:
|
|
103
|
+
#FFFFFF 1px 1px 0, #FFFFFF 1px -1px 0,
|
|
104
|
+
#FFFFFF -1px 1px 0, #FFFFFF -1px -1px 0;
|
|
105
|
+
}
|
|
106
|
+
.statbar strong small {
|
|
107
|
+
font-weight: normal;
|
|
108
|
+
}
|
|
109
|
+
.statbar .hpbar {
|
|
110
|
+
position: relative;
|
|
111
|
+
border: 1px solid #777777;
|
|
112
|
+
background: #FCFEFF;
|
|
113
|
+
padding: 1px;
|
|
114
|
+
height: 8px;
|
|
115
|
+
margin: 0 0 0 0;
|
|
116
|
+
width: calc(var(--hp-bar) + var(--hp-text) + 1px);
|
|
117
|
+
border-radius: 4px;
|
|
118
|
+
}
|
|
119
|
+
.statbar .hpbar .hp {
|
|
120
|
+
height: 4px;
|
|
121
|
+
border-top: 2px solid #00dd60;
|
|
122
|
+
background: #00bb51;
|
|
123
|
+
border-bottom: 2px solid #007734;
|
|
124
|
+
border-right: 1px solid #007734;
|
|
125
|
+
border-radius: 3px;
|
|
126
|
+
}
|
|
127
|
+
.statbar .hpbar .hp-yellow {
|
|
128
|
+
border-top-color: #f8e379;
|
|
129
|
+
background-color: #f5d538;
|
|
130
|
+
border-bottom-color: #be9f0a;
|
|
131
|
+
border-right-color: #be9f0a;
|
|
132
|
+
}
|
|
133
|
+
.statbar .hpbar .hp-red {
|
|
134
|
+
border-top-color: #f37f67;
|
|
135
|
+
background-color: #ee4928;
|
|
136
|
+
border-bottom-color: #a3260d;
|
|
137
|
+
border-right-color: #a3260d;
|
|
138
|
+
}
|
|
139
|
+
.statbar .hpbar .prevhp {
|
|
140
|
+
background: #BBEECC;
|
|
141
|
+
height: 8px;
|
|
142
|
+
border-radius: 3px;
|
|
143
|
+
}
|
|
144
|
+
.statbar .hpbar .prevhp-yellow {
|
|
145
|
+
background-color: #fcf4ca;
|
|
146
|
+
}
|
|
147
|
+
.statbar .hpbar .prevhp-red {
|
|
148
|
+
background-color: #facec5;
|
|
149
|
+
}
|
|
150
|
+
.statbar .hpbar .hptext {
|
|
151
|
+
position: absolute;
|
|
152
|
+
background: #777777;
|
|
153
|
+
color: #EEEEEE;
|
|
154
|
+
text-shadow: #000000 0 1px 0;
|
|
155
|
+
font-size: 10px;
|
|
156
|
+
width: 32px;
|
|
157
|
+
height: 12px;
|
|
158
|
+
top: -1px;
|
|
159
|
+
text-align: center;
|
|
160
|
+
}
|
|
161
|
+
.status, .volatile {
|
|
162
|
+
padding: 0px 1px;
|
|
163
|
+
border: 1px solid #FF4400;
|
|
164
|
+
border-radius: 3px;
|
|
165
|
+
}
|
|
166
|
+
.status {
|
|
167
|
+
min-height: 10px;
|
|
168
|
+
font-size: 7pt;
|
|
169
|
+
vertical-align: middle;
|
|
170
|
+
padding: 1px 2px;
|
|
171
|
+
border: 0;
|
|
172
|
+
border-radius: 3px;
|
|
173
|
+
text-transform: uppercase;
|
|
174
|
+
margin: 0 2px;
|
|
175
|
+
}
|
|
176
|
+
.status.brn {
|
|
177
|
+
background: #EE5533;
|
|
178
|
+
color: #FFFFFF;
|
|
179
|
+
}
|
|
180
|
+
.status.psn {
|
|
181
|
+
background: #A4009A;
|
|
182
|
+
color: #FFFFFF;
|
|
183
|
+
}
|
|
184
|
+
.status.par {
|
|
185
|
+
background: #9AA400;
|
|
186
|
+
color: #FFFFFF;
|
|
187
|
+
}
|
|
188
|
+
.status.slp {
|
|
189
|
+
background: #AA77AA;
|
|
190
|
+
color: #FFFFFF;
|
|
191
|
+
}
|
|
192
|
+
.status.frz {
|
|
193
|
+
background: #009AA4;
|
|
194
|
+
color: #FFFFFF;
|
|
195
|
+
}
|
|
196
|
+
.volatile {
|
|
197
|
+
display: inline-block;
|
|
198
|
+
padding: 0 5px;
|
|
199
|
+
margin: 2px
|
|
200
|
+
}
|
|
201
|
+
.volatile.bad {
|
|
202
|
+
background: #FFE5E0;
|
|
203
|
+
color: #FF4400;
|
|
204
|
+
border-color: #FF4400;
|
|
205
|
+
}
|
|
206
|
+
.volatile.good {
|
|
207
|
+
background: #E5FFE0;
|
|
208
|
+
color: #33AA00;
|
|
209
|
+
border-color: #33AA00;
|
|
210
|
+
}
|
|
211
|
+
.volatile.neutral {
|
|
212
|
+
background: #F0F0F0;
|
|
213
|
+
color: #555555;
|
|
214
|
+
border-color: #555555;
|
|
215
|
+
}
|
|
216
|
+
.rstatbar .hpbar .hptext {
|
|
217
|
+
right: 0px;
|
|
218
|
+
border-radius: 0 4px 4px 0;
|
|
219
|
+
}
|
|
220
|
+
.moves ul {
|
|
221
|
+
column-count: 2;
|
|
222
|
+
column-gap: 2em;
|
|
223
|
+
}
|
|
224
|
+
.left {
|
|
225
|
+
padding-right: 32px;
|
|
226
|
+
display: flex;
|
|
227
|
+
flex-direction: column;
|
|
228
|
+
align-items: center;
|
|
229
|
+
}
|
|
230
|
+
.stats {
|
|
231
|
+
display: flex;
|
|
232
|
+
padding: 0 1em;
|
|
233
|
+
justify-content: space-between;
|
|
234
|
+
text-align: center;
|
|
235
|
+
width: 100%;
|
|
236
|
+
}
|
|
237
|
+
.types {
|
|
238
|
+
display: inherit;
|
|
239
|
+
}
|
|
240
|
+
.disabled {
|
|
241
|
+
color: #AAAAAA;
|
|
242
|
+
}
|
|
243
|
+
.log {
|
|
244
|
+
display: flex;
|
|
245
|
+
justify-content: center;
|
|
246
|
+
padding-bottom: 2em;
|
|
247
|
+
}
|
|
248
|
+
.position {
|
|
249
|
+
position: absolute;
|
|
250
|
+
right: 10px;
|
|
251
|
+
color: #AAAAAA;
|
|
252
|
+
}
|
|
253
|
+
.left .position {
|
|
254
|
+
display: none;
|
|
255
|
+
}
|
|
256
|
+
.right .position {
|
|
257
|
+
display: block;
|
|
258
|
+
}
|
|
259
|
+
.teamicons {
|
|
260
|
+
display: inline-block;
|
|
261
|
+
}
|
|
262
|
+
@media(max-width: 1300px) {
|
|
263
|
+
#content {
|
|
264
|
+
font-size: 0.85em;
|
|
265
|
+
max-width: 95%;
|
|
266
|
+
margin: 2em auto;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
@media(max-width: 1200px) {
|
|
270
|
+
.moves ul {
|
|
271
|
+
column-count: 1;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
@media(max-width: 1100px) {
|
|
275
|
+
.pokemon {
|
|
276
|
+
flex-direction: column;
|
|
277
|
+
}
|
|
278
|
+
.stats {
|
|
279
|
+
justify-content: center;
|
|
280
|
+
padding: 0;
|
|
281
|
+
}
|
|
282
|
+
.boosts {
|
|
283
|
+
margin-left: 5px;
|
|
284
|
+
}
|
|
285
|
+
.moves ul {
|
|
286
|
+
column-count: 2;
|
|
287
|
+
}
|
|
288
|
+
.left {
|
|
289
|
+
padding-right: 0;
|
|
290
|
+
}
|
|
291
|
+
.right {
|
|
292
|
+
padding-top: 1em;
|
|
293
|
+
}
|
|
294
|
+
.volatiles {
|
|
295
|
+
display: flex;
|
|
296
|
+
justify-content: center;
|
|
297
|
+
flex-wrap: wrap;
|
|
298
|
+
}
|
|
299
|
+
.left .position {
|
|
300
|
+
display: block;
|
|
301
|
+
}
|
|
302
|
+
.right .position {
|
|
303
|
+
display: none;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
@media(max-width: 800px) {
|
|
307
|
+
.moves {
|
|
308
|
+
display: flex;
|
|
309
|
+
justify-content: center;
|
|
310
|
+
}
|
|
311
|
+
.moves ul {
|
|
312
|
+
column-count: 1;
|
|
313
|
+
display: inline-block;
|
|
314
|
+
padding: 0;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
@media(max-width: 680px) {
|
|
318
|
+
.stats {
|
|
319
|
+
flex-direction: column;
|
|
320
|
+
padding: 0;
|
|
321
|
+
}
|
|
322
|
+
.boosts {
|
|
323
|
+
display: flex;
|
|
324
|
+
justify-content: space-between;
|
|
325
|
+
margin-top: 0.5em;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
@media(max-width: 550px) {
|
|
329
|
+
#content {
|
|
330
|
+
font-size: 0.65em;
|
|
331
|
+
max-width: 95%;
|
|
332
|
+
margin: 1em auto;
|
|
333
|
+
--hp-bar: 101px;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
@media(max-width: 425px) {
|
|
337
|
+
.stats {
|
|
338
|
+
font-size: 0.9em;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
h1 {
|
|
342
|
+
text-align: center;
|
|
343
|
+
}
|
|
344
|
+
.error {
|
|
345
|
+
overflow: auto;
|
|
346
|
+
scrollbar-width: none;
|
|
347
|
+
padding: 10px 20px;
|
|
348
|
+
border: 3px solid #DD4422;
|
|
349
|
+
background: #FFF8F2;
|
|
350
|
+
}
|
|
351
|
+
.error::-webkit-scrollbar {
|
|
352
|
+
display: none;
|
|
353
|
+
}
|
|
354
|
+
</style>
|
|
355
|
+
</head>
|
|
356
|
+
<body>
|
|
357
|
+
<div id="content">{{{ content }}}</div>
|
|
358
|
+
<script>
|
|
359
|
+
for (const details of document.getElementsByTagName('details')) {
|
|
360
|
+
details.addEventListener('toggle', e => {
|
|
361
|
+
for (const d of details.parentElement.parentElement.getElementsByTagName('details')) {
|
|
362
|
+
if (d.open !== details.open) d.open = details.open;
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
</script>
|
|
367
|
+
</body>
|
|
368
|
+
</html>
|