@homie-rocks/studio 0.30.2 → 0.31.1
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/CHANGELOG.md +108 -1
- package/README.md +349 -23
- package/assets/animate.ts +16 -6
- package/assets/assets.ts +14 -3
- package/assets/render-page.ts +10 -2
- package/assets/safety.mjs +7 -1
- package/bin/homie-studio.mjs +204 -124
- package/lib/art-cli.mjs +61 -18
- package/lib/art-tools.mjs +53 -10
- package/lib/asset-check.mjs +249 -46
- package/lib/asset-import.mjs +11 -5
- package/lib/asset-manifest.mjs +128 -51
- package/lib/build.mjs +239 -72
- package/lib/characters.mjs +119 -20
- package/lib/check.mjs +110 -7
- package/lib/chrome.mjs +11 -0
- package/lib/clips.mjs +54 -0
- package/lib/cloudflare.mjs +203 -10
- package/lib/codex.mjs +6 -0
- package/lib/collision.mjs +245 -0
- package/lib/decisions.mjs +2 -2
- package/lib/deploy-state.mjs +166 -0
- package/lib/dev.mjs +430 -0
- package/lib/directory.mjs +172 -9
- package/lib/doctor.mjs +12 -11
- package/lib/handoff.mjs +2 -6
- package/lib/library.mjs +1 -1
- package/lib/mcp-tools.mjs +42 -37
- package/lib/mcp.mjs +1 -1
- package/lib/net.mjs +81 -2
- package/lib/node-version.mjs +40 -0
- package/lib/office.mjs +6 -6
- package/lib/optimise.mjs +39 -4
- package/lib/parts-build.mjs +169 -0
- package/lib/parts-cli.mjs +104 -0
- package/lib/parts-store.mjs +342 -0
- package/lib/parts-tools.mjs +92 -0
- package/lib/parts.mjs +720 -0
- package/lib/perf.mjs +131 -31
- package/lib/port-check.mjs +40 -6
- package/lib/preview.mjs +77 -0
- package/lib/routes.mjs +240 -0
- package/lib/scaffold.mjs +83 -22
- package/lib/shoot.mjs +307 -0
- package/lib/site.mjs +1 -2
- package/lib/stage.mjs +118 -0
- package/lib/studio.mjs +0 -66
- package/lib/style-board.mjs +49 -6
- package/lib/template-history.json +101 -0
- package/lib/trailer.mjs +47 -0
- package/lib/typecheck.mjs +107 -0
- package/mcp/ui/lineup.js +3 -3
- package/mcp/ui/rights.js +1 -3
- package/netplay/NETPLAY.md +552 -16
- package/netplay/netplay.ts +950 -43
- package/package.json +10 -1
- package/parts/PARTS.md +169 -0
- package/port/index.ts +8 -2
- package/port/personas.ts +275 -0
- package/port/powerups.ts +357 -0
- package/port/probe.ts +38 -2
- package/port/room.ts +329 -40
- package/site/SITE.md +102 -20
- package/starters/ember-vale/index.html +1 -1
- package/starters/ember-vale/src/main.ts +7 -1
- package/starters/gem-rush/index.html +1 -1
- package/starters/gem-rush/src/main.ts +41 -12
- package/starters/gem-rush-3d/assets/RIGHTS.md +0 -24
- package/starters/gem-rush-3d/assets/manifest.json +0 -24
- package/starters/gem-rush-3d/index.html +1 -1
- package/starters/gem-rush-3d/src/main.ts +50 -11
- package/starters/hero-rush-3d/assets/RIGHTS.md +0 -19
- package/starters/hero-rush-3d/assets/manifest.json +0 -19
- package/starters/hero-rush-3d/index.html +1 -1
- package/starters/hero-rush-3d/src/main.ts +50 -11
- package/worker/arrival.mjs +4 -1
- package/worker/discover.mjs +11 -25
- package/worker/index.mjs +142 -62
- package/worker/license.mjs +45 -41
- package/worker/office-page.mjs +0 -4
- package/worker/office-schema.mjs +6 -0
- package/worker/office.mjs +19 -23
- package/worker/pages.mjs +156 -29
- package/worker/parts.mjs +275 -0
- package/worker/room.mjs +157 -14
- package/worker/schema.mjs +6 -8
- package/worker/seats.mjs +71 -0
- package/worker/site.mjs +51 -53
- package/worker/version.mjs +1 -1
- package/lib/remix-assets.mjs +0 -84
package/site/SITE.md
CHANGED
|
@@ -17,14 +17,21 @@ and every game gets a landing page. Anything the studio puts in `site/` wins.
|
|
|
17
17
|
|
|
18
18
|
A section with nothing in it has no tab, and its address answers 404. Every page ends with a
|
|
19
19
|
"Made with Homie" link to https://homie.rocks/studio/. studio.json may add `"tagline"` (one line, on Home and in
|
|
20
|
-
the feeds)
|
|
20
|
+
the feeds), `"site": { "featured": "<game id>" }` (the game on Home; otherwise the first with its own footage) and
|
|
21
|
+
`"site": { "order": ["<game id>", "<game id>"] }`: **the order of the games** in every list (Home's cards, the
|
|
22
|
+
Games page, `/api/games`, `/sitemap.xml`, `/llms.txt` and `/.well-known/homie-studio.json`). The
|
|
23
|
+
games it names come first, as it names them; every other game follows by id, which is the order all of them had
|
|
24
|
+
before. An id that is not one of the studio's games is skipped, and the build says so. Without `featured`, Home's
|
|
25
|
+
hero is still the first game with footage, now looked for in that order.
|
|
21
26
|
|
|
22
27
|
Every card (Home, Games, Rooms, a post's), every live room's row and the directory's manifest show a game's
|
|
23
28
|
**landing still**: `hero/wide.jpg` (or `landing.hero.image`), else a trailer's poster, else game.json's `cover`.
|
|
24
29
|
|
|
25
30
|
## What the homie.rocks hub reads
|
|
26
31
|
|
|
27
|
-
`/.well-known/homie-studio.json` is the studio's manifest: its games (each with its landing still
|
|
32
|
+
`/.well-known/homie-studio.json` is the studio's manifest: its games (in the site's order, each with its landing still
|
|
33
|
+
as `cover`, and `build`: `{ "hash", "bundle" }`, the digest `homie-studio build` printed for the game that is live
|
|
34
|
+
and the address of the bundle its page loads; [The build](#the-build)),
|
|
28
35
|
songs (each with a `cover`), videos (each with a `poster`), the latest posts, and `rooms`, the address of
|
|
29
36
|
`/api/rooms`, so the hub's Rooms page lists this studio's public rooms. With studio.json `"stats": { "share": true }`
|
|
30
37
|
(`homie-studio stats share on`) it also says "played this week": `played` for the whole studio and each game's own
|
|
@@ -73,26 +80,41 @@ Made from the game's own files, nothing invented:
|
|
|
73
80
|
`games/<id>/hero/` by the house brands' names (`wide.mp4` and `tall.mp4`, their AV1 cuts `wide.av1.mp4` and
|
|
74
81
|
`tall.av1.mp4`, stills `wide.jpg` and `tall.jpg`); a trailer in `videos/manifest.json` whose `for.game` is the
|
|
75
82
|
game (its 16:9 `video`, 9:16 `vertical` and `poster`). A loop of 8 to 15 s under 3 MB each, muted, plays best.
|
|
83
|
+
|
|
84
|
+
**The files, exactly** (all in `games/<id>/hero/`, every one optional; `homie-studio build` names the first two
|
|
85
|
+
when a landing has neither picture nor footage):
|
|
86
|
+
|
|
87
|
+
| File | Shape | What it is |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| `wide.jpg` | 16:9, 1280×720 or larger | The still a computer, a TV and every card show (also `.webp`, `.png`, `.avif`) |
|
|
90
|
+
| `tall.jpg` | 9:16, 720×1280 | The still an upright phone shows; without it the wide one is cropped |
|
|
91
|
+
| `wide.mp4` | 16:9, 1280×720 to 1600×900 | Footage over the still: silent, 8 to 15 s, under 3 MB (also `.webm`) |
|
|
92
|
+
| `tall.mp4` | 9:16, 720×1280 | The same for an upright phone |
|
|
93
|
+
| `wide.av1.mp4`, `tall.av1.mp4` | as above | AV1 cuts, offered first to a browser that plays them |
|
|
94
|
+
|
|
95
|
+
The shapes are what fills the screen without waste, not a rule: any size is shown, cropped to fill
|
|
96
|
+
(`landing.hero.focus` says which part to keep). One file is 25 MiB at most. In the built site they are served
|
|
97
|
+
at `/games/<id>/_landing/<file>` (a static game's, which copies its whole folder, at `/games/<id>/hero/<file>`);
|
|
98
|
+
`/api/games` says each exactly, as `games[].landing.hero` (`wide`, `tall`, `wideImage`, `tallImage`).
|
|
76
99
|
- **The pitch** (`landing.pitch`, else the blurb), a big **Play** button that drops into a public room at once,
|
|
77
100
|
and the live line ("3 players playing right now", refreshed every 15 s from `/<id>/live`).
|
|
78
101
|
- **Phone, computer, TV**: the controls for each (`landing.controls`, else `credits.json` `controls`), a code a
|
|
79
102
|
phone scans to play, and how to put the game on a TV (`/<id>/tv` shows the room with its own join code).
|
|
80
103
|
- **Live rooms**, each with Join. When studio.json shares its stats (`stats.share`), also "played this week".
|
|
81
104
|
- **How to play**: `landing.about`, `landing.howToPlay` (a few lines), the controls, the room facts.
|
|
82
|
-
- **Credits**: the studio and `landing.credits` (`[{ "role", "name", "url" }]`); for a
|
|
83
|
-
<game> by <studio>" linked to the original's page (game.json `remixOf
|
|
105
|
+
- **Credits**: the studio and `landing.credits` (`[{ "role", "name", "url" }]`); for a game that was made from another
|
|
106
|
+
studio's, "Based on <game> by <studio>" linked to the original's page (game.json `remixOf`; it is also
|
|
84
107
|
under the game's name in the hero); for a port, the original, its author and licence and every part inside it
|
|
85
108
|
(`credits.json`, as the port skill writes it), with the full licence texts on `/<id>/credits`; Homie's open
|
|
86
|
-
engine; and the
|
|
87
|
-
- **Make a game like this**: the two commands that install the Homie plugin and the words to say. When the game
|
|
88
|
-
shares its source (the default; `"share": { "source": false }` keeps it closed, and the owner's Remixable
|
|
89
|
-
switch withdraws it) and its licence allows remixing, the words remix it from `/games/<id>/source.json`, and
|
|
90
|
-
the button goes to `https://homie.rocks/studio/?remix=<that address>`.
|
|
109
|
+
engine; and the licence the game names for itself, when it names one.
|
|
91
110
|
|
|
92
|
-
A game's licence is game.json `"license"
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
|
|
111
|
+
A game's licence is game.json `"license"`: an SPDX identifier (`"MIT"`, `"CC-BY-4.0"`), as the word or as
|
|
112
|
+
`{ "spdx": "MIT" }`. It is a statement about the game, said in its credits, its structured data and `/llms.txt`; a
|
|
113
|
+
game that names none says nothing. It offers nobody the game's source: no game is handed over whole. Games build on
|
|
114
|
+
each other through parts, the pieces a studio chooses to share (`parts/PARTS.md`; a game with shared parts gets a
|
|
115
|
+
"Parts from this game" band, and the studio a `/parts/` page). Remix was retired: the settings it had (`"remix"`,
|
|
116
|
+
`"share"`, `landing.make`, the licence words `"remix-with-credit"`, `"remix-freely"` and `"no-remix"`) are ignored
|
|
117
|
+
with one build note, and `/games/<id>/source.json` answers 410.
|
|
96
118
|
|
|
97
119
|
game.json's `landing` block, every key optional:
|
|
98
120
|
|
|
@@ -161,12 +183,74 @@ atproto later. `/.well-known/homie-studio.json` lists the latest posts for the h
|
|
|
161
183
|
| `site/theme.json` | The tokens every page uses: `bg`, `fg`, `accent`, `glow`, `panel`, `accentInk` (colours: `#hex`, `rgb()`, `hsl()`, `oklch()`), `display`, `text`, `mono` (font lists), `fonts` (`[{ "family", "src": "/fonts/x.woff2", "weight": "400 800" }]`, files in `site/public`), `radius` (0 to 40), `mark` (a logo), `icon`, `social` (the share picture), `wordmark`, `uppercase: false`, `scheme: "light"`. Or `palette`: neon, dock, gold, acid, ember, orchid, tide, candy. A value that is not what it should be is dropped, with a warning. |
|
|
162
184
|
| `site/theme.css` | CSS after the site's own, on every generated page: restyle anything. |
|
|
163
185
|
| `site/partials/<name>.html` | A piece of every generated page: `head` (in `<head>`), `header` (the top line and its tabs), `footer` (the "Made with Homie" footer), `home` (a band on Home), `game` (a band on every landing), `game-<id>` (a band on one), `post` (after a post). `{{studio.name}}`, `{{game.name}}`, `{{game.id}}`, `{{game.play}}`, `{{year}}` are filled in. A band partial sits in one of the page's bands, unless it is its own `<section class="band">`. |
|
|
164
|
-
| `site/pages/<path>/index.html` (or `site/pages/<path>.html`) | A whole page at `/<path>/`, served as it is, instead of the generated one (`site/pages/index.html` is Home, `site/pages/<id>/index.html` a game's landing) or beside them (`/about/`). It may borrow the site's parts: `<!-- homie:style -->`, `<!-- homie:header -->`, `<!-- homie:footer -->`, `<!-- homie:script -->`,
|
|
186
|
+
| `site/pages/<path>/index.html` (or `site/pages/<path>.html`) | A whole page at `/<path>/`, served as it is, instead of the generated one (`site/pages/index.html` is Home, `site/pages/<id>/index.html` a game's landing) or beside them (`/about/`). It may borrow the site's parts: `<!-- homie:style -->`, `<!-- homie:header -->`, `<!-- homie:footer -->`, `<!-- homie:script -->`, in its `<head>` `<!-- homie:schema -->` (the structured data the generated page there would carry: Home's, a landing's, else the studio's Organization), and `<!-- homie:home-hero -->` (Home's generated hero for the featured game: its footage or art, title, pitch, Play and the live line) or `<!-- homie:home-hero <id> -->` (the same for the game it names), so a Home of the studio's own never writes a hero by hand. The rooms, the game files, the API and the owner's pages are never a page's. |
|
|
165
187
|
| `site/public/` | Files served as they are at the same path: fonts, a logo, hero footage, a page's pictures. A `robots.txt`, `sitemap.xml`, `llms.txt` or `llms-full.txt` here replaces the one the site makes. |
|
|
166
188
|
| `site/src/worker.mjs` | The Worker. A studio that keeps a whole brand site of its own (with its own routes) wraps the template's Worker here, as the house brands do; everything it does not answer goes to the template. |
|
|
167
189
|
|
|
168
190
|
A studio with its own landing in `site/` keeps it: the template never replaces what the studio made.
|
|
169
191
|
|
|
192
|
+
**A Home of your own, with the generated hero.** `site/pages/index.html` replaces Home. To keep the hero the site
|
|
193
|
+
makes and change only what is under it (or only which game it shows), borrow it:
|
|
194
|
+
|
|
195
|
+
```html
|
|
196
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
197
|
+
<title>Night Owls</title><!-- homie:style --><!-- homie:schema --></head>
|
|
198
|
+
<body><!-- homie:header -->
|
|
199
|
+
<main><!-- homie:home-hero crown-thief -->
|
|
200
|
+
<section class="band"><div class="band-in"><h2>Whatever this studio wants here</h2></div></section></main>
|
|
201
|
+
<!-- homie:footer --><!-- homie:script --></body></html>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The marker needs `<!-- homie:style -->` (the hero's CSS) and `<!-- homie:script -->` (its live line refreshes).
|
|
205
|
+
A game that is private, invite-only or not this studio's leaves nothing there. A page that does want a game's
|
|
206
|
+
hero files by address reads them from `/api/games` (`games[].landing.hero`); for a bundled game they are
|
|
207
|
+
`/games/<id>/_landing/wide.jpg`, `tall.jpg`, `wide.mp4` and `tall.mp4`, as the table under
|
|
208
|
+
[A game's landing](#a-games-landing-id) says. On the generated Home, `"site": { "featured": "<id>" }` already
|
|
209
|
+
picks the hero's game.
|
|
210
|
+
|
|
211
|
+
## The build
|
|
212
|
+
|
|
213
|
+
`homie-studio build` (`npm run build`) makes `site/dist`, and `homie-studio build <id>` rebuilds one game in it.
|
|
214
|
+
|
|
215
|
+
- **All or nothing.** The site is built in a folder of its own (`.studio/build/`, git-ignored) and put in place
|
|
216
|
+
only when all of it is there. A game that does not build fails the command (exit 1) and leaves `site/dist`
|
|
217
|
+
exactly as it was: never a site without that game for the next deploy to ship. When it is put in place,
|
|
218
|
+
`site/dist` and its folders are not removed and made again: a file that did not change is not touched, a file
|
|
219
|
+
that did is renamed into place in one step, and what the new build no longer has is taken away last. A server
|
|
220
|
+
or a script running from `site/dist` keeps its files across a build.
|
|
221
|
+
- **A bundle named by its content.** A game's code is `games/<id>/assets/main-<HASH>.js`, and the built
|
|
222
|
+
`index.html` names it (write `./assets/main.js` in the game's own `index.html`, as always: the build points it at
|
|
223
|
+
the hashed file). A browser or an edge that kept the last build's bundle is never asked for the new one under the
|
|
224
|
+
old name. `assets/main.js` is still there, one line that imports the hashed bundle, for anything that knew the
|
|
225
|
+
old address; `bundle.json` beside the page says the real file and its chunks.
|
|
226
|
+
- **Code that loads later.** The bundle is ES modules with code splitting: `const scene = await import('./level-2')`
|
|
227
|
+
in a game becomes `assets/chunk-<HASH>.js`, fetched when the game asks for it, from beside the bundle, wherever
|
|
228
|
+
the game is served (the site, its frame at `/<id>/__game/`, `dev`, `preview`, the Game Lab). A 3D game can draw
|
|
229
|
+
its first screen before the rest of its code and three.js add-ons arrive. A game with no `import()` is one file.
|
|
230
|
+
- **What changed.** Each game's line says `new`, `changed` or `unchanged` against the build before, its **build
|
|
231
|
+
hash** (sixteen characters over every file of the game, the same digest `homie-studio perf` names a build by).
|
|
232
|
+
`site/dist/_site/build.json` keeps it for whatever runs next (`{ "v":
|
|
233
|
+
1, "at", "commit", "games": { "<id>": { "hash", "bundle", "chunks", "changed" } } }`), and
|
|
234
|
+
the live site says each game's `build.hash` in `/.well-known/homie-studio.json`: "is the live game the one I
|
|
235
|
+
built?" is that against this, with no file to fetch and no edge cache to wait out.
|
|
236
|
+
- **`build --types`** checks the games' TypeScript first (the build itself only strips types, so a type error
|
|
237
|
+
ships otherwise). It uses the studio's own `typescript` (a new studio's `package.json` asks for it;
|
|
238
|
+
`npm install --save-dev typescript` in an older one; the toolkit does not depend on it), reads a game's own
|
|
239
|
+
`tsconfig.json` when it has one, else checks its entry and what that imports the way the build reads them
|
|
240
|
+
(ES2022, a browser, strict). A type error in a game's own files stops the build before anything is built;
|
|
241
|
+
errors in a package the game imports as source are counted and said, never fatal.
|
|
242
|
+
- **`homie-studio preview <id> [--port 8788]`** serves one built game's files and nothing else (no Wrangler, no
|
|
243
|
+
rooms, no database), on this computer only, for a capture, a screenshot or a perf script. It is the page as the
|
|
244
|
+
build made it, with no room to join: the game plays offline with its bots. It prints its address first (one
|
|
245
|
+
line of JSON with `--json`), reads each file when asked, so the next build is what the next reload gets, and
|
|
246
|
+
stops with Ctrl-C.
|
|
247
|
+
- **A 3D game's model budgets.** `createModels()` (`@homie-rocks/studio/assets`) warns in development about any
|
|
248
|
+
model over 1,500 triangles or 300 KB: a small prop's budget. A game whose models are bigger on purpose says so
|
|
249
|
+
once, in game.json: `"assets": { "budgets": { "triangles": 8000, "bytes": 1500000 } }` (per model; `texturePx`
|
|
250
|
+
and `materials` are taken too, and `createModels({ budget })` or a single `load(url, { budget })` still wins).
|
|
251
|
+
The build writes it into the bundle. A game copied from a 3D starter has `"assets": "library"` there, a note that
|
|
252
|
+
only mattered when it was copied: replace it with the object.
|
|
253
|
+
|
|
170
254
|
## The play page
|
|
171
255
|
|
|
172
256
|
**The arrival (0.26.0).** The play page and the big screen never open blank. From the first paint, an arrival
|
|
@@ -338,7 +422,7 @@ schema.org `@graph`:
|
|
|
338
422
|
| --- | --- |
|
|
339
423
|
| Home | `Organization` (the studio: name, address, `site/theme.json` `mark` as its logo, `social` as its image, the tagline) and `WebSite` |
|
|
340
424
|
| `/games/`, `/rooms/` | `BreadcrumbList`, `ItemList` of the public games' landings (the Rooms page's data never carries a live count) |
|
|
341
|
-
| `/<id>/` | `BreadcrumbList`, `VideoGame` co-typed `WebApplication`: name, description, the pitch as `abstract`, its pictures and screenshots, `genre`, `numberOfPlayers` (min and max), `playMode`, `gamePlatform`, the studio as author and publisher, `license` (the
|
|
425
|
+
| `/<id>/` | `BreadcrumbList`, `VideoGame` co-typed `WebApplication`: name, description, the pitch as `abstract`, its pictures and screenshots, `genre`, `numberOfPlayers` (min and max), `playMode`, `gamePlatform`, the studio as author and publisher, `license` (the licence the game names, linked to its SPDX page; left out when it names none), `isBasedOn` (a port's original), `trailer` (a `VideoObject`), `datePublished` and `dateModified`, a `PlayAction`, and one free-to-play `Offer` (price 0) |
|
|
342
426
|
| `/<id>/credits`, `/<id>/servers/`, a server's page | `BreadcrumbList` |
|
|
343
427
|
| `/music/`, `/music/<slug>/` | `MusicAlbum` (every song names one album) or an `ItemList`; `MusicRecording` (`byArtist` the studio, its duration, its `AudioObject`, its cover, `inAlbum`, `musicalKey`, its date) |
|
|
344
428
|
| `/videos/`, `/videos/<slug>/` | `ItemList`; `VideoObject` (`thumbnailUrl`, `uploadDate`, `duration`, `contentUrl`: the file at its own address, from R2 or the site) |
|
|
@@ -374,15 +458,13 @@ any property schema.org does not put on that type. A whole block of the owner's
|
|
|
374
458
|
- `/sitemap.xml`: Home, Games and each landing (and its credits), Rooms, Music, Videos, Posts and every page in
|
|
375
459
|
`site/pages`, with `lastmod` only where a real date says when it changed.
|
|
376
460
|
- `/llms.txt`, following the llms.txt convention (an H1, a `>` summary, then `##` sections of links): what the
|
|
377
|
-
studio is; each game with its pitch, players, rounds, Play, Watch and the big screen
|
|
378
|
-
|
|
379
|
-
`game_remix` tool, or `homie-studio game remix <source> --id <new-id>`); songs; videos; posts with their Atom and
|
|
461
|
+
studio is; each game with its pitch, players, rounds, Play, Watch and the big screen, the licence it names and
|
|
462
|
+
the game it was based on (when it has either); songs; videos; posts with their Atom and
|
|
380
463
|
JSON feeds; the studio's own pages (a changelog among them); and homie.rocks's own llms.txt.
|
|
381
464
|
- `/llms-full.txt`: the same, with each game's whole description, how to play, controls and credits, and every
|
|
382
465
|
post's text.
|
|
383
466
|
|
|
384
|
-
|
|
385
|
-
allows it, as for the directory. They are cached for five minutes. A file of the studio's own in `site/public` wins.
|
|
467
|
+
They are cached for five minutes. A file of the studio's own in `site/public` wins.
|
|
386
468
|
|
|
387
469
|
## Headers
|
|
388
470
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Ember Vale</title>
|
|
7
7
|
<link rel="icon" href="data:,">
|
|
8
8
|
<style>
|
|
9
|
-
html, body { margin: 0; height: 100%; background: #0b0f0a; overflow: hidden; overscroll-behavior: none; -webkit-user-select: none; user-select: none; font: 15px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; color: #f3ead2; }
|
|
9
|
+
html, body { margin: 0; height: 100%; background: #0b0f0a; overflow: hidden; overscroll-behavior: none; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; font: 15px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; color: #f3ead2; }
|
|
10
10
|
canvas { position: fixed; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
|
|
11
11
|
.ui { position: fixed; z-index: 3; }
|
|
12
12
|
.pill { left: calc(12px + env(safe-area-inset-left)); top: calc(86px + env(safe-area-inset-top)); display: flex; gap: 6px; }
|
|
@@ -174,6 +174,10 @@ const room = createRoom<Body, SlimeRow[], { slimes: Slime[]; seq: number }>({
|
|
|
174
174
|
adopt: (b) => { me.x = b.x; me.y = b.y; me.has = true; },
|
|
175
175
|
local: () => (me.has ? ({ ...mine(), x: me.x, y: me.y } as Body) : null),
|
|
176
176
|
onTakeover: (b) => { b.score = 0; b.flags = 0; b.hp = b.maxHp; },
|
|
177
|
+
// SEAT OR SOLO (NETPLAY.md section 28). A hero who is in the room but has no body in tonight's hunt (every body is
|
|
178
|
+
// somebody's, or every seat is taken) is not left watching: after four seconds they hunt on their own with bots,
|
|
179
|
+
// the line over the game says so, and they step into the room's hunt the moment it has a body for them.
|
|
180
|
+
fallback: 'solo',
|
|
177
181
|
});
|
|
178
182
|
const net = room.net;
|
|
179
183
|
const myLevel = (): number => hero?.level ?? 1;
|
|
@@ -1229,7 +1233,9 @@ exposePort(net, {
|
|
|
1229
1233
|
size: R,
|
|
1230
1234
|
score: () => { const s = room.mySeat(); return room.view().find((b) => b.seat === s && !b.bot)?.score ?? null; },
|
|
1231
1235
|
busy: () => !ui.idle(),
|
|
1232
|
-
|
|
1236
|
+
// `alive` is read by name by the playtest (port/probe.ts PortExtra): a downed hero is waiting to stand up again, and a
|
|
1237
|
+
// press that moves nothing then is not a broken control.
|
|
1238
|
+
extra: { alive: () => !((myViewBody()?.flags ?? 0) & DOWN), level: () => hero?.level ?? 0, frames: () => frames, saves: () => saves.status().mode },
|
|
1233
1239
|
});
|
|
1234
1240
|
|
|
1235
1241
|
void net.ready.then(() => { const b = room.mine(); if (b) { me.x = b.x; me.y = b.y; me.has = true; } });
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Gem Rush</title>
|
|
7
7
|
<link rel="icon" href="data:,">
|
|
8
8
|
<style>
|
|
9
|
-
html, body { margin: 0; height: 100%; background: #04060c; overflow: hidden; overscroll-behavior: none; -webkit-user-select: none; user-select: none; }
|
|
9
|
+
html, body { margin: 0; height: 100%; background: #04060c; overflow: hidden; overscroll-behavior: none; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
|
|
10
10
|
canvas { position: fixed; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
|
|
11
11
|
</style>
|
|
12
12
|
</head>
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
*
|
|
37
37
|
* Canvas 2D on purpose: the point is the contract, in ~700 readable lines.
|
|
38
38
|
*/
|
|
39
|
-
import { createNetplay, Roster, q, lerp, capMove, PALETTE, AI_MARK, type RoleChange, type RoundInfo, type RoundResult, type Skill, type Slot, type Snapshot } from '@homie-rocks/studio/netplay';
|
|
39
|
+
import { createNetplay, guardGestures, Roster, q, lerp, capMove, PALETTE, AI_MARK, type Peer, type RoleChange, type RoundInfo, type RoundResult, type Skill, type Slot, type Snapshot } from '@homie-rocks/studio/netplay';
|
|
40
40
|
// The port toolkit: its probe (what `homie-studio port check` reads for the owner tests, and sandbox + audio shims),
|
|
41
41
|
// and a flat world on every screen (port/view.ts: the camera, and name labels that never pile up).
|
|
42
42
|
import { BUBBLE_FONT, createBubbles, createLabels, exposePort, fitView, paintBubbles, type BubbleIn, type BubbleOut, type LabelIn, type LabelOut } from '@homie-rocks/studio/port';
|
|
@@ -82,7 +82,7 @@ interface Body extends Knock { slot: number; seat: number | null; name: string;
|
|
|
82
82
|
interface Gem { id: number; x: number; y: number }
|
|
83
83
|
/** Slow state, on the keyed state channel (net.state('zone', ...)), not in the 20 Hz snapshot. */
|
|
84
84
|
interface Zone { n: number; x: number; y: number; r: number; until: number }
|
|
85
|
-
interface Ckpt { round: RoundInfo; bodies: Body[]; gems: Gem[]; roster: Slot[]; tick: number; gemSeq: number }
|
|
85
|
+
interface Ckpt { round: RoundInfo; bodies: Body[]; gems: Gem[]; roster: Slot[]; tick: number; gemSeq: number; occ?: [number, number][] }
|
|
86
86
|
|
|
87
87
|
/* --------------------------------------------------------------- the net */
|
|
88
88
|
/**
|
|
@@ -93,6 +93,9 @@ interface Ckpt { round: RoundInfo; bodies: Body[]; gems: Gem[]; roster: Slot[];
|
|
|
93
93
|
const MOVEMENT: 'owner' | 'host' = (() => { try { return new URLSearchParams(location.search).get('movement') === 'host' ? 'host' : 'owner'; } catch { return 'owner'; } })();
|
|
94
94
|
// caps: its bots read the dial ('skill'), and its Roster takes an AI's slot for it ('agents': the join passes p.agent).
|
|
95
95
|
const net = createNetplay<Snap, Avatar, Ckpt>({ game: 'gem-rush', maxPlayers: MAX_SLOTS, movement: MOVEMENT, snapshotHz: 20, inputHz: 20, checkpointMs: 1000, caps: ['skill', 'agents'], checkpoint: () => checkpoint() });
|
|
96
|
+
// A touch game guards its own page (NETPLAY.md section 24): a long press on the canvas never selects text or raises
|
|
97
|
+
// the copy/paste callout on a phone, and a touch on it never pans or zooms the page.
|
|
98
|
+
guardGestures({ touch: 'canvas' });
|
|
96
99
|
/** The Roster keeps the server's AI seats (revision 6): the policy is read whenever it fills. */
|
|
97
100
|
const policy = () => net.policy;
|
|
98
101
|
|
|
@@ -164,9 +167,12 @@ function newZone(n: number): Zone {
|
|
|
164
167
|
}
|
|
165
168
|
function setZone(z: Zone): void { zone = z; net.state('zone', z); }
|
|
166
169
|
|
|
167
|
-
function startRound(n: number): void {
|
|
170
|
+
function startRound(n: number, rollover = false): void {
|
|
168
171
|
const now = net.now();
|
|
169
172
|
roster.trim();
|
|
173
|
+
// The next round of a running room: the trim is what makes room (the seats a server no longer keeps for AI go
|
|
174
|
+
// here), so whoever was seated with no body is given one now, before the bodies are placed.
|
|
175
|
+
if (rollover && !net.offline) seatWaiting();
|
|
170
176
|
syncBodiesFromRoster();
|
|
171
177
|
let i = 0;
|
|
172
178
|
for (const b of [...bodies.values()].sort((a, c) => a.slot - c.slot)) {
|
|
@@ -208,7 +214,9 @@ function becomeHost(e: RoleChange<Snap, Ckpt>): void {
|
|
|
208
214
|
startRound((e.round?.n ?? 0) + 1);
|
|
209
215
|
}
|
|
210
216
|
// Whoever is connected now is who plays: seats that left during the gap become bots.
|
|
211
|
-
const peers = net.offline ? [{ seat: 0, name: 'You' }] : [...net.peers.values()].filter((p) => p.seat !== null).map((p) => ({ seat: p.seat, name: p.name, agent: p.agent ?? null }));
|
|
217
|
+
const peers = net.offline ? [{ seat: 0, name: 'You' }] : [...net.peers.values()].filter((p) => p.seat !== null).map((p) => ({ seat: p.seat, name: p.name, agent: p.agent ?? null, occ: p.occ ?? null }));
|
|
218
|
+
// `occ` (NETPLAY.md section 25): which stay in its seat each peer's is, so a seat number that changed hands while
|
|
219
|
+
// nobody was hosting is claimed afresh instead of inheriting the last player's body in silence.
|
|
212
220
|
const { claimed } = roster.reconcile(peers);
|
|
213
221
|
syncBodiesFromRoster();
|
|
214
222
|
for (const s of claimed) { const b = bodies.get(s.slot); if (b && b.seat !== mySeat()) hostMoved(b); }
|
|
@@ -225,7 +233,7 @@ function becomeHost(e: RoleChange<Snap, Ckpt>): void {
|
|
|
225
233
|
function restore(e: RoleChange<Snap, Ckpt>): void {
|
|
226
234
|
const ck = e.ckpt?.d ?? null;
|
|
227
235
|
if (ck) {
|
|
228
|
-
roster = Roster.from(ck.roster, { min: MIN_SLOTS, max: MAX_SLOTS, botName, policy });
|
|
236
|
+
roster = Roster.from(ck.roster, { min: MIN_SLOTS, max: MAX_SLOTS, botName, policy }, ck.occ ?? null);
|
|
229
237
|
bodies = new Map(ck.bodies.map((b) => [b.slot, { ...b }]));
|
|
230
238
|
gems = ck.gems.map((g) => ({ ...g }));
|
|
231
239
|
gemSeq = ck.gemSeq;
|
|
@@ -266,6 +274,8 @@ function checkpoint(): Ckpt {
|
|
|
266
274
|
bodies: [...bodies.values()].map((b) => ({ ...b })),
|
|
267
275
|
gems: gems.map((g) => ({ ...g })),
|
|
268
276
|
roster: roster.toJSON(),
|
|
277
|
+
// Whose stay each body is, beside the roster: how the next host tells a player who came back from a new one.
|
|
278
|
+
occ: roster.occupants(),
|
|
269
279
|
tick,
|
|
270
280
|
gemSeq,
|
|
271
281
|
};
|
|
@@ -275,15 +285,30 @@ net.on('role', (e) => {
|
|
|
275
285
|
if (e.role === 'host') becomeHost(e);
|
|
276
286
|
else { hosting = false; }
|
|
277
287
|
});
|
|
278
|
-
|
|
279
|
-
|
|
288
|
+
/** Host: a body for somebody the relay seated. False when every body is somebody's (they wait: see seatWaiting). */
|
|
289
|
+
function giveBody(p: Peer): boolean {
|
|
290
|
+
if (p.seat === null) return false;
|
|
280
291
|
// An AI takes a seat kept for AI, a person never does (revision 6: the Roster needs p.agent for that).
|
|
281
|
-
const c = roster.claim(p.seat, p.name, p.agent ? { role: p.agent.role, hands: p.agent.hands } : null);
|
|
282
|
-
if (!c) return;
|
|
292
|
+
const c = roster.claim(p.seat, p.name, p.agent ? { role: p.agent.role, hands: p.agent.hands } : null, p.occ ?? null);
|
|
293
|
+
if (!c) return false;
|
|
283
294
|
syncBodiesFromRoster();
|
|
284
295
|
const b = bodies.get(c.slot.slot);
|
|
285
296
|
// The arriving human takes over the bot's body where it stands, score and all (reset = "adopt this position").
|
|
286
297
|
if (b) { b.vx = 0; b.vy = 0; b.kvx = 0; b.kvy = 0; b.knockUntil = 0; hostMoved(b); }
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* SEAT WHOEVER IS WAITING (NETPLAY.md section 28). The relay seats a person; this host gives them a body. A claim that
|
|
302
|
+
* found every body taken (a server's AI seats were still kept, say) used to be tried once: that player watched for the
|
|
303
|
+
* rest of the visit, online, with nothing said. It is tried again whenever a body frees up and at every round start.
|
|
304
|
+
*/
|
|
305
|
+
function seatWaiting(): boolean {
|
|
306
|
+
let any = false;
|
|
307
|
+
for (const p of net.peers.values()) if (p.seat !== null && !roster.bySeat(p.seat) && giveBody(p)) any = true;
|
|
308
|
+
return any;
|
|
309
|
+
}
|
|
310
|
+
net.on('join', (p) => {
|
|
311
|
+
if (!hosting || !giveBody(p)) return;
|
|
287
312
|
publishRoster();
|
|
288
313
|
net.snapshot(buildSnap(), tick, true);
|
|
289
314
|
});
|
|
@@ -291,7 +316,10 @@ net.on('leave', (p) => {
|
|
|
291
316
|
if (!hosting || p.seat === null) return;
|
|
292
317
|
roster.release(p.seat); // their body stays, driven by a bot
|
|
293
318
|
syncBodiesFromRoster();
|
|
319
|
+
// A body just went to a bot: somebody seated with none takes it now.
|
|
320
|
+
const seated = seatWaiting();
|
|
294
321
|
publishRoster();
|
|
322
|
+
if (seated) net.snapshot(buildSnap(), tick, true);
|
|
295
323
|
});
|
|
296
324
|
// The server's policy changed: its AI seats come (or go between rounds) at once.
|
|
297
325
|
net.on('policy', () => { if (!hosting) return; roster.fill(); syncBodiesFromRoster(); publishRoster(); });
|
|
@@ -577,7 +605,7 @@ function stepHost(dt: number): void {
|
|
|
577
605
|
}
|
|
578
606
|
}
|
|
579
607
|
if (now >= round.endsAt) endRound();
|
|
580
|
-
} else if (round && round.phase === 'over' && now >= round.endsAt) startRound(round.n + 1);
|
|
608
|
+
} else if (round && round.phase === 'over' && now >= round.endsAt) startRound(round.n + 1, true);
|
|
581
609
|
if (net.snapshotDue()) net.snapshot(buildSnap(), tick);
|
|
582
610
|
}
|
|
583
611
|
|
|
@@ -980,7 +1008,8 @@ function hud(cw: number, ch: number, phone: boolean, list: { slot: number; name:
|
|
|
980
1008
|
ctx.fillText(`${row.place}. ${mine && !net.watching ? 'You' : label(row.name, row.bot)}${mine && net.watching ? ' ◂' : ''} — ${row.score}`, cw / 2, y + 56 + i * 26);
|
|
981
1009
|
});
|
|
982
1010
|
}
|
|
983
|
-
|
|
1011
|
+
// Only a browser that WAS in its room is reconnecting; one still joining, or stopped for good, is not (section 22).
|
|
1012
|
+
if (net.link === 'reconnecting' && net.role !== 'host') {
|
|
984
1013
|
ctx.textAlign = 'center'; ctx.fillStyle = '#ffd166'; ctx.font = '600 14px ui-sans-serif, system-ui, sans-serif';
|
|
985
1014
|
ctx.fillText('Reconnecting…', cw / 2, ch - pad);
|
|
986
1015
|
}
|
|
@@ -1036,7 +1065,7 @@ net.expose({
|
|
|
1036
1065
|
if (!hosting) return false;
|
|
1037
1066
|
const b = [...bodies.values()].find((x) => x.seat === seat);
|
|
1038
1067
|
if (!b) return false;
|
|
1039
|
-
knock(b, b.x < W / 2 ? b.x - 1 : b.x + 1, b.y); // push toward the middle, away from the nearer wall
|
|
1068
|
+
knock(b, b.x < W / 2 ? b.x - 1 : b.x + 1, b.y, -1); // push toward the middle, away from the nearer wall (bumped by nobody)
|
|
1040
1069
|
return true;
|
|
1041
1070
|
},
|
|
1042
1071
|
});
|
|
@@ -15,7 +15,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
15
15
|
- **What:** character (Players/Cat): `public/models/animal-cat.glb`
|
|
16
16
|
- **From:** from the Homie starter library: Kenney Cube Pets, animal-cat (https://kenney.nl/assets/cube-pets); library, in game (src/main.ts)
|
|
17
17
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
18
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
19
18
|
- **Notes:** From the Homie starter library (kenney-cube-pets); the pack's own licence text: licenses/kenney-cube-pets.txt. `game new` fetches the file; the repository holds no models.
|
|
20
19
|
|
|
21
20
|
### animal-caterpillar
|
|
@@ -23,7 +22,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
23
22
|
- **What:** character (Players/Caterpillar): `public/models/animal-caterpillar.glb`
|
|
24
23
|
- **From:** from the Homie starter library: Kenney Cube Pets, animal-caterpillar (https://kenney.nl/assets/cube-pets); library, in game (src/main.ts)
|
|
25
24
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
26
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
27
25
|
- **Notes:** From the Homie starter library (kenney-cube-pets); the pack's own licence text: licenses/kenney-cube-pets.txt. `game new` fetches the file; the repository holds no models.
|
|
28
26
|
|
|
29
27
|
### animal-chick
|
|
@@ -31,7 +29,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
31
29
|
- **What:** character (Players/Chick): `public/models/animal-chick.glb`
|
|
32
30
|
- **From:** from the Homie starter library: Kenney Cube Pets, animal-chick (https://kenney.nl/assets/cube-pets); library, in game (src/main.ts)
|
|
33
31
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
34
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
35
32
|
- **Notes:** From the Homie starter library (kenney-cube-pets); the pack's own licence text: licenses/kenney-cube-pets.txt. `game new` fetches the file; the repository holds no models.
|
|
36
33
|
|
|
37
34
|
### animal-crab
|
|
@@ -39,7 +36,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
39
36
|
- **What:** character (Players/Crab): `public/models/animal-crab.glb`
|
|
40
37
|
- **From:** from the Homie starter library: Kenney Cube Pets, animal-crab (https://kenney.nl/assets/cube-pets); library, in game (src/main.ts)
|
|
41
38
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
42
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
43
39
|
- **Notes:** From the Homie starter library (kenney-cube-pets); the pack's own licence text: licenses/kenney-cube-pets.txt. `game new` fetches the file; the repository holds no models.
|
|
44
40
|
|
|
45
41
|
### animal-elephant
|
|
@@ -47,7 +43,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
47
43
|
- **What:** character (Players/Elephant): `public/models/animal-elephant.glb`
|
|
48
44
|
- **From:** from the Homie starter library: Kenney Cube Pets, animal-elephant (https://kenney.nl/assets/cube-pets); library, in game (src/main.ts)
|
|
49
45
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
50
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
51
46
|
- **Notes:** From the Homie starter library (kenney-cube-pets); the pack's own licence text: licenses/kenney-cube-pets.txt. `game new` fetches the file; the repository holds no models.
|
|
52
47
|
|
|
53
48
|
### animal-fox
|
|
@@ -55,7 +50,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
55
50
|
- **What:** character (Players/Fox): `public/models/animal-fox.glb`
|
|
56
51
|
- **From:** from the Homie starter library: Kenney Cube Pets, animal-fox (https://kenney.nl/assets/cube-pets); library, in game (src/main.ts)
|
|
57
52
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
58
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
59
53
|
- **Notes:** From the Homie starter library (kenney-cube-pets); the pack's own licence text: licenses/kenney-cube-pets.txt. `game new` fetches the file; the repository holds no models.
|
|
60
54
|
|
|
61
55
|
### animal-panda
|
|
@@ -63,7 +57,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
63
57
|
- **What:** character (Players/Panda): `public/models/animal-panda.glb`
|
|
64
58
|
- **From:** from the Homie starter library: Kenney Cube Pets, animal-panda (https://kenney.nl/assets/cube-pets); library, in game (src/main.ts)
|
|
65
59
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
66
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
67
60
|
- **Notes:** From the Homie starter library (kenney-cube-pets); the pack's own licence text: licenses/kenney-cube-pets.txt. `game new` fetches the file; the repository holds no models.
|
|
68
61
|
|
|
69
62
|
### animal-pig
|
|
@@ -71,7 +64,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
71
64
|
- **What:** character (Players/Pig): `public/models/animal-pig.glb`
|
|
72
65
|
- **From:** from the Homie starter library: Kenney Cube Pets, animal-pig (https://kenney.nl/assets/cube-pets); library, in game (src/main.ts)
|
|
73
66
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
74
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
75
67
|
- **Notes:** From the Homie starter library (kenney-cube-pets); the pack's own licence text: licenses/kenney-cube-pets.txt. `game new` fetches the file; the repository holds no models.
|
|
76
68
|
|
|
77
69
|
### bush
|
|
@@ -79,7 +71,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
79
71
|
- **What:** prop (Places/Bush): `public/models/bush.glb`
|
|
80
72
|
- **From:** from the Homie starter library: Kenney Nature Kit, tree-fat (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
81
73
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
82
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
83
74
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
84
75
|
|
|
85
76
|
### campfire
|
|
@@ -87,7 +78,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
87
78
|
- **What:** prop (Places/Campfire): `public/models/campfire.glb`
|
|
88
79
|
- **From:** from the Homie starter library: Kenney Nature Kit, campfire-stones (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
89
80
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
90
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
91
81
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
92
82
|
|
|
93
83
|
### fence
|
|
@@ -95,7 +85,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
95
85
|
- **What:** kit (Places/Fence): `public/models/fence.glb`
|
|
96
86
|
- **From:** from the Homie starter library: Kenney Nature Kit, fence-simple (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
97
87
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
98
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
99
88
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
100
89
|
|
|
101
90
|
### firewood
|
|
@@ -103,7 +92,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
103
92
|
- **What:** prop (Places/Firewood): `public/models/firewood.glb`
|
|
104
93
|
- **From:** from the Homie starter library: Kenney Nature Kit, campfire-logs (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
105
94
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
106
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
107
95
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
108
96
|
|
|
109
97
|
### flower-purple
|
|
@@ -111,7 +99,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
111
99
|
- **What:** prop (Places/Purple flower): `public/models/flower-purple.glb`
|
|
112
100
|
- **From:** from the Homie starter library: Kenney Nature Kit, flower-purple-a (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
113
101
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
114
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
115
102
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
116
103
|
|
|
117
104
|
### flower-red
|
|
@@ -119,7 +106,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
119
106
|
- **What:** prop (Places/Red flower): `public/models/flower-red.glb`
|
|
120
107
|
- **From:** from the Homie starter library: Kenney Nature Kit, flower-red-a (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
121
108
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
122
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
123
109
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
124
110
|
|
|
125
111
|
### flower-yellow
|
|
@@ -127,7 +113,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
127
113
|
- **What:** prop (Places/Yellow flower): `public/models/flower-yellow.glb`
|
|
128
114
|
- **From:** from the Homie starter library: Kenney Nature Kit, flower-yellow-a (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
129
115
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
130
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
131
116
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
132
117
|
|
|
133
118
|
### gem
|
|
@@ -135,7 +120,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
135
120
|
- **What:** prop (Items/Gem): `public/models/gem.glb`
|
|
136
121
|
- **From:** from the Homie starter library: Kenney Platformer Kit, jewel (https://kenney.nl/assets/platformer-kit); library, in game (src/main.ts)
|
|
137
122
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
138
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
139
123
|
- **Notes:** From the Homie starter library (kenney-platformer-kit); the pack's own licence text: licenses/kenney-platformer-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
140
124
|
|
|
141
125
|
### grass
|
|
@@ -143,7 +127,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
143
127
|
- **What:** prop (Places/Grass): `public/models/grass.glb`
|
|
144
128
|
- **From:** from the Homie starter library: Kenney Nature Kit, grass-leafs-large (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
145
129
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
146
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
147
130
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
148
131
|
|
|
149
132
|
### log
|
|
@@ -151,7 +134,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
151
134
|
- **What:** prop (Places/Log seat): `public/models/log.glb`
|
|
152
135
|
- **From:** from the Homie starter library: Kenney Nature Kit, log-large (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
153
136
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
154
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
155
137
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
156
138
|
|
|
157
139
|
### mushrooms
|
|
@@ -159,7 +141,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
159
141
|
- **What:** prop (Places/Mushrooms): `public/models/mushrooms.glb`
|
|
160
142
|
- **From:** from the Homie starter library: Kenney Nature Kit, mushroom-red-group (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
161
143
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
162
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
163
144
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
164
145
|
|
|
165
146
|
### rock
|
|
@@ -167,7 +148,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
167
148
|
- **What:** prop (Places/Rock): `public/models/rock.glb`
|
|
168
149
|
- **From:** from the Homie starter library: Kenney Nature Kit, stone-large-d (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
169
150
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
170
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
171
151
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
172
152
|
|
|
173
153
|
### stone
|
|
@@ -175,7 +155,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
175
155
|
- **What:** prop (Places/Standing stone): `public/models/stone.glb`
|
|
176
156
|
- **From:** from the Homie starter library: Kenney Nature Kit, stone-tall-c (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
177
157
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
178
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
179
158
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
180
159
|
|
|
181
160
|
### tree-oak
|
|
@@ -183,7 +162,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
183
162
|
- **What:** prop (Places/Oak tree): `public/models/tree-oak.glb`
|
|
184
163
|
- **From:** from the Homie starter library: Kenney Nature Kit, tree-oak (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
185
164
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
186
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
187
165
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
188
166
|
|
|
189
167
|
### tree-pine
|
|
@@ -191,7 +169,6 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
191
169
|
- **What:** prop (Places/Pine): `public/models/tree-pine.glb`
|
|
192
170
|
- **From:** from the Homie starter library: Kenney Nature Kit, tree-pine-round-a (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
193
171
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
194
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
195
172
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|
|
196
173
|
|
|
197
174
|
### tree-round
|
|
@@ -199,5 +176,4 @@ Read on the dates shown. Terms change: read the live page again before publishin
|
|
|
199
176
|
- **What:** prop (Places/Round tree): `public/models/tree-round.glb`
|
|
200
177
|
- **From:** from the Homie starter library: Kenney Nature Kit, tree-default (https://kenney.nl/assets/nature-kit); library, in game (src/main.ts)
|
|
201
178
|
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: Kenney
|
|
202
|
-
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
203
179
|
- **Notes:** From the Homie starter library (kenney-nature-kit); the pack's own licence text: licenses/kenney-nature-kit.txt. `game new` fetches the file; the repository holds no models.
|