@origingame/create-game 0.2.0 → 0.4.0
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 +2 -2
- package/archetypes/README.md +2 -1
- package/archetypes/arena/.origin/setup +14 -0
- package/archetypes/arena/LICENSES.md +8 -0
- package/archetypes/arena/README.md +67 -0
- package/archetypes/arena/assets/audio/coin.ogt +9 -0
- package/archetypes/arena/assets/audio/dash.ogt +9 -0
- package/archetypes/arena/assets/audio/win.ogt +9 -0
- package/archetypes/arena/assets/fx/coin.ogt +20 -0
- package/archetypes/arena/assets/looks/arena.ogt +10 -0
- package/archetypes/arena/gitattributes +22 -0
- package/archetypes/arena/gitignore +8 -0
- package/archetypes/arena/host.d.ts +46 -0
- package/archetypes/arena/package.json +18 -0
- package/archetypes/arena/project.ogt +14 -0
- package/archetypes/arena/src/game.ts +314 -0
- package/archetypes/arena/src/main.ts +100 -0
- package/archetypes/arena/tests/duel.og.ts +32 -0
- package/archetypes/arena/tests/solo.og.ts +22 -0
- package/archetypes/arena/tsconfig.json +17 -0
- package/archetypes/idle/LICENSES.md +1 -1
- package/archetypes/idle/README.md +5 -5
- package/archetypes/idle/assets/looks/idle.ogt +2 -2
- package/archetypes/idle/assets/{skins → styles}/sparkorb/LICENSES.md +6 -6
- package/archetypes/idle/assets/{skins/sparkorb/skin.ogt → styles/sparkorb/style.ogt} +47 -47
- package/archetypes/idle/package.json +1 -1
- package/archetypes/idle/project.ogt +1 -1
- package/archetypes/idle/references/metal/card.png +0 -0
- package/archetypes/idle/references/metal/orbit.png +0 -0
- package/archetypes/idle/references/metal/title.png +0 -0
- package/archetypes/idle/src/components.ts +1 -1
- package/archetypes/idle/src/main.ts +5 -5
- package/archetypes/idle/src/ui.ts +13 -13
- package/archetypes/idle/tools/make-assets.py +5 -5
- package/archetypes/match3/package.json +1 -1
- package/archetypes/match3/project.ogt +1 -1
- package/archetypes/rhythm/LICENSES.md +1 -1
- package/archetypes/rhythm/README.md +5 -5
- package/archetypes/rhythm/assets/{skins → styles}/lanebeat/LICENSES.md +6 -6
- package/archetypes/rhythm/assets/{skins/lanebeat/skin.ogt → styles/lanebeat/style.ogt} +45 -45
- package/archetypes/rhythm/package.json +1 -1
- package/archetypes/rhythm/project.ogt +1 -1
- package/archetypes/rhythm/references/metal/results.png +0 -0
- package/archetypes/rhythm/references/metal/title.png +0 -0
- package/archetypes/rhythm/src/components.ts +1 -1
- package/archetypes/rhythm/src/main.ts +3 -3
- package/archetypes/rhythm/src/ui.ts +12 -12
- package/archetypes/roguelike/LICENSES.md +1 -1
- package/archetypes/roguelike/README.md +5 -5
- package/archetypes/roguelike/assets/{skins → styles}/lampdive/LICENSES.md +6 -6
- package/archetypes/roguelike/assets/{skins/lampdive/skin.ogt → styles/lampdive/style.ogt} +41 -41
- package/archetypes/roguelike/package.json +1 -1
- package/archetypes/roguelike/project.ogt +1 -1
- package/archetypes/roguelike/references/metal/over.png +0 -0
- package/archetypes/roguelike/references/metal/title.png +0 -0
- package/archetypes/roguelike/src/components.ts +1 -1
- package/archetypes/roguelike/src/main.ts +2 -2
- package/archetypes/roguelike/src/ui.ts +8 -8
- package/archetypes/runner/package.json +1 -1
- package/archetypes/runner/project.ogt +1 -1
- package/archetypes/third-person/LICENSES.md +1 -1
- package/archetypes/third-person/README.md +4 -4
- package/archetypes/third-person/assets/{skins → styles}/ember/LICENSES.md +4 -4
- package/archetypes/third-person/assets/{skins/ember/skin.ogt → styles/ember/style.ogt} +40 -41
- package/archetypes/third-person/package.json +1 -1
- package/archetypes/third-person/project.ogt +1 -1
- package/archetypes/third-person/references/metal/beacon.png +0 -0
- package/archetypes/third-person/references/metal/dawn.png +0 -0
- package/archetypes/third-person/references/metal/title.png +0 -0
- package/archetypes/third-person/references/webgl2/beacon.png +0 -0
- package/archetypes/third-person/src/components.ts +1 -1
- package/archetypes/third-person/src/main.ts +3 -3
- package/archetypes/third-person/src/sound.ts +1 -1
- package/archetypes/third-person/src/ui.ts +11 -11
- package/archetypes/topdown/package.json +1 -1
- package/archetypes/topdown/project.ogt +1 -1
- package/archetypes/tower-defense/LICENSES.md +1 -1
- package/archetypes/tower-defense/README.md +5 -5
- package/archetypes/tower-defense/assets/{skins → styles}/pathguard/LICENSES.md +4 -4
- package/archetypes/tower-defense/assets/styles/pathguard/atlas.png +0 -0
- package/archetypes/tower-defense/assets/styles/pathguard/style.ogt +111 -0
- package/archetypes/tower-defense/package.json +1 -1
- package/archetypes/tower-defense/project.ogt +1 -1
- package/archetypes/tower-defense/references/metal/end.png +0 -0
- package/archetypes/tower-defense/references/metal/title.png +0 -0
- package/archetypes/tower-defense/references/metal/wave.png +0 -0
- package/archetypes/tower-defense/src/components.ts +1 -1
- package/archetypes/tower-defense/src/main.ts +2 -2
- package/archetypes/tower-defense/src/ui.ts +20 -18
- package/bin/create.ts +11 -8
- package/looks/catalogue.json +40 -40
- package/package.json +2 -2
- package/template/README.md +2 -1
- package/template/assets/audio/sting.ogt +10 -0
- package/template/assets/timelines/intro.ogt +101 -0
- package/template/assets/timelines/narration.srt +11 -0
- package/template/host.d.ts +46 -0
- package/template/package.json +1 -1
- package/template/project.ogt +1 -1
- package/template/references/metal/intro-4.png +0 -0
- package/template/references/metal/intro-8.png +0 -0
- package/template/src/main.ts +1 -1
- package/template/tests/frames.ts +134 -0
- package/template/tests/intro.og.ts +65 -0
- package/template/tests/smoke.og.ts +30 -36
- package/template/tsconfig.json +1 -1
- package/template-2d/LICENSES.md +1 -0
- package/template-2d/README.md +6 -2
- package/template-2d/assets/rigs/keeper.ogt +292 -0
- package/template-2d/assets/rigs/keeper.png +0 -0
- package/template-2d/assets/rigs/keeper.png.ogt +7 -0
- package/template-2d/package.json +1 -1
- package/template-2d/project.ogt +1 -1
- package/template-2d/src/main.ts +240 -4
- package/template-2d/tests/keeper.og.ts +178 -0
- package/archetypes/tower-defense/assets/skins/pathguard/atlas.png +0 -0
- package/archetypes/tower-defense/assets/skins/pathguard/skin.ogt +0 -111
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/ChakraPetch-BoldItalic.ttf +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/ChakraPetch-BoldItalic.ttf.ogt +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/OFL.txt +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/Orbitron-Medium.ttf +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/Orbitron-Medium.ttf.ogt +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/Rajdhani-Bold.ttf +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/Rajdhani-Bold.ttf.ogt +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/atlas.png +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/atlas.png.ogt +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/icons.png +0 -0
- /package/archetypes/idle/assets/{skins → styles}/sparkorb/icons.png.ogt +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/ChakraPetch-Medium.ttf +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/ChakraPetch-Medium.ttf.ogt +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/JetBrainsMono-Regular.ttf +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/JetBrainsMono-Regular.ttf.ogt +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/OFL.txt +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/Orbitron-Bold.ttf +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/Orbitron-Bold.ttf.ogt +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/atlas.png +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/atlas.png.ogt +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/icons.png +0 -0
- /package/archetypes/rhythm/assets/{skins → styles}/lanebeat/icons.png.ogt +0 -0
- /package/archetypes/roguelike/assets/{skins → styles}/lampdive/DotGothic16-Regular.ttf +0 -0
- /package/archetypes/roguelike/assets/{skins → styles}/lampdive/DotGothic16-Regular.ttf.ogt +0 -0
- /package/archetypes/roguelike/assets/{skins → styles}/lampdive/OFL.txt +0 -0
- /package/archetypes/roguelike/assets/{skins → styles}/lampdive/atlas.png +0 -0
- /package/archetypes/roguelike/assets/{skins → styles}/lampdive/atlas.png.ogt +0 -0
- /package/archetypes/roguelike/assets/{skins → styles}/lampdive/icons.png +0 -0
- /package/archetypes/roguelike/assets/{skins → styles}/lampdive/icons.png.ogt +0 -0
- /package/archetypes/third-person/assets/{skins → styles}/ember/Cinzel-Bold.ttf +0 -0
- /package/archetypes/third-person/assets/{skins → styles}/ember/Cinzel-Bold.ttf.ogt +0 -0
- /package/archetypes/third-person/assets/{skins → styles}/ember/Newsreader-Medium.ttf +0 -0
- /package/archetypes/third-person/assets/{skins → styles}/ember/Newsreader-Medium.ttf.ogt +0 -0
- /package/archetypes/third-person/assets/{skins → styles}/ember/OFL.txt +0 -0
- /package/archetypes/third-person/assets/{skins → styles}/ember/atlas.png +0 -0
- /package/archetypes/third-person/assets/{skins → styles}/ember/atlas.png.ogt +0 -0
- /package/archetypes/third-person/assets/{skins → styles}/ember/icons.png +0 -0
- /package/archetypes/third-person/assets/{skins → styles}/ember/icons.png.ogt +0 -0
- /package/archetypes/tower-defense/assets/{skins → styles}/pathguard/Fredoka-SemiBold.ttf +0 -0
- /package/archetypes/tower-defense/assets/{skins → styles}/pathguard/Fredoka-SemiBold.ttf.ogt +0 -0
- /package/archetypes/tower-defense/assets/{skins → styles}/pathguard/LilitaOne-Regular.ttf +0 -0
- /package/archetypes/tower-defense/assets/{skins → styles}/pathguard/LilitaOne-Regular.ttf.ogt +0 -0
- /package/archetypes/tower-defense/assets/{skins → styles}/pathguard/OFL.txt +0 -0
- /package/archetypes/tower-defense/assets/{skins → styles}/pathguard/atlas.png.ogt +0 -0
- /package/archetypes/tower-defense/assets/{skins → styles}/pathguard/icons.png +0 -0
- /package/archetypes/tower-defense/assets/{skins → styles}/pathguard/icons.png.ogt +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Coin Duel, the arena archetype (2D profile): two players on one floor run
|
|
2
|
+
// for one coin; each touch is a point and the coin jumps somewhere else,
|
|
3
|
+
// first to five wins the round. It plays alone (the local keys drive Red)
|
|
4
|
+
// and over a room in lockstep: `og relay` and two `og run . --room
|
|
5
|
+
// ws://127.0.0.1:47900/duel` processes (or two Play pages in one platform
|
|
6
|
+
// room) each run the whole world and exchange only their inputs and the
|
|
7
|
+
// world's hash (og.net, docs/design.md §2.10).
|
|
8
|
+
//
|
|
9
|
+
// start turns og.Lockstep on for two players and spawns the two fighters,
|
|
10
|
+
// each an og.PlayerInput whose `player` is its seat, and the coin. The
|
|
11
|
+
// systems are in src/game.ts: Submit hands this peer's keys to
|
|
12
|
+
// og.net.submit, Move and CoinDrop read og.PlayerInput and og.Rng only.
|
|
13
|
+
|
|
14
|
+
import { og } from "@origingame/engine";
|
|
15
|
+
import type { Entity, World } from "@origingame/engine";
|
|
16
|
+
import { Coin, COIN, CoinDrop, Duel, facts, Feedback, Fighter, FIGHTER, HEIGHT, Hud, Move, must, setBot, Sound, Sparks, Submit, Tuning, Voice, Voices, WIDTH } from "./game.ts";
|
|
17
|
+
|
|
18
|
+
/** The brand ink #14161b, linear, behind the floor. */
|
|
19
|
+
const INK = { r: 0.00699, g: 0.00802, b: 0.01096 };
|
|
20
|
+
const FLOOR = { r: 0.022, g: 0.026, b: 0.034 };
|
|
21
|
+
const LINE = { r: 0.06, g: 0.068, b: 0.085 };
|
|
22
|
+
/** Red and Blue, linear. */
|
|
23
|
+
const INKS = [
|
|
24
|
+
{ r: 0.9, g: 0.12, b: 0.1 },
|
|
25
|
+
{ r: 0.1, g: 0.38, b: 0.95 },
|
|
26
|
+
];
|
|
27
|
+
const GOLD = { r: 1, g: 0.62, b: 0.08 };
|
|
28
|
+
|
|
29
|
+
function rect(world: World, name: string, x: number, y: number, w: number, h: number, color: { r: number; g: number; b: number }, layer: number, glow = 0): Entity {
|
|
30
|
+
const e = must(world.spawn({ name, position: { x, y, z: 0 } }));
|
|
31
|
+
must(world.insert(e, og.Sprite, { width: w, height: h, color, layer, glow }));
|
|
32
|
+
return e;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function start(world: World): void {
|
|
36
|
+
// Lockstep for two: in a room the world waits for both seats' inputs each tick.
|
|
37
|
+
const lock = must(world.resource(og.Lockstep));
|
|
38
|
+
lock.enabled = true;
|
|
39
|
+
lock.players = 2;
|
|
40
|
+
lock.inputDelay = 2;
|
|
41
|
+
lock.hashEvery = 10;
|
|
42
|
+
|
|
43
|
+
const camera = must(world.resource(og.Camera));
|
|
44
|
+
camera.orthographic = true;
|
|
45
|
+
camera.height = HEIGHT + 2;
|
|
46
|
+
camera.position = { x: WIDTH / 2, y: HEIGHT / 2, z: 50 };
|
|
47
|
+
camera.background = INK;
|
|
48
|
+
|
|
49
|
+
rect(world, "floor", WIDTH / 2, HEIGHT / 2, WIDTH, HEIGHT, FLOOR, -10);
|
|
50
|
+
rect(world, "midline", WIDTH / 2, HEIGHT / 2, 0.06, HEIGHT, LINE, -9);
|
|
51
|
+
|
|
52
|
+
for (const player of [0, 1]) {
|
|
53
|
+
const e = rect(world, `fighter-${player}`, player === 0 ? 3 : WIDTH - 3, HEIGHT / 2, FIGHTER * 2, FIGHTER * 2, INKS[player]!, 1);
|
|
54
|
+
must(world.insert(e, og.PlayerInput, { player }));
|
|
55
|
+
must(world.insert(e, Fighter, { faceX: player === 0 ? 1 : -1 }));
|
|
56
|
+
}
|
|
57
|
+
const coin = rect(world, "coin", WIDTH / 2, HEIGHT / 2, COIN * 2, COIN * 2, GOLD, 0, 0.6);
|
|
58
|
+
must(world.insert(coin, Coin));
|
|
59
|
+
|
|
60
|
+
// The feedback (src/game.ts Feedback, Voices): one voice per sound, the coin's sparks.
|
|
61
|
+
for (const [path, bit, volume] of [
|
|
62
|
+
["audio/coin.ogt", Sound.Coin, 0.7],
|
|
63
|
+
["audio/dash.ogt", Sound.Dash, 0.45],
|
|
64
|
+
["audio/win.ogt", Sound.Win, 0.8],
|
|
65
|
+
] as const) {
|
|
66
|
+
const v = must(world.spawn({ name: `sfx-${bit}` }));
|
|
67
|
+
must(world.insert(v, og.AudioSource, { sound: must(og.load.sound(world, path)), playing: false, bus: "sfx", volume, priority: 200 }));
|
|
68
|
+
must(world.insert(v, Voice, { bit }));
|
|
69
|
+
}
|
|
70
|
+
const sparks = must(world.spawn({ name: "sparks", position: { x: WIDTH / 2, y: HEIGHT / 2, z: 2 } }));
|
|
71
|
+
must(world.insert(sparks, og.ParticleSystem, { emitter: must(og.load.emitter(world, "fx/coin.ogt")), seed: 7 }));
|
|
72
|
+
must(world.insert(sparks, Sparks));
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default og.game({
|
|
77
|
+
tick: 60,
|
|
78
|
+
seed: 1,
|
|
79
|
+
look: "looks/arena.ogt",
|
|
80
|
+
resources: [Duel, Tuning],
|
|
81
|
+
systems: [Submit, Move, CoinDrop, Feedback, Voices, Hud],
|
|
82
|
+
start,
|
|
83
|
+
commands: {
|
|
84
|
+
/** `bot:<n>`: this peer's input comes from scripted bot n (0 hands it back to the keys). */
|
|
85
|
+
bot: (_world, args) => setBot(Number(args[0])),
|
|
86
|
+
/** `warp:<player>:<x>:<y>`: put a fighter there. */
|
|
87
|
+
warp: (world, args) => {
|
|
88
|
+
const e = world.find(`fighter-${Number(args[0])}`);
|
|
89
|
+
const x = Number(args[1]);
|
|
90
|
+
const y = Number(args[2]);
|
|
91
|
+
if (e === null || !Number.isFinite(x) || !Number.isFinite(y)) throw new Error("write warp:<player>:<x>:<y>");
|
|
92
|
+
must(world.set(e, og.Transform, { position: { x, y, z: 0 } }));
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
commandHelp: {
|
|
96
|
+
bot: { usage: "<n>", doc: "Drive this peer's player by scripted bot n (1, 2, ...); 0 gives it back to the keys." },
|
|
97
|
+
warp: { usage: "<player>:<x>:<y>", doc: "Put fighter 0 (Red) or 1 (Blue) at a point of the floor." },
|
|
98
|
+
},
|
|
99
|
+
facts,
|
|
100
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Two processes play one duel in lockstep through a relay (docs/design.md
|
|
2
|
+
// §2.10): each drives its own seat by a different scripted bot, and both
|
|
3
|
+
// quit on tick 600 (the steps run on the room's shared ticks), where both
|
|
4
|
+
// worlds hash the same, no og.Desync was sent, and the coin moved.
|
|
5
|
+
import { playtest } from "@origingame/engine/playtest";
|
|
6
|
+
|
|
7
|
+
interface Duel {
|
|
8
|
+
value: { score0: number; score1: number; coins: number };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default playtest({
|
|
12
|
+
name: "two processes in one room play the same duel to tick 600",
|
|
13
|
+
commands: "0:bot:1;9.5:get:Duel:duel.json;10:quit",
|
|
14
|
+
room: { players: 2, commands: ["0:bot:1;9.5:get:Duel:duel.json;10:quit", "0:bot:2;9.5:get:Duel:duel.json;10:quit"], name: "duel" },
|
|
15
|
+
check(run, t) {
|
|
16
|
+
t.equal(run.peers.length, 1, "one peer beside the run");
|
|
17
|
+
const peer = run.peers[0]!;
|
|
18
|
+
t.equal(run.tick, 600, "the run quit on tick 600");
|
|
19
|
+
t.equal(peer.tick, 600, "the peer quit on tick 600");
|
|
20
|
+
t.ok(run.lockstep?.running === true && peer.lockstep?.running === true, "lockstep ran on both");
|
|
21
|
+
t.equal([run.lockstep?.peer, peer.lockstep?.peer].sort(), [0, 1], "the peers are 0 and 1");
|
|
22
|
+
t.equal(run.lockstep?.desync ?? null, null, "no desync on the run");
|
|
23
|
+
t.equal(peer.lockstep?.desync ?? null, null, "no desync on the peer");
|
|
24
|
+
t.ok(run.netHash.length > 0, "the run has a shared hash");
|
|
25
|
+
t.equal(peer.netHash, run.netHash, `both worlds hash ${run.netHash} at tick 600`);
|
|
26
|
+
const a = run.json<Duel>("duel.json").value;
|
|
27
|
+
const b = peer.json<Duel>("duel.json").value;
|
|
28
|
+
t.ok(a.coins > 0, `the fighters took coins (${a.coins})`);
|
|
29
|
+
// The reads ran on one tick of the room's clock: the same duel, field for field.
|
|
30
|
+
t.equal(b, a, `the peer read the same duel at 9.5 s`);
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Coin Duel alone (no room): the local keys drive Red. D walks it right
|
|
2
|
+
// through the coin at the centre (Red 1), Space dashes, and the HUD reads
|
|
3
|
+
// both scores. The frame at 2 s is what
|
|
4
|
+
// `og polish` reads for contrast, text size and the HUD's share.
|
|
5
|
+
import { playtest } from "@origingame/engine/playtest";
|
|
6
|
+
|
|
7
|
+
export default playtest({
|
|
8
|
+
name: "alone, the keys walk Red through the coin and dash it, and the HUD shows the scores",
|
|
9
|
+
commands: [
|
|
10
|
+
"0.2:key_down:KeyD",
|
|
11
|
+
"1.2:expect:entity:fighter-0:within:7,0,-1,16,9,1",
|
|
12
|
+
"1.2:key_up:KeyD",
|
|
13
|
+
"1.3:key_down:Space",
|
|
14
|
+
"1.4:key_up:Space",
|
|
15
|
+
"1.5:expect:entity:fighter-1:within:12.5,4,-1,13.5,5,1",
|
|
16
|
+
"2:screenshot:solo.png",
|
|
17
|
+
"2:expect:ui:text:Red 1",
|
|
18
|
+
"2:expect:ui:text:Blue 0",
|
|
19
|
+
"2:expect:ui:text:Local: Red",
|
|
20
|
+
"2.2:quit",
|
|
21
|
+
].join(";"),
|
|
22
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"types": [],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"exactOptionalPropertyTypes": true,
|
|
10
|
+
"noFallthroughCasesInSwitch": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"noEmit": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*.ts", "tests/**/*.ts", "host.d.ts"]
|
|
17
|
+
}
|
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
| `assets/audio/*.ogt` | CC0 1.0 | `og sfx` presets (click, select, back, error, levelup) that `og cook` synthesises, written by the same script |
|
|
9
9
|
| `assets/strings/*.ogt` | CC0 1.0 | self-made: the English and Chinese string tables |
|
|
10
10
|
| `assets/fonts/NotoSansSC-subset.ttf` | SIL Open Font License 1.1 (`assets/fonts/OFL.txt`) | Noto Sans SC Regular, subset to Latin-1 and the characters of `assets/strings/zh-CN.ogt` by the same script from the project template's `assets/fonts/NotoSansSC-Regular-subset.ttf` |
|
|
11
|
-
| `assets/
|
|
11
|
+
| `assets/styles/sparkorb/*` | CC0 1.0 art, SIL OFL 1.1 fonts | the engine's neon style pack (`templates/styles/neon`, `assets/styles/sparkorb/LICENSES.md`) with this game's palette; `atlas.png` is the pack's atlas with its magenta frames turned violet by the same script |
|
|
12
12
|
| `references/*/*.png` | CC0 1.0 | frames of this game recorded by `tests/frames.og.ts` |
|
|
@@ -62,7 +62,7 @@ og build . --publish # dist/: the play page as it ships
|
|
|
62
62
|
fanfare; the supernova bursts 200 sparks, holds the world 10 ticks,
|
|
63
63
|
shakes and punches it.
|
|
64
64
|
- **og.ui counters on the tokens** (og-ui §2–§6, og-taste §1–§7): the
|
|
65
|
-
engine's neon pack recoloured as `assets/
|
|
65
|
+
engine's neon pack recoloured as `assets/styles/sparkorb` (the orb's gold
|
|
66
66
|
as the one accent on night violet: a burnished amber under the buttons'
|
|
67
67
|
cream labels, the bright gold `accentInk` for gold words and icons); the wallet and the rate top left, the
|
|
68
68
|
stardust (an `og.ui.counter`) and Pause top right, the four generators
|
|
@@ -168,10 +168,10 @@ pure functions, the stars come from an og.Rng stream forked by name, and
|
|
|
168
168
|
an `owned<n>` field.
|
|
169
169
|
3. **Prestige**: `novaGain`, `dustMultiplier`.
|
|
170
170
|
4. **The look**: `assets/looks/idle.ogt` (named by `GameDesc.look`) holds
|
|
171
|
-
the frame's treatment and names the
|
|
171
|
+
the frame's treatment and names the style; `extends "<slug>"` there
|
|
172
172
|
starts from a style of the catalogue (og-taste §12), and the reference
|
|
173
173
|
frames are then recorded again (`OG_FX_CAPTURE=1 og test .`). The
|
|
174
|
-
|
|
174
|
+
style's palette is `assets/styles/sparkorb/style.ogt`, the art
|
|
175
175
|
`tools/make-assets.py`: `python3 tools/make-assets.py && og cook .`.
|
|
176
176
|
5. **Words and numbers**: `assets/strings/*.ogt` (a locale's `num.step`
|
|
177
177
|
and units make its big numbers); a new Chinese string needs the script
|
|
@@ -221,8 +221,8 @@ in the engine repository).
|
|
|
221
221
|
`tools/make-assets.py` regenerates every asset byte for byte (two runs
|
|
222
222
|
give the same bytes): the orb, the glow, the spark and the motes, the
|
|
223
223
|
emitters, the sounds and the music, the `og sfx` preset files of the UI's
|
|
224
|
-
sounds, the font subset and the
|
|
225
|
-
lists them; the
|
|
224
|
+
sounds, the font subset and the style's recoloured atlas. `LICENSES.md`
|
|
225
|
+
lists them; the style's are in `assets/styles/sparkorb/LICENSES.md`.
|
|
226
226
|
|
|
227
227
|
## Known limits (engine gaps, not design)
|
|
228
228
|
|
|
@@ -7,8 +7,8 @@ version 1
|
|
|
7
7
|
# suit a glowing orb) adds that style's treatment under the lines below,
|
|
8
8
|
# and the reference frames are then recorded again (OG_FX_CAPTURE=1 og test .).
|
|
9
9
|
name "Spark Orb"
|
|
10
|
-
# The UI wears the sparkorb
|
|
11
|
-
|
|
10
|
+
# The UI wears the sparkorb style (start keeps it and adds the sounds).
|
|
11
|
+
ui "styles/sparkorb/style.ogt"
|
|
12
12
|
# The night and the orb are authored colour: not tone mapped, and no light
|
|
13
13
|
# from the sky.
|
|
14
14
|
RenderSettings {
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
# Spark Orb's
|
|
1
|
+
# Spark Orb's style (the engine's neon style pack, recoloured): licences
|
|
2
2
|
|
|
3
|
-
The palette, the body size and the fallback font in `
|
|
3
|
+
The palette, the body size and the fallback font in `style.ogt` are this
|
|
4
4
|
game's edits by hand; `atlas.png` is the pack's atlas with its magenta
|
|
5
5
|
frames turned violet by `tools/make-assets.py`; the rest is the pack as
|
|
6
|
-
`templates/
|
|
6
|
+
`templates/styles/neon` ships it.
|
|
7
7
|
|
|
8
8
|
| File | Licence | Origin |
|
|
9
9
|
|---|---|---|
|
|
10
|
-
| `atlas.png` | CC0 1.0 | self-made by the Origin Engine authors: drawn by `templates/ui-kit/tools/make-
|
|
10
|
+
| `atlas.png` | CC0 1.0 | self-made by the Origin Engine authors: drawn by `templates/ui-kit/tools/make-styles.py` (deterministic) |
|
|
11
11
|
| `icons.png` | CC0 1.0 | self-made: the kit's 32 icons (`tools/make-assets.py`) restyled by the same script |
|
|
12
|
-
| `
|
|
12
|
+
| `style.ogt`, `*.ogt` | CC0 1.0 | written by the same script |
|
|
13
13
|
| `ChakraPetch-BoldItalic.ttf` | SIL Open Font License 1.1 (`OFL.txt`) | Chakra Petch Bold Italic (github.com/m4rc1e/Chakra-Petch); unmodified. Copyright 2018 The Chakra Petch Project Authors (https://github.com/m4rc1e/Chakra-Petch.git) |
|
|
14
14
|
| `Orbitron-Medium.ttf` | SIL Open Font License 1.1 (`OFL.txt`) | Orbitron Medium (github.com/theleagueof/orbitron); unmodified. Copyright 2018 The Orbitron Project Authors (https://github.com/theleagueof/orbitron), with Reserved Font Name: "Orbitron". |
|
|
15
15
|
| `Rajdhani-Bold.ttf` | SIL Open Font License 1.1 (`OFL.txt`) | Rajdhani Bold (Indian Type Foundry, Google Fonts); subset to Latin-1 with fontTools. Copyright (c) 2014 Indian Type Foundry (info@indiantypefoundry.com) |
|
|
16
16
|
|
|
17
17
|
Characters these fonts lack (Chinese, Japanese, Korean) are drawn with the
|
|
18
|
-
|
|
18
|
+
style's `fallback` font, `fonts/NotoSansSC-subset.ttf` (Noto Sans SC subset to
|
|
19
19
|
this game's Chinese strings, OFL 1.1, `assets/fonts/OFL.txt`).
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
type
|
|
1
|
+
type style
|
|
2
2
|
version 1
|
|
3
3
|
---
|
|
4
|
-
# Spark Orb's
|
|
4
|
+
# Spark Orb's style: the engine's neon style pack (templates/ui-kit/tools/make-styles.py, CC0 art,
|
|
5
5
|
# OFL fonts; LICENSES.md) with this game's palette (the orb's gold as the one accent on night
|
|
6
6
|
# violet) and its Chinese fallback font, edited by hand.
|
|
7
7
|
name "sparkorb"
|
|
8
|
-
|
|
9
|
-
atlas "
|
|
8
|
+
scheme dark
|
|
9
|
+
atlas "styles/sparkorb/atlas.png" 384 256
|
|
10
10
|
scale 1
|
|
11
11
|
palette {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
accentFg "#fff6e6"
|
|
32
|
-
accentInk "#ffc35c"
|
|
33
|
-
accentSoft "#ffb02e28"
|
|
12
|
+
backdrop "#0a0614"
|
|
13
|
+
well "#120a22"
|
|
14
|
+
panel "#1c1232"
|
|
15
|
+
panelHover "#281a46"
|
|
16
|
+
panelRaised "#1c1232"
|
|
17
|
+
line "#4a3470"
|
|
18
|
+
lineStrong "#c9a6ff"
|
|
19
|
+
ink "#fff6e6"
|
|
20
|
+
inkMuted "#e3d4bd"
|
|
21
|
+
inkFaint "#b9a890"
|
|
22
|
+
inkInverse "#140a00"
|
|
23
|
+
control "#3a2a5c"
|
|
24
|
+
controlStrong "#4a3874"
|
|
25
|
+
primary "#ad690a"
|
|
26
|
+
primaryHover "#d98f1f"
|
|
27
|
+
primaryActive "#a8650a"
|
|
28
|
+
onPrimary "#fff6e6"
|
|
29
|
+
primarySoft "#ffb02e28"
|
|
30
|
+
highlight "#ffc35c"
|
|
34
31
|
focus "#ffd27a"
|
|
35
|
-
|
|
32
|
+
knob "#5a3aa6"
|
|
33
|
+
hud "#1c1232"
|
|
36
34
|
scrim "#030008cc"
|
|
35
|
+
shadow "#000000aa"
|
|
36
|
+
sheen "#ffffff52"
|
|
37
37
|
success "#3ddc97"
|
|
38
38
|
warn "#ffd23d"
|
|
39
39
|
danger "#f45f80"
|
|
40
40
|
info "#36e8ff"
|
|
41
|
-
|
|
41
|
+
health "#3ddc97"
|
|
42
|
+
mana "#0a84ff"
|
|
43
|
+
xp "#bf5af2"
|
|
44
|
+
gold "#ad690a"
|
|
42
45
|
}
|
|
43
46
|
radius {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
panel 14
|
|
49
|
-
sheet 14
|
|
50
|
-
button 12
|
|
47
|
+
small 6
|
|
48
|
+
control 6
|
|
49
|
+
bar 6
|
|
50
|
+
panel 10
|
|
51
51
|
}
|
|
52
52
|
ramp {
|
|
53
53
|
body 16 21
|
|
@@ -68,12 +68,12 @@ durations {
|
|
|
68
68
|
slow 380
|
|
69
69
|
}
|
|
70
70
|
fonts {
|
|
71
|
-
body "
|
|
72
|
-
display "
|
|
73
|
-
numeric "
|
|
71
|
+
body "styles/sparkorb/Rajdhani-Bold.ttf"
|
|
72
|
+
display "styles/sparkorb/ChakraPetch-BoldItalic.ttf"
|
|
73
|
+
numeric "styles/sparkorb/Orbitron-Medium.ttf"
|
|
74
74
|
fallback "fonts/NotoSansSC-subset.ttf"
|
|
75
75
|
}
|
|
76
|
-
icons "
|
|
76
|
+
icons "styles/sparkorb/icons.png" 8 4 {
|
|
77
77
|
names "heart" "star" "coin" "lock" "pause" "gear" "sword" "shield" "potion" "gem" "key" "map" "bolt" "clock" "trophy" "home" "plus" "close" "check" "play" "back" "bag" "bomb" "crown" "flag" "apple" "leaf" "fire" "drop" "skull" "music" "person"
|
|
78
78
|
}
|
|
79
79
|
slice panel 4 4 96 96 22 authored
|
|
@@ -85,21 +85,21 @@ slice buttonDisabled 328 4 48 48 16 tint
|
|
|
85
85
|
slice field 4 104 48 48 10 tint
|
|
86
86
|
slice slot 56 104 48 48 12 tint
|
|
87
87
|
slice tooltip 108 104 32 32 10 tint
|
|
88
|
-
slice
|
|
88
|
+
slice hud 144 104 32 32 14 tint
|
|
89
89
|
slice track 180 104 32 16 6 tint
|
|
90
90
|
slice fill 216 104 32 16 6 tint
|
|
91
91
|
soft 252 104 64 64 24
|
|
92
|
-
corner 320 104 32 32 20 20
|
|
92
|
+
corner 320 104 32 32 20 20 primary 2
|
|
93
93
|
corners card
|
|
94
|
-
divider 4 172 128 24 128 24
|
|
95
|
-
flourish 136 172 64 24 36 16
|
|
94
|
+
divider 4 172 128 24 128 24 primary
|
|
95
|
+
flourish 136 172 64 24 36 16 primary 0
|
|
96
96
|
tile 204 172 64 64 48 48 0.12 10 panel
|
|
97
97
|
shadow panel 0 0 0 0 "#00000000"
|
|
98
|
-
glow button 6
|
|
99
|
-
glow
|
|
100
|
-
glow panel 14
|
|
98
|
+
glow button 6 primary 0.35
|
|
99
|
+
glow hud 4 primary 0.25
|
|
100
|
+
glow panel 14 primary 0.18
|
|
101
101
|
glow slot 3 focus 0.2
|
|
102
|
-
textGlow display 2.0
|
|
102
|
+
textGlow display 2.0 primary 0.7
|
|
103
103
|
textGlow numeric 1.2 focus 0.5
|
|
104
104
|
press 0.96 0.96 0
|
|
105
105
|
hover 0 1.04
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -24,7 +24,7 @@ export const Sound = { Tap: 1, Buy: 2, Milestone: 4, Nova: 8, Welcome: 16, Move:
|
|
|
24
24
|
/** The run, the save and the effects' bookkeeping. */
|
|
25
25
|
export const Game = defineResource("Game", {
|
|
26
26
|
phase: u32(Phase.Title, { doc: "Phase.* of src/components.ts." }),
|
|
27
|
-
leftTitle: bool(false, { doc: "The player has left the title the game opened on: from then on the title enters with the
|
|
27
|
+
leftTitle: bool(false, { doc: "The player has left the title the game opened on: from then on the title enters with the style's entrance (the opening one is built settled, og.ui.mainMenu's instant: the web draws it behind the play gate)." }),
|
|
28
28
|
paused: bool(false),
|
|
29
29
|
settingsOpen: bool(false),
|
|
30
30
|
held: bool(false, { doc: "The time scale is held at 0 (paused, in the settings or a dialog)." }),
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// the rate.
|
|
10
10
|
//
|
|
11
11
|
// The look (GameDesc.look, a file under assets/looks/) sets the frame's
|
|
12
|
-
// treatment and the UI
|
|
12
|
+
// treatment and the UI style before start runs. `start` loads the strings,
|
|
13
13
|
// the sounds and the orb's art and spawns the rows: the orb and its glow,
|
|
14
14
|
// 48 motes (12 per generator), the burst pools, a voice per sound and the
|
|
15
15
|
// music. The systems are in
|
|
@@ -35,11 +35,11 @@ function must<T>(r: { ok: true; value?: T } | { ok: false; reason: string }): T
|
|
|
35
35
|
|
|
36
36
|
function start(world: World): void {
|
|
37
37
|
for (const l of LANGUAGES) must(og.i18n.load(world, `strings/${l.code}.ogt`));
|
|
38
|
-
// GameDesc.look set the frame and loaded the
|
|
39
|
-
// keeps that
|
|
40
|
-
const
|
|
38
|
+
// GameDesc.look set the frame and loaded the style before start; the UI
|
|
39
|
+
// keeps that style and adds the sounds.
|
|
40
|
+
const style = og.render.look(world)?.style ?? null;
|
|
41
41
|
const sound = (path: string) => must(og.load.sound(world, path));
|
|
42
|
-
must(og.ui.configure(world, {
|
|
42
|
+
must(og.ui.configure(world, { style, sounds: { press: sound("audio/ui-press.ogt"), open: sound("audio/ui-open.ogt"), close: sound("audio/ui-close.ogt"), error: sound("audio/ui-error.ogt"), success: sound("audio/ui-success.ogt") } }));
|
|
43
43
|
|
|
44
44
|
const camera = must(world.resource(og.Camera));
|
|
45
45
|
camera.orthographic = true;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Spark Orb's screens on og.ui and the
|
|
1
|
+
// Spark Orb's screens on og.ui and the style in assets/styles/sparkorb (the
|
|
2
2
|
// engine's neon pack in the orb's gold on night violet): the title, the
|
|
3
3
|
// HUD (the sparks and their rate top left, stardust and pause top right,
|
|
4
4
|
// the four generators along the bottom, the supernova's progress), the
|
|
@@ -25,7 +25,7 @@ export const LANGUAGES = [
|
|
|
25
25
|
{ code: "zh-CN", key: "lang.zh" },
|
|
26
26
|
] as const;
|
|
27
27
|
|
|
28
|
-
/** The generators' icons in the
|
|
28
|
+
/** The generators' icons in the style's atlas. */
|
|
29
29
|
const ICONS = ["fire", "bolt", "gem", "star"] as const;
|
|
30
30
|
|
|
31
31
|
/** The tokens' spacing scale in CSS px (og.ui.frameTheme answers it too, but builds the whole theme each call). */
|
|
@@ -166,7 +166,7 @@ function platform(world: World, g: GameV, p: PrefsV): void {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
function capsule(id: string, build: () => void): void {
|
|
169
|
-
og.ui.row(id, { gap: S.xs, cross: "center", padding: { left: S.sm, top: S.xs, right: S.md, bottom: S.xs }, frame: "
|
|
169
|
+
og.ui.row(id, { gap: S.xs, cross: "center", padding: { left: S.sm, top: S.xs, right: S.md, bottom: S.xs }, frame: "hud", background: { token: "panel" } }, build);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
function hud(world: World, g: GameV): void {
|
|
@@ -176,13 +176,13 @@ function hud(world: World, g: GameV): void {
|
|
|
176
176
|
og.ui.setNavigation(false);
|
|
177
177
|
og.ui.anchor("Wallet", { anchor: "topLeft", direction: "column", gap: S.xs }, () => {
|
|
178
178
|
capsule("Sparks", () => {
|
|
179
|
-
og.ui.icon("Icon", "star", { size: 24, tint: { token: "
|
|
179
|
+
og.ui.icon("Icon", "star", { size: 24, tint: { token: "highlight" } });
|
|
180
180
|
// The sparks change every tick, so they are a label in the locale's words, not a counter
|
|
181
181
|
// (a counter re-counts from where it is on every change); counters show what jumps.
|
|
182
182
|
og.ui.label("Amount", big(world, g.sparks), { step: "xl", font: "display", nowrap: true });
|
|
183
183
|
});
|
|
184
184
|
og.ui.row("RateRow", { padding: { left: S.sm, top: 0, right: 0, bottom: 0 } }, () => {
|
|
185
|
-
og.ui.label("Rate", say(world, "hud.rate", { n: big(world, g.rate) }), { step: "body", color: { token: "
|
|
185
|
+
og.ui.label("Rate", say(world, "hud.rate", { n: big(world, g.rate) }), { step: "body", color: { token: "inkMuted" }, nowrap: true });
|
|
186
186
|
});
|
|
187
187
|
});
|
|
188
188
|
let paused = false;
|
|
@@ -213,10 +213,10 @@ function hud(world: World, g: GameV): void {
|
|
|
213
213
|
if (g.refusedSlot === i && g.refusedTick === world.tick - 1) og.ui.shake({ sound: true });
|
|
214
214
|
if (g.milestoneSlot === i && g.milestoneTick === world.tick - 1) og.ui.burst({ count: 10 });
|
|
215
215
|
og.ui.row("Head", { gap: S.xxs, cross: "center" }, () => {
|
|
216
|
-
og.ui.label("Name", say(world, `gen.${name}`), { step: "body", color: { token: g.selected === i ? "
|
|
217
|
-
og.ui.counter("Owned", n, { font: "mono", prefix: "×", color: { token: "
|
|
216
|
+
og.ui.label("Name", say(world, `gen.${name}`), { step: "body", color: { token: g.selected === i ? "highlight" : "ink" }, nowrap: true });
|
|
217
|
+
og.ui.counter("Owned", n, { font: "mono", prefix: "×", color: { token: "inkMuted" } });
|
|
218
218
|
});
|
|
219
|
-
og.ui.label("Cost", big(world, cost), { font: "mono", color: { token: can ? "
|
|
219
|
+
og.ui.label("Cost", big(world, cost), { font: "mono", color: { token: can ? "highlight" : "inkMuted" }, nowrap: true });
|
|
220
220
|
});
|
|
221
221
|
});
|
|
222
222
|
});
|
|
@@ -226,9 +226,9 @@ function hud(world: World, g: GameV): void {
|
|
|
226
226
|
og.ui.anchor("Nova", { anchor: phone ? "topRight" : "bottomRight", offsetY: phone ? 64 : 0, direction: "column", cross: "center", gap: S.xxs }, () => {
|
|
227
227
|
if (gain >= 1) {
|
|
228
228
|
if (og.ui.actionButton("Supernova", say(world, "hud.nova"), { icon: "bolt", size: 64, primary: true }).clicked) g.confirmNova = true;
|
|
229
|
-
og.ui.label("Gain", say(world, "hud.novaGain", { n: gain }), { color: { token: "
|
|
229
|
+
og.ui.label("Gain", say(world, "hud.novaGain", { n: gain }), { color: { token: "highlight" }, nowrap: true });
|
|
230
230
|
} else if (g.runEarned > 0) {
|
|
231
|
-
og.ui.label("At", say(world, "hud.novaAt", { n: big(world, novaAt(t)) }), { step: "caption", color: { token: "
|
|
231
|
+
og.ui.label("At", say(world, "hud.novaAt", { n: big(world, novaAt(t)) }), { step: "caption", color: { token: "inkMuted" }, nowrap: true });
|
|
232
232
|
og.ui.meter("Progress", Math.min(g.runEarned, novaAt(t)), novaAt(t), { width: px(phone ? 110 : 140) });
|
|
233
233
|
}
|
|
234
234
|
});
|
|
@@ -250,7 +250,7 @@ function settings(world: World, g: GameV, p: PrefsV): void {
|
|
|
250
250
|
p.master = slider("Master", "settings.master", p.master);
|
|
251
251
|
p.music = slider("Music", "settings.music", p.music);
|
|
252
252
|
p.effects = slider("Effects", "settings.effects", p.effects);
|
|
253
|
-
og.ui.label("LanguageLabel", say(world, "settings.language"), { color: { token: "
|
|
253
|
+
og.ui.label("LanguageLabel", say(world, "settings.language"), { color: { token: "inkMuted" } });
|
|
254
254
|
const r = og.ui.radio("Language", LANGUAGES.map((l) => say(world, l.key)), p.language, { horizontal: true });
|
|
255
255
|
if (r.selected !== p.language) setLanguage(world, p, r.selected);
|
|
256
256
|
p.reducedMotion = og.ui.toggle("Motion", p.reducedMotion, say(world, "settings.motion")).on;
|
|
@@ -287,7 +287,7 @@ function dialogs(world: World, g: GameV): void {
|
|
|
287
287
|
}
|
|
288
288
|
let collect = false;
|
|
289
289
|
const w = og.ui.modal("Welcome", g.welcome, { title: say(world, "welcome.title"), width }, () => {
|
|
290
|
-
og.ui.row("Mark", { width: grow, align: "center" }, () => og.ui.icon("Icon", "clock", { size: 40, tint: { token: "
|
|
290
|
+
og.ui.row("Mark", { width: grow, align: "center" }, () => og.ui.icon("Icon", "clock", { size: 40, tint: { token: "highlight" } }));
|
|
291
291
|
og.ui.label("Body", say(world, "welcome.body", { time: duration(world, g.away), n: big(world, g.offline) }), { width: grow, align: "center" });
|
|
292
292
|
og.ui.row("Actions", { width: grow, align: "center" }, () => {
|
|
293
293
|
if (og.ui.button("Collect", say(world, "welcome.collect"), { variant: "primary" }).clicked) collect = true;
|
|
@@ -373,7 +373,7 @@ export const Ui = defineSystem("Ui", { reads: [Tuning, og.Input, og.Gamepad], wr
|
|
|
373
373
|
],
|
|
374
374
|
footer: say(world, "title.footer"),
|
|
375
375
|
// The title the game opens on is built settled: the web draws it behind the play gate, where
|
|
376
|
-
// the
|
|
376
|
+
// the style's entrance would stand part-way until the tap (og-platform-sdk §3). Later titles enter.
|
|
377
377
|
instant: !g.leftTitle,
|
|
378
378
|
});
|
|
379
379
|
if (!g.settingsOpen && r.selected === "play") {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
the sky's haze), spark.png (particles and the fixed stars) and
|
|
9
9
|
mote-<generator>.png (the four motes: an ember, a bolt, a crystal, a
|
|
10
10
|
star),
|
|
11
|
-
- assets/
|
|
11
|
+
- assets/styles/sparkorb/atlas.png: the neon pack's atlas
|
|
12
12
|
(templates/ui-kit) with its magenta frames turned violet,
|
|
13
13
|
- assets/fx/*.ogt: the particle emitters (the tap's burst, a milestone's
|
|
14
14
|
ring, the supernova, the sparks drifting up from the orb, the stars),
|
|
@@ -272,10 +272,10 @@ render sprite 0
|
|
|
272
272
|
""")
|
|
273
273
|
|
|
274
274
|
|
|
275
|
-
# ── the
|
|
275
|
+
# ── the style's atlas ───────────────────────────────────────────────────────
|
|
276
276
|
|
|
277
277
|
def read_png(path):
|
|
278
|
-
"""An 8-bit RGBA, non-interlaced PNG's rows of pixels (what make-
|
|
278
|
+
"""An 8-bit RGBA, non-interlaced PNG's rows of pixels (what make-styles.py writes)."""
|
|
279
279
|
data = open(path, "rb").read()
|
|
280
280
|
at, idat, w, h = 8, b"", 0, 0
|
|
281
281
|
while at < len(data):
|
|
@@ -315,7 +315,7 @@ def read_png(path):
|
|
|
315
315
|
def atlas():
|
|
316
316
|
"""The neon pack's atlas with its baked magenta frames turned to the
|
|
317
317
|
night's violet (hue 300 to 262), the rest untouched."""
|
|
318
|
-
src = os.path.join(root, "..", "..", "
|
|
318
|
+
src = os.path.join(root, "..", "..", "styles", "neon", "atlas.png")
|
|
319
319
|
if not os.path.exists(src):
|
|
320
320
|
print("atlas recolour skipped (no templates/ui-kit here); the committed atlas stays")
|
|
321
321
|
return None
|
|
@@ -434,7 +434,7 @@ def main():
|
|
|
434
434
|
write(f"sprites/mote-{kind}.png.ogt", LINEAR)
|
|
435
435
|
recoloured = atlas()
|
|
436
436
|
if recoloured is not None:
|
|
437
|
-
write("
|
|
437
|
+
write("styles/sparkorb/atlas.png", recoloured)
|
|
438
438
|
write("fx/tap.ogt", TAP)
|
|
439
439
|
write("fx/milestone.ogt", MILESTONE)
|
|
440
440
|
write("fx/nova.ogt", NOVA)
|
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
| `assets/fx/*.ogt` | CC0 1.0 | self-made: the hit's burst, same script |
|
|
10
10
|
| `assets/strings/*.ogt` | CC0 1.0 | self-made: the English and Chinese string tables |
|
|
11
11
|
| `assets/fonts/NotoSansSC-subset.ttf` | SIL Open Font License 1.1 (`assets/fonts/OFL.txt`) | Noto Sans SC Regular, subset to Latin-1 and the characters of `assets/strings/zh-CN.ogt` by the same script from the project template's `assets/fonts/NotoSansSC-Regular-subset.ttf` |
|
|
12
|
-
| `assets/
|
|
12
|
+
| `assets/styles/lanebeat/*` | CC0 1.0 art, SIL OFL 1.1 fonts | the engine's sci-fi style pack (`templates/styles/scifi`, `assets/styles/lanebeat/LICENSES.md`) with this game's palette |
|
|
13
13
|
| `references/*/*.png` | CC0 1.0 | frames of this game recorded by `tests/frames.og.ts` |
|