@v12-engine/create 0.1.1 → 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 +4 -1
- package/base/_gitignore +5 -0
- package/base/package.json +3 -3
- package/base/src/main.ts +36 -10
- package/base/src/navigation.worker.ts +7 -0
- package/base/vite.config.ts +24 -2
- package/cli.mjs +21 -6
- package/index.mjs +27 -7
- package/layers/desktop/electron/app.mjs +237 -0
- package/layers/desktop/electron/config.mjs +16 -0
- package/layers/desktop/electron/dev.mjs +63 -0
- package/layers/desktop/electron/files.mjs +86 -0
- package/layers/desktop/electron/icons/icon.icns +0 -0
- package/layers/desktop/electron/icons/icon.png +0 -0
- package/layers/desktop/electron/main.mjs +12 -0
- package/layers/desktop/electron/package.mjs +75 -0
- package/layers/desktop/electron/preload.cjs +41 -0
- package/layers/desktop/electron/presentation.m +87 -0
- package/layers/desktop/electron/serve.mjs +76 -0
- package/layers/desktop/electron/tools.mjs +63 -0
- package/layers/desktop/electron/updates.mjs +71 -0
- package/layers/desktop/package.json +13 -0
- package/layers/desktop/src/desktop.ts +175 -0
- package/layers/desktop/src/shell.ts +279 -0
- package/layers/desktop/tests/main.test.ts +189 -0
- package/layers/desktop/tests/shell.test.ts +152 -0
- package/layers/rooms/server.ts +13 -10
- package/layers/server/server.ts +4 -5
- package/layers/source/vite.config.ts +21 -1
- package/package.json +1 -1
- package/starters/campaign/README.md +9 -2
- package/starters/campaign/layers/rooms/server.ts +3 -3
- package/starters/campaign/layers/server/server.ts +3 -5
- package/starters/campaign/layers/tests/tests/rules.test.ts +40 -21
- package/starters/campaign/src/campaign/game.ts +26 -5
- package/starters/campaign/src/campaign/index.ts +104 -68
- package/starters/campaign/src/campaign/kills.ts +1 -1
- package/starters/campaign/src/campaign/missions/homecoming.ts +7 -7
- package/starters/campaign/src/campaign/missions/siege.ts +6 -6
- package/starters/campaign/src/campaign/missions/warlord.ts +13 -14
- package/starters/campaign/src/campaign/scenes.ts +8 -7
- package/starters/campaign/src/campaign/units.ts +59 -20
- package/starters/campaign/src/campaign/view/attacks.ts +19 -0
- package/starters/campaign/src/campaign/view/fire.ts +2 -2
- package/starters/campaign/src/campaign/view/index.ts +54 -43
- package/starters/campaign/src/campaign/waves.ts +18 -17
- package/starters/campaign/src/game.ts +8 -1
- package/starters/campaign/src/main.ts +38 -19
- package/starters/campaign/src/maps/fort.names.ts +2 -2
- package/starters/campaign/src/maps/fort.vgmap.json +102 -2
- package/starters/campaign/src/maps/homestead.names.ts +2 -2
- package/starters/campaign/src/maps/homestead.vgmap.json +85 -1
- package/starters/campaign/src/maps/pass.names.ts +2 -2
- package/starters/campaign/src/maps/pass.vgmap.json +69 -1
- package/starters/campaign/src/waves-panel.ts +2 -1
- package/starters/empty/README.md +18 -10
- package/starters/empty/layers/tests/tests/rules.test.ts +16 -17
- package/starters/empty/src/game.ts +11 -75
- package/starters/empty/src/generate.worker.ts +5 -5
- package/starters/empty/src/rules.ts +83 -25
- package/starters/empty/src/view.ts +82 -0
- package/starters/examples/README.md +83 -0
- package/starters/examples/layers/tests/tests/examples.test.ts +30 -0
- package/starters/examples/package.json +5 -0
- package/starters/examples/src/examples/abilities/areas.ts +165 -0
- package/starters/examples/src/examples/abilities/areasLooks.ts +30 -0
- package/starters/examples/src/examples/abilities/buffs.ts +287 -0
- package/starters/examples/src/examples/abilities/buffsLooks.ts +46 -0
- package/starters/examples/src/examples/abilities/changing.ts +228 -0
- package/starters/examples/src/examples/abilities/changingLooks.ts +61 -0
- package/starters/examples/src/examples/abilities/control.ts +158 -0
- package/starters/examples/src/examples/abilities/controlLooks.ts +78 -0
- package/starters/examples/src/examples/abilities/damage.ts +156 -0
- package/starters/examples/src/examples/abilities/damageLooks.ts +37 -0
- package/starters/examples/src/examples/abilities/family.ts +29 -0
- package/starters/examples/src/examples/abilities/index.ts +32 -0
- package/starters/examples/src/examples/abilities/kit.ts +179 -0
- package/starters/examples/src/examples/abilities/lookKit.ts +71 -0
- package/starters/examples/src/examples/abilities/looks.ts +47 -0
- package/starters/examples/src/examples/abilities/missiles.ts +203 -0
- package/starters/examples/src/examples/abilities/missilesLooks.ts +38 -0
- package/starters/examples/src/examples/abilities/moves.ts +206 -0
- package/starters/examples/src/examples/abilities/movesLooks.ts +52 -0
- package/starters/examples/src/examples/abilities/recasts.ts +181 -0
- package/starters/examples/src/examples/abilities/recastsLooks.ts +41 -0
- package/starters/examples/src/examples/abilities/resources.ts +166 -0
- package/starters/examples/src/examples/abilities/resourcesLooks.ts +42 -0
- package/starters/examples/src/examples/abilities/summons.ts +296 -0
- package/starters/examples/src/examples/abilities/summonsLooks.ts +115 -0
- package/starters/examples/src/examples/abilities/triggers.ts +204 -0
- package/starters/examples/src/examples/abilities/triggersLooks.ts +53 -0
- package/starters/examples/src/examples/abilities/vision.ts +137 -0
- package/starters/examples/src/examples/abilities/visionLooks.ts +50 -0
- package/starters/examples/src/examples/animation/index.ts +235 -0
- package/starters/examples/src/examples/animation/looks.ts +444 -0
- package/starters/examples/src/examples/catalog.ts +52 -0
- package/starters/examples/src/examples/cinematics/index.ts +217 -0
- package/starters/examples/src/examples/cinematics/looks.ts +476 -0
- package/starters/examples/src/examples/cinematics/tour.json +43 -0
- package/starters/examples/src/examples/cinematics/village.ts +88 -0
- package/starters/examples/src/examples/common/looks.ts +65 -0
- package/starters/examples/src/examples/common/units.ts +70 -0
- package/starters/examples/src/examples/construction/index.ts +329 -0
- package/starters/examples/src/examples/construction/looks.ts +275 -0
- package/starters/examples/src/examples/custom-animation/bones.ts +92 -0
- package/starters/examples/src/examples/custom-animation/index.ts +190 -0
- package/starters/examples/src/examples/custom-animation/looks.ts +523 -0
- package/starters/examples/src/examples/custom-animation/runes.ts +82 -0
- package/starters/examples/src/examples/custom-fx/glaive.look.ts +110 -0
- package/starters/examples/src/examples/custom-fx/glaive.ts +40 -0
- package/starters/examples/src/examples/custom-fx/gpu.look.ts +145 -0
- package/starters/examples/src/examples/custom-fx/gpu.ts +22 -0
- package/starters/examples/src/examples/custom-fx/index.ts +25 -0
- package/starters/examples/src/examples/custom-fx/lights.look.ts +144 -0
- package/starters/examples/src/examples/custom-fx/lights.ts +25 -0
- package/starters/examples/src/examples/custom-fx/looks.ts +36 -0
- package/starters/examples/src/examples/custom-fx/meteor.look.ts +133 -0
- package/starters/examples/src/examples/custom-fx/meteor.ts +26 -0
- package/starters/examples/src/examples/custom-fx/parts.look.ts +193 -0
- package/starters/examples/src/examples/custom-fx/parts.ts +21 -0
- package/starters/examples/src/examples/custom-fx/portal.look.ts +193 -0
- package/starters/examples/src/examples/custom-fx/portal.ts +48 -0
- package/starters/examples/src/examples/custom-fx/renderer.look.ts +139 -0
- package/starters/examples/src/examples/custom-fx/renderer.ts +18 -0
- package/starters/examples/src/examples/custom-fx/ribbons.look.ts +151 -0
- package/starters/examples/src/examples/custom-fx/ribbons.ts +22 -0
- package/starters/examples/src/examples/custom-fx/shader.ts +42 -0
- package/starters/examples/src/examples/custom-fx/shield.look.ts +226 -0
- package/starters/examples/src/examples/custom-fx/shield.ts +43 -0
- package/starters/examples/src/examples/define.ts +86 -0
- package/starters/examples/src/examples/destruction/index.ts +570 -0
- package/starters/examples/src/examples/destruction/looks.ts +447 -0
- package/starters/examples/src/examples/dialogs/index.ts +383 -0
- package/starters/examples/src/examples/dialogs/looks.ts +546 -0
- package/starters/examples/src/examples/dynamic-map/index.ts +603 -0
- package/starters/examples/src/examples/dynamic-map/looks.ts +301 -0
- package/starters/examples/src/examples/fx/blood.look.ts +62 -0
- package/starters/examples/src/examples/fx/blood.ts +59 -0
- package/starters/examples/src/examples/fx/feel.look.ts +86 -0
- package/starters/examples/src/examples/fx/feel.ts +57 -0
- package/starters/examples/src/examples/fx/fight.ts +28 -0
- package/starters/examples/src/examples/fx/floats.look.ts +108 -0
- package/starters/examples/src/examples/fx/floats.ts +24 -0
- package/starters/examples/src/examples/fx/gallery.look.ts +168 -0
- package/starters/examples/src/examples/fx/gallery.ts +23 -0
- package/starters/examples/src/examples/fx/index.ts +30 -0
- package/starters/examples/src/examples/fx/links.look.ts +83 -0
- package/starters/examples/src/examples/fx/links.ts +49 -0
- package/starters/examples/src/examples/fx/looks.ts +55 -0
- package/starters/examples/src/examples/fx/map.look.ts +93 -0
- package/starters/examples/src/examples/fx/map.ts +33 -0
- package/starters/examples/src/examples/fx/marks.look.ts +100 -0
- package/starters/examples/src/examples/fx/marks.ts +36 -0
- package/starters/examples/src/examples/fx/shots.look.ts +92 -0
- package/starters/examples/src/examples/fx/shots.ts +71 -0
- package/starters/examples/src/examples/fx/spells.look.ts +87 -0
- package/starters/examples/src/examples/fx/spells.ts +122 -0
- package/starters/examples/src/examples/fx/stage.ts +31 -0
- package/starters/examples/src/examples/gui/babylonScreen.ts +167 -0
- package/starters/examples/src/examples/gui/babylonWorld.ts +191 -0
- package/starters/examples/src/examples/gui/htmlPart.ts +95 -0
- package/starters/examples/src/examples/gui/index.ts +143 -0
- package/starters/examples/src/examples/gui/keys.ts +91 -0
- package/starters/examples/src/examples/gui/layout.ts +42 -0
- package/starters/examples/src/examples/gui/looks.ts +270 -0
- package/starters/examples/src/examples/gui/minimap.ts +127 -0
- package/starters/examples/src/examples/gui/ownInterface.ts +129 -0
- package/starters/examples/src/examples/gui/plates.ts +87 -0
- package/starters/examples/src/examples/gui/radial.ts +104 -0
- package/starters/examples/src/examples/gui/solidPart.ts +107 -0
- package/starters/examples/src/examples/looks.ts +45 -0
- package/starters/examples/src/examples/mechanics/battle.ts +51 -0
- package/starters/examples/src/examples/mechanics/damage.ts +80 -0
- package/starters/examples/src/examples/mechanics/flying.ts +93 -0
- package/starters/examples/src/examples/mechanics/formations.ts +58 -0
- package/starters/examples/src/examples/mechanics/highGround.ts +82 -0
- package/starters/examples/src/examples/mechanics/index.ts +44 -0
- package/starters/examples/src/examples/mechanics/looks.ts +289 -0
- package/starters/examples/src/examples/mechanics/maze.ts +119 -0
- package/starters/examples/src/examples/mechanics/orders.ts +68 -0
- package/starters/examples/src/examples/mechanics/selection.ts +30 -0
- package/starters/examples/src/examples/moba/index.ts +377 -0
- package/starters/examples/src/examples/moba/looks.ts +349 -0
- package/starters/examples/src/examples/moba/maps.ts +69 -0
- package/starters/examples/src/examples/moba/spells.ts +34 -0
- package/starters/examples/src/examples/moba/units.ts +196 -0
- package/starters/examples/src/examples/nature/camera.ts +15 -0
- package/starters/examples/src/examples/nature/grass.ts +64 -0
- package/starters/examples/src/examples/nature/index.ts +327 -0
- package/starters/examples/src/examples/nature/land.ts +173 -0
- package/starters/examples/src/examples/nature/looks.ts +577 -0
- package/starters/examples/src/examples/nature/maps.ts +370 -0
- package/starters/examples/src/examples/nature/skies.ts +324 -0
- package/starters/examples/src/examples/nature/weather.ts +80 -0
- package/starters/examples/src/examples/performance/index.ts +263 -0
- package/starters/examples/src/examples/performance/looks.ts +372 -0
- package/starters/examples/src/examples/performance/meter.ts +158 -0
- package/starters/examples/src/examples/post/grades.ts +86 -0
- package/starters/examples/src/examples/post/index.ts +202 -0
- package/starters/examples/src/examples/post/looks.ts +884 -0
- package/starters/examples/src/examples/post/passes.ts +213 -0
- package/starters/examples/src/examples/rts/ai.ts +49 -0
- package/starters/examples/src/examples/rts/airlift.ts +43 -0
- package/starters/examples/src/examples/rts/base.ts +129 -0
- package/starters/examples/src/examples/rts/build.ts +65 -0
- package/starters/examples/src/examples/rts/camps.ts +36 -0
- package/starters/examples/src/examples/rts/carry.ts +103 -0
- package/starters/examples/src/examples/rts/creeps.ts +61 -0
- package/starters/examples/src/examples/rts/data.ts +406 -0
- package/starters/examples/src/examples/rts/ferry.ts +41 -0
- package/starters/examples/src/examples/rts/garrison.ts +50 -0
- package/starters/examples/src/examples/rts/harvest.ts +29 -0
- package/starters/examples/src/examples/rts/hero.ts +67 -0
- package/starters/examples/src/examples/rts/index.ts +77 -0
- package/starters/examples/src/examples/rts/looks.ts +721 -0
- package/starters/examples/src/examples/rts/shop.ts +46 -0
- package/starters/examples/src/examples/rts/tech.ts +39 -0
- package/starters/examples/src/examples/rts/train.ts +48 -0
- package/starters/examples/src/examples/rts/waterside.ts +185 -0
- package/starters/examples/src/examples/showcase/index.ts +714 -0
- package/starters/examples/src/examples/showcase/looks.ts +238 -0
- package/starters/examples/src/game.ts +17 -0
- package/starters/examples/src/main.ts +82 -0
- package/starters/examples/src/rules.ts +31 -0
- package/starters/examples/src/shell/look.ts +79 -0
- package/starters/examples/src/shell/registry.ts +7 -0
- package/starters/examples/src/shell/scenes.ts +178 -0
- package/starters/examples/src/shell/shell.ts +290 -0
- package/starters/examples/src/shell/style.ts +202 -0
- package/starters/examples/src/view.ts +50 -0
- package/starters/melee/README.md +12 -3
- package/starters/melee/layers/tests/tests/heroes.test.ts +402 -0
- package/starters/melee/layers/tests/tests/rules.test.ts +10 -8
- package/starters/melee/src/game.ts +32 -2
- package/starters/melee/src/maps/bay.names.ts +2 -2
- package/starters/melee/src/maps/bay.vgmap.json +198 -1
- package/starters/melee/src/maps.ts +11 -9
- package/starters/melee/src/melee/abilities.ts +1 -194
- package/starters/melee/src/melee/blight.ts +7 -7
- package/starters/melee/src/melee/cellLayers.json +3 -3
- package/starters/melee/src/melee/data.json +1 -18
- package/starters/melee/src/melee/game.ts +6 -4
- package/starters/melee/src/melee/heroes.ts +1025 -0
- package/starters/melee/src/melee/index.ts +64 -61
- package/starters/melee/src/melee/items.ts +15 -362
- package/starters/melee/src/melee/languages/he.json +14 -2
- package/starters/melee/src/melee/pings.ts +5 -6
- package/starters/melee/src/melee/saves.ts +1 -1
- package/starters/melee/src/melee/undead.ts +15 -230
- package/starters/melee/src/melee/units.ts +37 -171
- package/starters/melee/src/melee/view/attacks.ts +19 -0
- package/starters/melee/src/melee/view/heroSpellLooks.ts +39 -8
- package/starters/melee/src/melee/view/index.ts +56 -52
- package/starters/melee/src/melee/view/looks.ts +86 -63
- package/starters/melee/src/melee/view/undeadLooks.ts +70 -31
- package/starters/melee/src/melee/view/walls.ts +1 -1
- package/starters.mjs +6 -0
- package/v12.mjs +14 -15
- package/starters/campaign/src/rules.ts +0 -20
- package/starters/empty/src/maps.ts +0 -44
- package/starters/empty/src/tiles.ts +0 -12
- package/starters/melee/src/melee/heroSpells.ts +0 -38
- package/starters/melee/src/rules.ts +0 -24
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The post-processing examples on the client's side: each changes how the picture is finished after the scene is
|
|
3
|
+
* drawn (`client.picture`, `WorldPicture`), and puts it back as it is left. The look (`picture.look`: tone, exposure,
|
|
4
|
+
* colours, a grade, bloom, vignette, sharpening, ambient occlusion) is the game's look of the picture; the lens
|
|
5
|
+
* (`picture.cinematic`: depth of field, grain, the colours split) is what a cinematic asks; outlines, ink and the
|
|
6
|
+
* screen's bars are the client's too; passes of our own go on with `picture.addPass` (`./passes.ts`)
|
|
7
|
+
*/
|
|
8
|
+
import type { CinematicPicture, GameClientApi, MapPictureLook } from '@v12-engine/client'
|
|
9
|
+
import { button, type CategoryLooks, readout, select, slider, toggle } from '../../shell/look'
|
|
10
|
+
import { PLAYER } from '../common/units'
|
|
11
|
+
import { stand } from '../nature/camera'
|
|
12
|
+
import { gradePath, GRADES, teachGrades } from './grades'
|
|
13
|
+
import { SQUARE } from '../nature/maps'
|
|
14
|
+
import { addShader, type OwnPass, SHADERS, type ShaderName } from './passes'
|
|
15
|
+
|
|
16
|
+
// ---- the picture, changed and put back
|
|
17
|
+
|
|
18
|
+
/** What the picture was before an example changed it: its look, its lens and how it smooths edges, to put back */
|
|
19
|
+
interface Before {
|
|
20
|
+
look: MapPictureLook
|
|
21
|
+
cinematic: CinematicPicture | null
|
|
22
|
+
antialias: GameClientApi['picture']['antialias']
|
|
23
|
+
occlusion: GameClientApi['picture']['occlusion']
|
|
24
|
+
resolve: GameClientApi['picture']['resolve']
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Keeps what the picture is now, and gives back what puts it back so */
|
|
28
|
+
function keep(client: GameClientApi): () => void {
|
|
29
|
+
const { picture } = client
|
|
30
|
+
const before: Before = {
|
|
31
|
+
look: picture.look,
|
|
32
|
+
cinematic: picture.cinematic,
|
|
33
|
+
antialias: picture.antialias,
|
|
34
|
+
occlusion: picture.occlusion,
|
|
35
|
+
resolve: picture.resolve
|
|
36
|
+
}
|
|
37
|
+
const scale = client.renderScale
|
|
38
|
+
return () => {
|
|
39
|
+
picture.look = before.look
|
|
40
|
+
picture.cinematic = before.cinematic
|
|
41
|
+
picture.antialias = before.antialias
|
|
42
|
+
picture.occlusion = before.occlusion
|
|
43
|
+
picture.resolve = before.resolve
|
|
44
|
+
client.renderScale = scale
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The picture's look changed field by field, over what it is now (`picture.look`, the game's look of the picture) */
|
|
49
|
+
const look = (client: GameClientApi, change: MapPictureLook) => {
|
|
50
|
+
client.picture.look = { ...client.picture.look, ...change }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The lens changed field by field (`picture.cinematic`: what only a cinematic asks, depth of field, grain, the colours split) */
|
|
54
|
+
const lens = (client: GameClientApi, change: CinematicPicture) => {
|
|
55
|
+
client.picture.cinematic = { ...client.picture.cinematic, ...change }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The people about the square, by side */
|
|
59
|
+
const people = (client: GameClientApi, owner: number) => client.session.snapshot().entities.filter((each) => each.owner === owner && each.health > 0)
|
|
60
|
+
|
|
61
|
+
/** The knight in the middle of the square: what the lens focuses on */
|
|
62
|
+
const knight = (client: GameClientApi) => client.session.snapshot().entities.find((each) => each.type === 'knight')
|
|
63
|
+
|
|
64
|
+
/** The square seen from the south-east, near and low */
|
|
65
|
+
const onSquare = (client: GameClientApi, distance = 22, pitch = 30) => stand(client, { x: SQUARE.x, z: SQUARE.z + 1 }, distance, 200, pitch)
|
|
66
|
+
|
|
67
|
+
/** What the picture says now, in short */
|
|
68
|
+
const lookNow = (client: GameClientApi) => {
|
|
69
|
+
const now = client.picture.now
|
|
70
|
+
const parts = [
|
|
71
|
+
now.tone && now.tone !== 'none' ? now.tone : '',
|
|
72
|
+
now.bloom ? `bloom ${now.bloom.toFixed(2)}` : '',
|
|
73
|
+
client.picture.pipeline ? 'pipeline on' : 'no pipeline'
|
|
74
|
+
]
|
|
75
|
+
return parts.filter(Boolean).join(' · ')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---- the whole pipeline's choices, as the first example keeps them
|
|
79
|
+
|
|
80
|
+
const pipeline = {
|
|
81
|
+
focus: false,
|
|
82
|
+
aperture: 0.2,
|
|
83
|
+
grain: 0,
|
|
84
|
+
aberration: 0
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The lens as the pipeline example's controls have it */
|
|
88
|
+
const pipelineLens = (client: GameClientApi) => {
|
|
89
|
+
const target = knight(client)
|
|
90
|
+
client.picture.cinematic = {
|
|
91
|
+
focus: pipeline.focus && target ? { at: { entity: target.id }, aperture: pipeline.aperture } : null,
|
|
92
|
+
grain: pipeline.grain,
|
|
93
|
+
aberration: pipeline.aberration
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ---- the looks: each a picture, a lens, and passes of its own
|
|
98
|
+
|
|
99
|
+
interface Preset {
|
|
100
|
+
look: MapPictureLook
|
|
101
|
+
lens?: CinematicPicture
|
|
102
|
+
/** Bars of a film's ratio over the picture */
|
|
103
|
+
bars?: number
|
|
104
|
+
/** A pass of ours, and how strong */
|
|
105
|
+
shader?: [ShaderName, number]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const PRESETS: Record<string, Preset> = {
|
|
109
|
+
'As drawn': { look: {} },
|
|
110
|
+
Cinematic: {
|
|
111
|
+
look: {
|
|
112
|
+
tone: 'aces',
|
|
113
|
+
exposure: 1.15,
|
|
114
|
+
contrast: 1.15,
|
|
115
|
+
saturation: 1.1,
|
|
116
|
+
tint: [1, 0.85, 0.7],
|
|
117
|
+
tintAmount: 0.12,
|
|
118
|
+
bloom: 0.45,
|
|
119
|
+
bloomThreshold: 0.8,
|
|
120
|
+
vignette: 0.45,
|
|
121
|
+
sharpen: 0.2
|
|
122
|
+
},
|
|
123
|
+
lens: { grain: 0.12 },
|
|
124
|
+
bars: 2.39
|
|
125
|
+
},
|
|
126
|
+
Noir: {
|
|
127
|
+
look: { tone: 'standard', saturation: 0, contrast: 1.6, exposure: 1.05, vignette: 0.9, sharpen: 0.4 },
|
|
128
|
+
lens: { grain: 0.5 },
|
|
129
|
+
bars: 1.85
|
|
130
|
+
},
|
|
131
|
+
Dreamy: {
|
|
132
|
+
look: {
|
|
133
|
+
tone: 'aces',
|
|
134
|
+
exposure: 1.35,
|
|
135
|
+
contrast: 0.85,
|
|
136
|
+
saturation: 1.15,
|
|
137
|
+
tint: [1, 0.75, 0.9],
|
|
138
|
+
tintAmount: 0.18,
|
|
139
|
+
bloom: 1,
|
|
140
|
+
bloomThreshold: 0.45,
|
|
141
|
+
bloomSpread: 1.6,
|
|
142
|
+
vignette: 0.3,
|
|
143
|
+
vignetteColor: [0.35, 0.2, 0.4]
|
|
144
|
+
},
|
|
145
|
+
lens: { aberration: 0.2 }
|
|
146
|
+
},
|
|
147
|
+
'Retro pixel': {
|
|
148
|
+
look: { tone: 'standard', saturation: 1.35, contrast: 1.2 },
|
|
149
|
+
shader: ['Pixels', 0.6]
|
|
150
|
+
},
|
|
151
|
+
Sepia: {
|
|
152
|
+
look: {
|
|
153
|
+
tone: 'standard',
|
|
154
|
+
saturation: 0,
|
|
155
|
+
tint: [1, 0.72, 0.42],
|
|
156
|
+
tintAmount: 0.45,
|
|
157
|
+
contrast: 1.1,
|
|
158
|
+
vignette: 0.7,
|
|
159
|
+
vignetteColor: [0.2, 0.12, 0.05]
|
|
160
|
+
},
|
|
161
|
+
lens: { grain: 0.35 }
|
|
162
|
+
},
|
|
163
|
+
'Cold thriller': {
|
|
164
|
+
look: { tone: 'aces', saturation: 0.6, contrast: 1.3, tint: [0.55, 0.8, 1], tintAmount: 0.3, exposure: 0.95, vignette: 0.6, sharpen: 0.3 },
|
|
165
|
+
bars: 2
|
|
166
|
+
},
|
|
167
|
+
'Old CRT': {
|
|
168
|
+
look: { tone: 'standard', saturation: 1.2, bloom: 0.5, bloomThreshold: 0.6 },
|
|
169
|
+
shader: ['CRT', 1]
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** What a preset put on: its pass, to take off again as another is picked */
|
|
174
|
+
const presetNow: { shader?: OwnPass } = {}
|
|
175
|
+
|
|
176
|
+
function showPreset(client: GameClientApi, name: string, base: MapPictureLook): void {
|
|
177
|
+
const preset = PRESETS[name] ?? PRESETS['As drawn']
|
|
178
|
+
client.picture.look = { ...base, ...preset.look }
|
|
179
|
+
client.picture.cinematic = preset.lens ?? null
|
|
180
|
+
client.screenPass.setFrame(preset.bars ?? false, 0.6)
|
|
181
|
+
presetNow.shader?.pass.remove()
|
|
182
|
+
presetNow.shader = preset.shader ? addShader(client, preset.shader[0], preset.shader[1]) : undefined
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ---- the bloom example's
|
|
186
|
+
|
|
187
|
+
/** The bloom example's start: its bloom, and torches bright enough for it to bleed from */
|
|
188
|
+
const BLOOM = { bloom: 2, torches: 2.5 }
|
|
189
|
+
/** The bloom the slider has, what the toggle puts back */
|
|
190
|
+
const bloomNow = { amount: BLOOM.bloom }
|
|
191
|
+
|
|
192
|
+
// ---- the depth of field example's
|
|
193
|
+
|
|
194
|
+
/** The depth of field example's aperture at first: wide open, the blur plain a few cells before and behind the focus */
|
|
195
|
+
const APERTURE = 0.1
|
|
196
|
+
|
|
197
|
+
/** The lens' aperture now, the example's own where none is set */
|
|
198
|
+
const aperture = (client: GameClientApi) => client.picture.cinematic?.focus?.aperture ?? APERTURE
|
|
199
|
+
|
|
200
|
+
// ---- the anti-aliasing example's
|
|
201
|
+
|
|
202
|
+
/** Low by the trees west of the square, their trunks and branches thin against the ground */
|
|
203
|
+
const nearTrees = (client: GameClientApi) => stand(client, { x: 24, z: 22 }, 12, 160, 24)
|
|
204
|
+
|
|
205
|
+
// ---- the outlines example's
|
|
206
|
+
|
|
207
|
+
const outlines = { color: [1, 0.85, 0.35] as [number, number, number], width: 2, pulse: false, style: 'line' as 'line' | 'glow' }
|
|
208
|
+
const OUTLINE_COLORS: Record<string, [number, number, number]> = {
|
|
209
|
+
Gold: [1, 0.85, 0.35],
|
|
210
|
+
White: [1, 1, 1],
|
|
211
|
+
Cyan: [0.3, 0.9, 1],
|
|
212
|
+
Green: [0.4, 1, 0.45],
|
|
213
|
+
Black: [0, 0, 0]
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export const category: CategoryLooks = {
|
|
217
|
+
looks: {
|
|
218
|
+
'post-pipeline': {
|
|
219
|
+
ready: (client) => {
|
|
220
|
+
const undo = keep(client)
|
|
221
|
+
Object.assign(pipeline, { focus: false, aperture: 0.2, grain: 0, aberration: 0 })
|
|
222
|
+
client.picture.look = { tone: 'aces', exposure: 1.1, contrast: 1.1, saturation: 1.1, bloom: 0.4, bloomThreshold: 0.8, vignette: 0.3 }
|
|
223
|
+
// the edges as the card says at first
|
|
224
|
+
client.picture.antialias = 'msaa'
|
|
225
|
+
onSquare(client, 26, 32)
|
|
226
|
+
return undo
|
|
227
|
+
},
|
|
228
|
+
controls: [
|
|
229
|
+
select(
|
|
230
|
+
'Tone',
|
|
231
|
+
['none', 'standard', 'aces', 'neutral'],
|
|
232
|
+
'aces',
|
|
233
|
+
(client, tone) => look(client, { tone: tone as MapPictureLook['tone'] }),
|
|
234
|
+
'How light brighter than the screen shows is brought into range: none cuts it off flat white, ACES rolls it off softly.'
|
|
235
|
+
),
|
|
236
|
+
slider(
|
|
237
|
+
'Exposure',
|
|
238
|
+
{ min: 0.3, max: 2.5, step: 0.05, value: 1.1 },
|
|
239
|
+
(client, value) => look(client, { exposure: value }),
|
|
240
|
+
'How much light is let in, as a camera’s: the whole picture darker or brighter.'
|
|
241
|
+
),
|
|
242
|
+
slider(
|
|
243
|
+
'Contrast',
|
|
244
|
+
{ min: 0.5, max: 2, step: 0.05, value: 1.1 },
|
|
245
|
+
(client, value) => look(client, { contrast: value }),
|
|
246
|
+
'How far the darks and the lights are pulled apart: watch the shadows under the units deepen.'
|
|
247
|
+
),
|
|
248
|
+
slider(
|
|
249
|
+
'Saturation',
|
|
250
|
+
{ min: 0, max: 2, step: 0.05, value: 1.1 },
|
|
251
|
+
(client, value) => look(client, { saturation: value }),
|
|
252
|
+
'How strong the colours are: 0 is black and white, 2 makes the grass and the roofs loud.'
|
|
253
|
+
),
|
|
254
|
+
slider(
|
|
255
|
+
'Bloom',
|
|
256
|
+
{ min: 0, max: 3, step: 0.05, value: 0.4 },
|
|
257
|
+
(client, value) => look(client, { bloom: value }),
|
|
258
|
+
'How much light bleeds out round what is bright: the sunlit path and the fire haze over.'
|
|
259
|
+
),
|
|
260
|
+
slider(
|
|
261
|
+
'Bloom from',
|
|
262
|
+
{ min: 0, max: 1, step: 0.05, value: 0.8 },
|
|
263
|
+
(client, value) => look(client, { bloomThreshold: value }),
|
|
264
|
+
'How bright a pixel must be before it blooms: lower it and the grass glows too, not only the path.'
|
|
265
|
+
),
|
|
266
|
+
slider(
|
|
267
|
+
'Vignette',
|
|
268
|
+
{ min: 0, max: 1, step: 0.05, value: 0.3 },
|
|
269
|
+
(client, value) => look(client, { vignette: value }),
|
|
270
|
+
'How much the corners of the picture are darkened.'
|
|
271
|
+
),
|
|
272
|
+
slider(
|
|
273
|
+
'Sharpen',
|
|
274
|
+
{ min: 0, max: 3, step: 0.05, value: 0 },
|
|
275
|
+
(client, value) => look(client, { sharpen: value }),
|
|
276
|
+
'How much edges are made crisper: look at the grass blades and the stones of the path.'
|
|
277
|
+
),
|
|
278
|
+
slider(
|
|
279
|
+
'Grain',
|
|
280
|
+
{ min: 0, max: 1, step: 0.05, value: 0 },
|
|
281
|
+
(client, value) => {
|
|
282
|
+
pipeline.grain = value
|
|
283
|
+
pipelineLens(client)
|
|
284
|
+
},
|
|
285
|
+
'Film grain over the picture, moving every frame: plainest on the flat ground.'
|
|
286
|
+
),
|
|
287
|
+
slider(
|
|
288
|
+
'Colour split',
|
|
289
|
+
{ min: 0, max: 1, step: 0.05, value: 0 },
|
|
290
|
+
(client, value) => {
|
|
291
|
+
pipeline.aberration = value
|
|
292
|
+
pipelineLens(client)
|
|
293
|
+
},
|
|
294
|
+
'Red, green and blue pulled apart towards the edges, as a cheap lens does: look at the corners.'
|
|
295
|
+
),
|
|
296
|
+
toggle(
|
|
297
|
+
'Depth of field (on the knight)',
|
|
298
|
+
false,
|
|
299
|
+
(client, on) => {
|
|
300
|
+
pipeline.focus = on
|
|
301
|
+
pipelineLens(client)
|
|
302
|
+
},
|
|
303
|
+
'The lens focused on the knight: the woods behind him and the ground before him blur.'
|
|
304
|
+
),
|
|
305
|
+
slider(
|
|
306
|
+
'Aperture (f-stop)',
|
|
307
|
+
{ min: 0.02, max: 2, step: 0.01, value: 0.2 },
|
|
308
|
+
(client, value) => {
|
|
309
|
+
pipeline.aperture = value
|
|
310
|
+
pipelineLens(client)
|
|
311
|
+
},
|
|
312
|
+
'With depth of field on: smaller is a wider lens, a thinner sharp band and more blur.'
|
|
313
|
+
),
|
|
314
|
+
toggle(
|
|
315
|
+
'Ambient occlusion',
|
|
316
|
+
false,
|
|
317
|
+
(client, on) => {
|
|
318
|
+
client.picture.occlusion = on ? 'high' : 'off'
|
|
319
|
+
look(client, { occlusion: on ? 1 : 0, occlusionRadius: 2 })
|
|
320
|
+
},
|
|
321
|
+
'Corners, creases and the feet of what stands darken where light from all round reaches less.'
|
|
322
|
+
),
|
|
323
|
+
select(
|
|
324
|
+
'Anti-aliasing',
|
|
325
|
+
['msaa', 'fxaa', 'taa', 'off'],
|
|
326
|
+
'msaa',
|
|
327
|
+
(client, mode) => (client.picture.antialias = mode as typeof client.picture.antialias),
|
|
328
|
+
'How jagged edges are smoothed: off shows the stairs on the roofs and the grass, TAA smooths the most.'
|
|
329
|
+
),
|
|
330
|
+
readout('Picture', lookNow, 'What the picture has on now: its tone, its bloom, whether a pipeline is made.')
|
|
331
|
+
]
|
|
332
|
+
},
|
|
333
|
+
'post-bloom': {
|
|
334
|
+
ready: (client) => {
|
|
335
|
+
const undo = keep(client)
|
|
336
|
+
client.picture.look = { tone: 'aces', exposure: 1.1, bloom: BLOOM.bloom, bloomThreshold: 0.5, bloomSpread: 0.8 }
|
|
337
|
+
bloomNow.amount = BLOOM.bloom
|
|
338
|
+
// torches bright enough to light the square: a bloom needs a bright light to bleed from
|
|
339
|
+
client.setEnvironment({ lamps: { brightness: BLOOM.torches } })
|
|
340
|
+
onSquare(client, 20, 26)
|
|
341
|
+
return undo
|
|
342
|
+
},
|
|
343
|
+
controls: [
|
|
344
|
+
slider(
|
|
345
|
+
'Bloom',
|
|
346
|
+
{ min: 0, max: 4, step: 0.05, value: BLOOM.bloom },
|
|
347
|
+
(client, value) => {
|
|
348
|
+
bloomNow.amount = value
|
|
349
|
+
look(client, { bloom: value })
|
|
350
|
+
},
|
|
351
|
+
'How much light bleeds out round what is bright: the haze round the torches and the fire.'
|
|
352
|
+
),
|
|
353
|
+
slider(
|
|
354
|
+
'Spreads',
|
|
355
|
+
{ min: 0.2, max: 2, step: 0.05, value: 0.8 },
|
|
356
|
+
(client, value) => look(client, { bloomSpread: value }),
|
|
357
|
+
'How far the bloom spreads: low keeps a tight halo round each light, high washes the square.'
|
|
358
|
+
),
|
|
359
|
+
slider(
|
|
360
|
+
'Blooms from (brightness)',
|
|
361
|
+
{ min: 0.2, max: 1, step: 0.05, value: 0.5 },
|
|
362
|
+
(client, value) => look(client, { bloomThreshold: value }),
|
|
363
|
+
'How bright a pixel must be before it glows: lower it and the lit ground blooms too, not only the torches.'
|
|
364
|
+
),
|
|
365
|
+
slider(
|
|
366
|
+
'Exposure',
|
|
367
|
+
{ min: 0.3, max: 3, step: 0.05, value: 1.1 },
|
|
368
|
+
(client, value) => look(client, { exposure: value }),
|
|
369
|
+
'How much light is let in: brighter, more of the picture passes the bloom’s brightness and glows.'
|
|
370
|
+
),
|
|
371
|
+
slider(
|
|
372
|
+
'Torches',
|
|
373
|
+
{ min: 0, max: 5, step: 0.05, value: BLOOM.torches },
|
|
374
|
+
(client, value) => client.setEnvironment({ lamps: { brightness: value } }),
|
|
375
|
+
'How bright the torches burn: at 0 the square goes dark and nothing is bright enough to bloom.'
|
|
376
|
+
),
|
|
377
|
+
toggle(
|
|
378
|
+
'Bloom on',
|
|
379
|
+
true,
|
|
380
|
+
(client, on) => look(client, { bloom: on ? bloomNow.amount : 0 }),
|
|
381
|
+
'The bloom on or off at the strength the slider has: compare the torches with and without it.'
|
|
382
|
+
)
|
|
383
|
+
]
|
|
384
|
+
},
|
|
385
|
+
'post-tone': {
|
|
386
|
+
ready: (client) => {
|
|
387
|
+
const undo = keep(client)
|
|
388
|
+
client.picture.look = { tone: 'none', exposure: 2.2, bloom: 0.3, bloomThreshold: 0.85 }
|
|
389
|
+
stand(client, { x: 32, z: 36 }, 40, 170, 30)
|
|
390
|
+
return undo
|
|
391
|
+
},
|
|
392
|
+
controls: [
|
|
393
|
+
select(
|
|
394
|
+
'Tone mapping',
|
|
395
|
+
['none', 'standard', 'aces', 'neutral'],
|
|
396
|
+
'none',
|
|
397
|
+
(client, tone) => look(client, { tone: tone as MapPictureLook['tone'] }),
|
|
398
|
+
'How light brighter than white is brought into range: none cuts the lake off flat white, the others keep its colour.'
|
|
399
|
+
),
|
|
400
|
+
slider(
|
|
401
|
+
'Contrast',
|
|
402
|
+
{ min: 0.5, max: 2, step: 0.05, value: 1 },
|
|
403
|
+
(client, value) => look(client, { contrast: value }),
|
|
404
|
+
'How far the darks and the lights are pulled apart: the shadows of the trees go deeper or greyer.'
|
|
405
|
+
),
|
|
406
|
+
slider(
|
|
407
|
+
'Exposure',
|
|
408
|
+
{ min: 0.25, max: 4, step: 0.05, value: 2.2 },
|
|
409
|
+
(client, value) => look(client, { exposure: value }),
|
|
410
|
+
'How much light is let in: high burns the lake out unless a tone mapping brings it back.'
|
|
411
|
+
),
|
|
412
|
+
readout('Picture', lookNow, 'What the picture has on now: its tone, its bloom, whether a pipeline is made.')
|
|
413
|
+
]
|
|
414
|
+
},
|
|
415
|
+
'post-color': {
|
|
416
|
+
ready: (client) => {
|
|
417
|
+
const undo = keep(client)
|
|
418
|
+
const forget = teachGrades(client)
|
|
419
|
+
client.picture.look = { tone: 'neutral' }
|
|
420
|
+
onSquare(client, 26, 34)
|
|
421
|
+
return () => {
|
|
422
|
+
undo()
|
|
423
|
+
forget()
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
controls: [
|
|
427
|
+
select(
|
|
428
|
+
'Grade (look-up table)',
|
|
429
|
+
['None', ...Object.keys(GRADES)],
|
|
430
|
+
'None',
|
|
431
|
+
(client, name) => look(client, { grade: name === 'None' ? undefined : gradePath(name), gradeAmount: 1 }),
|
|
432
|
+
'A colour grade from a look-up table made in code: every colour of the picture mapped to another.'
|
|
433
|
+
),
|
|
434
|
+
slider(
|
|
435
|
+
'Grade amount',
|
|
436
|
+
{ min: 0, max: 1, step: 0.05, value: 1 },
|
|
437
|
+
(client, value) => look(client, { gradeAmount: value }),
|
|
438
|
+
'How much of the grade is used: 0 the picture as drawn, 1 the grade in full.'
|
|
439
|
+
),
|
|
440
|
+
slider(
|
|
441
|
+
'Saturation',
|
|
442
|
+
{ min: 0, max: 2.5, step: 0.05, value: 1 },
|
|
443
|
+
(client, value) => look(client, { saturation: value }),
|
|
444
|
+
'How strong the colours are: 0 is black and white, high makes the grass and the roofs loud.'
|
|
445
|
+
),
|
|
446
|
+
slider(
|
|
447
|
+
'Contrast',
|
|
448
|
+
{ min: 0.4, max: 2.2, step: 0.05, value: 1 },
|
|
449
|
+
(client, value) => look(client, { contrast: value }),
|
|
450
|
+
'How far the darks and the lights are pulled apart: low greys the picture, high deepens the shadows.'
|
|
451
|
+
),
|
|
452
|
+
select(
|
|
453
|
+
'Tint',
|
|
454
|
+
['None', 'Warm', 'Cold', 'Green', 'Magenta'],
|
|
455
|
+
'None',
|
|
456
|
+
(client, name) => {
|
|
457
|
+
const tints: Record<string, [number, number, number]> = {
|
|
458
|
+
Warm: [1, 0.7, 0.4],
|
|
459
|
+
Cold: [0.45, 0.7, 1],
|
|
460
|
+
Green: [0.5, 1, 0.5],
|
|
461
|
+
Magenta: [1, 0.45, 0.9]
|
|
462
|
+
}
|
|
463
|
+
look(client, name === 'None' ? { tintAmount: 0 } : { tint: tints[name], tintAmount: Math.max(client.picture.look.tintAmount ?? 0, 0.3) })
|
|
464
|
+
},
|
|
465
|
+
'The colour the whole picture leans to: a warm dusk, a cold night, a sickly green.'
|
|
466
|
+
),
|
|
467
|
+
slider(
|
|
468
|
+
'Tint amount',
|
|
469
|
+
{ min: 0, max: 1, step: 0.05, value: 0.3 },
|
|
470
|
+
(client, value) => look(client, { tintAmount: value }),
|
|
471
|
+
'How far the picture leans to the tint’s colour.'
|
|
472
|
+
)
|
|
473
|
+
]
|
|
474
|
+
},
|
|
475
|
+
'post-depth-of-field': {
|
|
476
|
+
ready: (client) => {
|
|
477
|
+
const undo = keep(client)
|
|
478
|
+
onSquare(client, 18, 22)
|
|
479
|
+
// once the knight is there, the lens on him
|
|
480
|
+
const focus = setTimeout(() => {
|
|
481
|
+
const target = knight(client)
|
|
482
|
+
if (target) lens(client, { focus: { at: { entity: target.id }, aperture: APERTURE } })
|
|
483
|
+
}, 300)
|
|
484
|
+
return () => {
|
|
485
|
+
clearTimeout(focus)
|
|
486
|
+
undo()
|
|
487
|
+
client.camera.easeTo({ distance: 30, pitch: (45 * Math.PI) / 180 })
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
controls: [
|
|
491
|
+
button(
|
|
492
|
+
'Focus on the knight',
|
|
493
|
+
(client) => {
|
|
494
|
+
const target = knight(client)
|
|
495
|
+
if (target) lens(client, { focus: { at: { entity: target.id }, aperture: aperture(client) } })
|
|
496
|
+
},
|
|
497
|
+
'The lens on the knight in the middle: he is sharp, the woods behind and the ground before him blur.'
|
|
498
|
+
),
|
|
499
|
+
button(
|
|
500
|
+
'Focus near',
|
|
501
|
+
(client) => lens(client, { focus: { distance: 9, aperture: aperture(client) } }),
|
|
502
|
+
'The lens on the ground just before the camera: the cart and the stones near it sharp, the company behind them blurs.'
|
|
503
|
+
),
|
|
504
|
+
button(
|
|
505
|
+
'Focus on the far woods',
|
|
506
|
+
(client) => lens(client, { focus: { distance: 60, aperture: aperture(client) } }),
|
|
507
|
+
'The lens on the woods far behind: they come sharp, and the whole square before them blurs.'
|
|
508
|
+
),
|
|
509
|
+
slider(
|
|
510
|
+
'Aperture (f-stop)',
|
|
511
|
+
{ min: 0.05, max: 2, step: 0.01, value: APERTURE },
|
|
512
|
+
(client, value) => {
|
|
513
|
+
const focus = client.picture.cinematic?.focus
|
|
514
|
+
if (focus) lens(client, { focus: { ...focus, aperture: value } })
|
|
515
|
+
},
|
|
516
|
+
'How wide the lens opens: small is a thin band in focus and much blur, 2 almost everything sharp.'
|
|
517
|
+
),
|
|
518
|
+
button(
|
|
519
|
+
'Miniature (tilt-shift)',
|
|
520
|
+
(client) => {
|
|
521
|
+
stand(client, SQUARE, 55, 200, 55)
|
|
522
|
+
look(client, { saturation: 1.4, contrast: 1.15, exposure: 1.1, tone: 'aces' })
|
|
523
|
+
lens(client, { focus: { at: { x: SQUARE.x, z: SQUARE.z }, aperture: 0.03 } })
|
|
524
|
+
},
|
|
525
|
+
'From high up, sharp in the middle and blurred above and below, colours strong: the village looks a toy.'
|
|
526
|
+
),
|
|
527
|
+
button(
|
|
528
|
+
'Close up again',
|
|
529
|
+
(client) => {
|
|
530
|
+
onSquare(client, 18, 22)
|
|
531
|
+
look(client, { saturation: 1, contrast: 1, exposure: 1, tone: 'none' })
|
|
532
|
+
const target = knight(client)
|
|
533
|
+
if (target) lens(client, { focus: { at: { entity: target.id }, aperture: APERTURE } })
|
|
534
|
+
},
|
|
535
|
+
'Back down by the company, the lens on the knight again.'
|
|
536
|
+
),
|
|
537
|
+
toggle(
|
|
538
|
+
'Depth of field',
|
|
539
|
+
true,
|
|
540
|
+
(client, on) => {
|
|
541
|
+
const target = knight(client)
|
|
542
|
+
lens(client, { focus: on && target ? { at: { entity: target.id }, aperture: aperture(client) } : null })
|
|
543
|
+
},
|
|
544
|
+
'The lens’ blur on or off: off, everything is sharp from the nearest stone to the far woods.'
|
|
545
|
+
)
|
|
546
|
+
]
|
|
547
|
+
},
|
|
548
|
+
'post-occlusion': {
|
|
549
|
+
ready: (client) => {
|
|
550
|
+
const undo = keep(client)
|
|
551
|
+
client.picture.occlusion = 'high'
|
|
552
|
+
client.picture.look = { occlusion: 1, occlusionRadius: 2 }
|
|
553
|
+
onSquare(client, 16, 34)
|
|
554
|
+
return undo
|
|
555
|
+
},
|
|
556
|
+
controls: [
|
|
557
|
+
toggle(
|
|
558
|
+
'Ambient occlusion',
|
|
559
|
+
true,
|
|
560
|
+
(client, on) => look(client, { occlusion: on ? 1 : 0 }),
|
|
561
|
+
'Corners, creases and the feet of the units darken where light from all round reaches less.'
|
|
562
|
+
),
|
|
563
|
+
select(
|
|
564
|
+
'Quality',
|
|
565
|
+
['high', 'low'],
|
|
566
|
+
'high',
|
|
567
|
+
(client, quality) => (client.picture.occlusion = quality as 'high' | 'low'),
|
|
568
|
+
'Low draws the occlusion at half the pixels with half the samples: softer and blotchier, for less of the GPU.'
|
|
569
|
+
),
|
|
570
|
+
slider(
|
|
571
|
+
'Strength',
|
|
572
|
+
{ min: 0, max: 2, step: 0.05, value: 1 },
|
|
573
|
+
(client, value) => look(client, { occlusion: value }),
|
|
574
|
+
'How dark the occlusion makes the creases: look under the units and where the houses meet the ground.'
|
|
575
|
+
),
|
|
576
|
+
slider(
|
|
577
|
+
'Looks around (cells)',
|
|
578
|
+
{ min: 0.25, max: 6, step: 0.25, value: 2 },
|
|
579
|
+
(client, value) => look(client, { occlusionRadius: value }),
|
|
580
|
+
'How far round each point it looks for what hides the light: small darkens fine creases, large broad shade.'
|
|
581
|
+
),
|
|
582
|
+
button(
|
|
583
|
+
'Look at the houses',
|
|
584
|
+
(client) => stand(client, { x: 41, z: 24 }, 14, 200, 30),
|
|
585
|
+
'The camera on the houses, where walls meet the ground.'
|
|
586
|
+
),
|
|
587
|
+
button('Look at the square', (client) => onSquare(client, 16, 34), 'The camera back on the square and its people.')
|
|
588
|
+
]
|
|
589
|
+
},
|
|
590
|
+
'post-film': {
|
|
591
|
+
ready: (client) => {
|
|
592
|
+
const undo = keep(client)
|
|
593
|
+
client.picture.look = { tone: 'aces', vignette: 0.6, contrast: 1.1 }
|
|
594
|
+
client.picture.cinematic = { grain: 0.4, aberration: 0.4 }
|
|
595
|
+
onSquare(client, 24, 30)
|
|
596
|
+
return undo
|
|
597
|
+
},
|
|
598
|
+
controls: [
|
|
599
|
+
slider(
|
|
600
|
+
'Grain',
|
|
601
|
+
{ min: 0, max: 1.5, step: 0.05, value: 0.4 },
|
|
602
|
+
(client, value) => lens(client, { grain: value }),
|
|
603
|
+
'Film grain over the picture, moving every frame: plainest on the flat ground.'
|
|
604
|
+
),
|
|
605
|
+
slider(
|
|
606
|
+
'Colour split',
|
|
607
|
+
{ min: 0, max: 1.5, step: 0.05, value: 0.4 },
|
|
608
|
+
(client, value) => lens(client, { aberration: value }),
|
|
609
|
+
'Red, green and blue pulled apart towards the edges, as a cheap lens does: look at the corners.'
|
|
610
|
+
),
|
|
611
|
+
slider(
|
|
612
|
+
'Vignette',
|
|
613
|
+
{ min: 0, max: 1.5, step: 0.05, value: 0.6 },
|
|
614
|
+
(client, value) => look(client, { vignette: value }),
|
|
615
|
+
'How much the corners of the picture are darkened.'
|
|
616
|
+
),
|
|
617
|
+
select(
|
|
618
|
+
'Vignette colour',
|
|
619
|
+
['Black', 'Brown', 'Red', 'Blue'],
|
|
620
|
+
'Black',
|
|
621
|
+
(client, name) => {
|
|
622
|
+
const colors: Record<string, [number, number, number]> = {
|
|
623
|
+
Black: [0, 0, 0],
|
|
624
|
+
Brown: [0.25, 0.14, 0.05],
|
|
625
|
+
Red: [0.5, 0, 0],
|
|
626
|
+
Blue: [0, 0.05, 0.3]
|
|
627
|
+
}
|
|
628
|
+
look(client, { vignetteColor: colors[name] })
|
|
629
|
+
},
|
|
630
|
+
'The colour the corners darken to: red for a hurt hero, blue for a cold night.'
|
|
631
|
+
),
|
|
632
|
+
toggle(
|
|
633
|
+
'Widescreen bars',
|
|
634
|
+
false,
|
|
635
|
+
(client, on) => client.screenPass.setFrame(on ? 2.39 : false, 0.6),
|
|
636
|
+
'Black bars above and below, as a film’s wide picture: they slide in and out.'
|
|
637
|
+
),
|
|
638
|
+
button(
|
|
639
|
+
'Old film',
|
|
640
|
+
(client) => {
|
|
641
|
+
look(client, { saturation: 0.35, tint: [1, 0.8, 0.55], tintAmount: 0.3, vignette: 1 })
|
|
642
|
+
lens(client, { grain: 1, aberration: 0.15 })
|
|
643
|
+
},
|
|
644
|
+
'Faded warm colours, heavy grain and dark corners, all at once.'
|
|
645
|
+
),
|
|
646
|
+
button(
|
|
647
|
+
'Cheap lens',
|
|
648
|
+
(client) => {
|
|
649
|
+
look(client, { saturation: 1.1, tintAmount: 0, vignette: 0.5 })
|
|
650
|
+
lens(client, { grain: 0.1, aberration: 1.3 })
|
|
651
|
+
},
|
|
652
|
+
'The colours split hard at the edges, little grain.'
|
|
653
|
+
)
|
|
654
|
+
]
|
|
655
|
+
},
|
|
656
|
+
'post-outline': {
|
|
657
|
+
ready: (client) => {
|
|
658
|
+
const undo = keep(client)
|
|
659
|
+
const inked = client.inking
|
|
660
|
+
Object.assign(outlines, { color: OUTLINE_COLORS.Gold, width: 3, pulse: false, style: 'line' })
|
|
661
|
+
onSquare(client, 18, 32)
|
|
662
|
+
const handles: { ours: ReturnType<GameClientApi['outline']>[]; theirs: ReturnType<GameClientApi['outline']>[] } = { ours: [], theirs: [] }
|
|
663
|
+
// once they are there: ours in the look of the controls, theirs red and seen through the houses
|
|
664
|
+
const put = setTimeout(() => {
|
|
665
|
+
handles.ours = people(client, PLAYER).map((each) => client.outline(each.id, { ...outlines }))
|
|
666
|
+
handles.theirs = people(client, 0).map((each) =>
|
|
667
|
+
client.outline(each.id, { color: [1, 0.2, 0.15], width: 3, throughWalls: true, style: 'glow', pulse: 1.2 })
|
|
668
|
+
)
|
|
669
|
+
}, 300)
|
|
670
|
+
outlinesNow.set = (change) => {
|
|
671
|
+
Object.assign(outlines, change)
|
|
672
|
+
for (const handle of handles.ours) handle.set({ ...outlines, pulse: outlines.pulse ? 1.2 : undefined })
|
|
673
|
+
}
|
|
674
|
+
outlinesNow.theirs = (on) => {
|
|
675
|
+
for (const handle of handles.theirs) handle.remove()
|
|
676
|
+
handles.theirs = on
|
|
677
|
+
? people(client, 0).map((each) =>
|
|
678
|
+
client.outline(each.id, { color: [1, 0.2, 0.15], width: 3, throughWalls: true, style: 'glow', pulse: 1.2 })
|
|
679
|
+
)
|
|
680
|
+
: []
|
|
681
|
+
}
|
|
682
|
+
return () => {
|
|
683
|
+
clearTimeout(put)
|
|
684
|
+
for (const handle of [...handles.ours, ...handles.theirs]) handle.remove()
|
|
685
|
+
client.inking = inked
|
|
686
|
+
undo()
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
controls: [
|
|
690
|
+
select(
|
|
691
|
+
'Colour',
|
|
692
|
+
Object.keys(OUTLINE_COLORS),
|
|
693
|
+
'Gold',
|
|
694
|
+
(_client, name) => outlinesNow.set({ color: OUTLINE_COLORS[name] }),
|
|
695
|
+
'The colour of the line round your own company.'
|
|
696
|
+
),
|
|
697
|
+
slider(
|
|
698
|
+
'Width (pixels)',
|
|
699
|
+
{ min: 1, max: 8, step: 0.5, value: 3 },
|
|
700
|
+
(_client, value) => outlinesNow.set({ width: value }),
|
|
701
|
+
'How thick the line round your company is, in pixels of the screen, however near or far they stand.'
|
|
702
|
+
),
|
|
703
|
+
select(
|
|
704
|
+
'Style',
|
|
705
|
+
['line', 'glow'],
|
|
706
|
+
'line',
|
|
707
|
+
(_client, style) => outlinesNow.set({ style: style as 'line' | 'glow' }),
|
|
708
|
+
'A crisp line on the edge of each unit, or a soft glow round it.'
|
|
709
|
+
),
|
|
710
|
+
toggle(
|
|
711
|
+
'Pulse',
|
|
712
|
+
false,
|
|
713
|
+
(_client, on) => outlinesNow.set({ pulse: on }),
|
|
714
|
+
'The line round your company breathes, brighter and fainter about once a second.'
|
|
715
|
+
),
|
|
716
|
+
toggle(
|
|
717
|
+
'Strangers through walls',
|
|
718
|
+
true,
|
|
719
|
+
(_client, on) => outlinesNow.theirs(on),
|
|
720
|
+
'The strangers by the well glow red, seen even where a house stands between them and the camera.'
|
|
721
|
+
),
|
|
722
|
+
toggle(
|
|
723
|
+
'Outline what the pointer is over',
|
|
724
|
+
true,
|
|
725
|
+
(client, on) => (client.hoverOutline = on),
|
|
726
|
+
'A line round whatever unit the pointer is over: move it across the square to see.'
|
|
727
|
+
),
|
|
728
|
+
toggle(
|
|
729
|
+
'Ink',
|
|
730
|
+
false,
|
|
731
|
+
(client, on) => (client.inking = on),
|
|
732
|
+
'A thin dark line round every unit’s model, as a drawing’s ink: the game’s own look for units, not a highlight.'
|
|
733
|
+
)
|
|
734
|
+
]
|
|
735
|
+
},
|
|
736
|
+
'post-looks': {
|
|
737
|
+
ready: (client) => {
|
|
738
|
+
const undo = keep(client)
|
|
739
|
+
const base = client.picture.look
|
|
740
|
+
looksBase.look = base
|
|
741
|
+
showPreset(client, 'Cinematic', base)
|
|
742
|
+
onSquare(client, 24, 26)
|
|
743
|
+
return () => {
|
|
744
|
+
presetNow.shader?.pass.remove()
|
|
745
|
+
presetNow.shader = undefined
|
|
746
|
+
client.screenPass.setFrame(false, 0)
|
|
747
|
+
undo()
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
controls: [
|
|
751
|
+
select(
|
|
752
|
+
'Look',
|
|
753
|
+
Object.keys(PRESETS),
|
|
754
|
+
'Cinematic',
|
|
755
|
+
(client, name) => showPreset(client, name, looksBase.look),
|
|
756
|
+
'A whole look made of the passes together: its tone, colours, bloom, grain, bars and a pass of its own.'
|
|
757
|
+
),
|
|
758
|
+
button('Near', (client) => onSquare(client, 16, 22), 'The camera close on the company.'),
|
|
759
|
+
button('Far', (client) => onSquare(client, 40, 42), 'The camera far back over the whole village.'),
|
|
760
|
+
readout('Picture', lookNow, 'What the picture has on now: its tone, its bloom, whether a pipeline is made.')
|
|
761
|
+
]
|
|
762
|
+
},
|
|
763
|
+
'post-custom': {
|
|
764
|
+
ready: (client) => {
|
|
765
|
+
const undo = keep(client)
|
|
766
|
+
custom.amount = 1
|
|
767
|
+
custom.on = true
|
|
768
|
+
custom.pass = addShader(client, 'Sketch', 1)
|
|
769
|
+
onSquare(client, 24, 30)
|
|
770
|
+
return () => {
|
|
771
|
+
custom.pass?.pass.remove()
|
|
772
|
+
custom.pass = undefined
|
|
773
|
+
undo()
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
controls: [
|
|
777
|
+
select(
|
|
778
|
+
'Pass',
|
|
779
|
+
['None', ...Object.keys(SHADERS)],
|
|
780
|
+
'Sketch',
|
|
781
|
+
(client, name) => {
|
|
782
|
+
custom.pass?.pass.remove()
|
|
783
|
+
custom.pass = name === 'None' ? undefined : addShader(client, name as ShaderName, custom.amount)
|
|
784
|
+
if (custom.pass) custom.pass.pass.enabled = custom.on
|
|
785
|
+
},
|
|
786
|
+
'Which shader of ours is drawn over the finished picture: each is a few lines of GLSL and WGSL in passes.ts.'
|
|
787
|
+
),
|
|
788
|
+
slider(
|
|
789
|
+
'Strength',
|
|
790
|
+
{ min: 0.4, max: 1, step: 0.05, value: 1 },
|
|
791
|
+
(_client, value) => {
|
|
792
|
+
custom.amount = value
|
|
793
|
+
if (custom.pass) custom.pass.amount = value
|
|
794
|
+
},
|
|
795
|
+
'How strongly the pass is drawn: a uniform of its shader, set every frame.'
|
|
796
|
+
),
|
|
797
|
+
toggle(
|
|
798
|
+
'On',
|
|
799
|
+
true,
|
|
800
|
+
(_client, on) => {
|
|
801
|
+
custom.on = on
|
|
802
|
+
if (custom.pass) custom.pass.pass.enabled = on
|
|
803
|
+
},
|
|
804
|
+
'The pass taken off the camera and put back (its `enabled`), without making it again.'
|
|
805
|
+
),
|
|
806
|
+
readout(
|
|
807
|
+
'Passes of ours',
|
|
808
|
+
(client) =>
|
|
809
|
+
client.picture.passes.map((each) => `${each.name}${each.drawn ? '' : each.enabled ? ' (waiting)' : ' (off)'}`).join(', ') || 'none',
|
|
810
|
+
'The passes of ours on the picture, and whether each is drawn yet (its shader made) or off.'
|
|
811
|
+
)
|
|
812
|
+
]
|
|
813
|
+
},
|
|
814
|
+
'post-antialias': {
|
|
815
|
+
ready: (client) => {
|
|
816
|
+
const undo = keep(client)
|
|
817
|
+
// the edges as the card says at first
|
|
818
|
+
client.picture.antialias = 'msaa'
|
|
819
|
+
nearTrees(client)
|
|
820
|
+
return undo
|
|
821
|
+
},
|
|
822
|
+
controls: [
|
|
823
|
+
select(
|
|
824
|
+
'Anti-aliasing',
|
|
825
|
+
['msaa', 'fxaa', 'off', 'taa'],
|
|
826
|
+
'msaa',
|
|
827
|
+
(client, mode) => (client.picture.antialias = mode as typeof client.picture.antialias),
|
|
828
|
+
'How jagged edges are smoothed: off shows stairs along the roof’s beams and the branches, TAA smooths even the grass.'
|
|
829
|
+
),
|
|
830
|
+
slider(
|
|
831
|
+
'Resolution',
|
|
832
|
+
{ min: 0.25, max: 1, step: 0.05, value: 1 },
|
|
833
|
+
(client, value) => (client.renderScale = value),
|
|
834
|
+
'How many of the screen’s pixels the scene is drawn with: at a quarter it goes soft and blocky, the card stays sharp.'
|
|
835
|
+
),
|
|
836
|
+
slider(
|
|
837
|
+
'Sharpen',
|
|
838
|
+
{ min: 0, max: 3, step: 0.05, value: 0 },
|
|
839
|
+
(client, value) => look(client, { sharpen: value }),
|
|
840
|
+
'How much edges are made crisper after the picture is drawn: it wins back some of a low resolution.'
|
|
841
|
+
),
|
|
842
|
+
button('Near the trees', nearTrees, 'The camera low by the trees, their thin trunks and branches against the ground.'),
|
|
843
|
+
button('Far', (client) => onSquare(client, 45, 40), 'The camera far back: small things are where edges shimmer most.'),
|
|
844
|
+
readout(
|
|
845
|
+
'Drawn',
|
|
846
|
+
(client) => `${client.picture.antialias}, ${Math.round(client.renderScale * 100)}% resolution, ${client.picture.samples} samples`,
|
|
847
|
+
'How the scene is drawn now: the smoothing, the share of the screen’s pixels, the GPU’s samples of each.'
|
|
848
|
+
)
|
|
849
|
+
]
|
|
850
|
+
},
|
|
851
|
+
'post-zones': {
|
|
852
|
+
ready: (client) => {
|
|
853
|
+
const undo = keep(client)
|
|
854
|
+
onSquare(client, 30, 34)
|
|
855
|
+
return undo
|
|
856
|
+
},
|
|
857
|
+
controls: [
|
|
858
|
+
button(
|
|
859
|
+
'Into the cursed wood',
|
|
860
|
+
(client) => client.camera.panTo({ x: 11, z: 32 }, 3),
|
|
861
|
+
'The camera glides west into the cursed wood: the picture turns green, dark and misty as it goes in.'
|
|
862
|
+
),
|
|
863
|
+
button('Back to the square', (client) => client.camera.panTo(SQUARE, 3), 'The camera glides back, and the picture to the village’s own.'),
|
|
864
|
+
readout(
|
|
865
|
+
'Picture',
|
|
866
|
+
(client) => {
|
|
867
|
+
const now = client.picture.now
|
|
868
|
+
return `saturation ${(now.saturation ?? 1).toFixed(2)}, tint ${(now.tintAmount ?? 0).toFixed(2)}`
|
|
869
|
+
},
|
|
870
|
+
'The picture’s saturation and tint now: they ease between the two looks as the camera moves.'
|
|
871
|
+
)
|
|
872
|
+
]
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/** The looks example's picture before any look: each laid over it */
|
|
878
|
+
const looksBase: { look: MapPictureLook } = { look: {} }
|
|
879
|
+
|
|
880
|
+
/** The outlines example's: what the controls change, set as it is ready */
|
|
881
|
+
const outlinesNow: { set: (change: Partial<typeof outlines>) => void; theirs: (on: boolean) => void } = { set: () => {}, theirs: () => {} }
|
|
882
|
+
|
|
883
|
+
/** The custom pass example's pass on now, and its strength */
|
|
884
|
+
const custom: { pass?: OwnPass; amount: number; on: boolean } = { amount: 1, on: true }
|