@homie-rocks/studio 0.24.5 → 0.25.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/CHANGELOG.md +32 -1
- package/README.md +47 -1
- package/assets/animate.ts +521 -0
- package/assets/render-page.ts +77 -4
- package/bin/homie-studio.mjs +13 -4
- package/lib/art-cli.mjs +105 -5
- package/lib/art-tools.mjs +168 -1
- package/lib/asset-check.mjs +72 -5
- package/lib/characters.mjs +380 -0
- package/lib/clips.mjs +417 -0
- package/lib/decisions.mjs +38 -8
- package/lib/mcp-tools.mjs +1 -1
- package/lib/mcp.mjs +2 -2
- package/lib/optimise.mjs +79 -3
- package/lib/render3d.mjs +2 -0
- package/lib/rig.mjs +431 -0
- package/lib/scaffold.mjs +7 -0
- package/lib/studio.mjs +18 -3
- package/lib/style-board.mjs +3 -1
- package/lib/template-history.json +39 -0
- package/mcp/ui/animation.js +56 -0
- package/mcp/ui/card.css +4 -0
- package/mcp/ui/cast.js +36 -2
- package/mcp/ui/lineup.js +7 -7
- package/package.json +5 -1
- package/starters/hero-rush-3d/assets/RIGHTS.md +163 -0
- package/starters/hero-rush-3d/assets/manifest.json +1471 -0
- package/starters/hero-rush-3d/credits.json +40 -0
- package/starters/hero-rush-3d/game.json +11 -0
- package/starters/hero-rush-3d/index.html +20 -0
- package/starters/hero-rush-3d/lab.json +32 -0
- package/starters/hero-rush-3d/src/main.ts +2361 -0
- package/starters/hero-rush-3d/style.json +68 -0
- package/starters/hero-rush-3d/tunables.json +32 -0
- package/worker/version.mjs +1 -1
|
@@ -1195,6 +1195,45 @@
|
|
|
1195
1195
|
}
|
|
1196
1196
|
}
|
|
1197
1197
|
}
|
|
1198
|
+
},
|
|
1199
|
+
"0.25.0": {
|
|
1200
|
+
"files": {
|
|
1201
|
+
"CLAUDE.md": "ddf2b2a0376202f6",
|
|
1202
|
+
"README.md": "684b3cca8001673c",
|
|
1203
|
+
"HANDOFF.md": "ce7a8fd91c6bbee2",
|
|
1204
|
+
"games/README.md": "5ad32fe80c61f117",
|
|
1205
|
+
"music/README.md": "a3dc042dadd39df8",
|
|
1206
|
+
"videos/README.md": "16babc3437d5545b",
|
|
1207
|
+
"posts/README.md": "37841aae476bd540",
|
|
1208
|
+
"site/README.md": "ca131cd0472464fb",
|
|
1209
|
+
"site/src/worker.mjs": "ca3f1630f2420ad3"
|
|
1210
|
+
},
|
|
1211
|
+
"sections": {
|
|
1212
|
+
"AGENTS.md": {
|
|
1213
|
+
"(intro)": "214aa46eb3a895d5",
|
|
1214
|
+
"## Layout": "8baf49e4291e7e88",
|
|
1215
|
+
"## Commands (all through the pinned CLI in node_modules)": "d80ec56e4b4fc4c6",
|
|
1216
|
+
"## The site": "ab922bb6bde23293",
|
|
1217
|
+
"## Making games": "abf2f0d59341512d",
|
|
1218
|
+
"## The Game Codex and progress": "0738ef719cef3a4c",
|
|
1219
|
+
"## Art direction and models": "aaea65236db52126",
|
|
1220
|
+
"## Running live games (the back office)": "4aa5f513a7cd2c3b",
|
|
1221
|
+
"## Servers and AI seats": "98d08705087a33ec",
|
|
1222
|
+
"## Selling things (the shop)": "b04b0a2b25f524f2",
|
|
1223
|
+
"## Continuing a build from the Claude app": "0d68081e924ca419",
|
|
1224
|
+
"## Rules": "7e8458adc1b81ece",
|
|
1225
|
+
"## Beta": "9d02638207a26e73"
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1228
|
+
"named": {
|
|
1229
|
+
"Homie Arcade": {
|
|
1230
|
+
"sections": {
|
|
1231
|
+
"AGENTS.md": {
|
|
1232
|
+
"## Commands (all through the pinned CLI in node_modules)": "b35df2c19cd4b534"
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1198
1237
|
}
|
|
1199
1238
|
}
|
|
1200
1239
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* The animation card: each character's clips against the verbs the game needs, every one a small looping preview drawn
|
|
2
|
+
* by the game engine under the game's light (anim_preview), where it came from (its own, or the CC0 library's clips
|
|
3
|
+
* retargeted onto its skeleton), the verbs it lacks with Add (anim_add: free, baked into its skeleton's clip library),
|
|
4
|
+
* and Feel: the Game Lab on the move that plays it (game_lab with its take), New beside Today. */
|
|
5
|
+
(function () {
|
|
6
|
+
var C = Card;
|
|
7
|
+
var state = { busy: null };
|
|
8
|
+
function tile(r, c, sc) {
|
|
9
|
+
var t = C.el('div', 'clip' + (c.have ? '' : ' missing'));
|
|
10
|
+
var art = C.el('div', 'art');
|
|
11
|
+
var src = r.previews && r.previews[c.verb];
|
|
12
|
+
var i = src ? C.img(src, r.id + ' ' + c.verb) : null;
|
|
13
|
+
if (i) art.appendChild(i); else art.appendChild(C.el('span', '', c.have ? '…' : '+'));
|
|
14
|
+
t.appendChild(art);
|
|
15
|
+
t.appendChild(C.el('b', '', c.verb + (c.loop ? ' ↻' : '')));
|
|
16
|
+
t.appendChild(C.el('span', '', c.have ? (c.source ? c.source + (c.retargeted ? ' · retargeted' : '') : 'its own') : 'missing'));
|
|
17
|
+
if (!c.have) t.appendChild(C.btn(state.busy === r.id + c.verb ? 'Adding…' : 'Add (free)', 'small', function () {
|
|
18
|
+
state.busy = r.id + c.verb; C.render();
|
|
19
|
+
C.call('anim_add', { game: sc.game, asset: r.id, verbs: [c.verb] }).then(function (res) { state.busy = null; C.tell('clip:' + r.id + c.verb, 'The person added the ' + c.verb + ' clip to ' + r.id + ' on the animation card (free, retargeted onto its skeleton).'); C.take(res); }).catch(function () { state.busy = null; C.render(); });
|
|
20
|
+
}));
|
|
21
|
+
var take = sc.feel && sc.feel[c.verb];
|
|
22
|
+
if (c.have && take) t.appendChild(C.btn('Feel', 'ghost small', function () {
|
|
23
|
+
state.busy = 'Opening the Game Lab on “' + take + '”…'; C.render();
|
|
24
|
+
C.tell('feel:' + c.verb, 'The person pressed Feel on ' + r.id + '\'s ' + c.verb + ' clip: the Game Lab opens on the "' + take + '" take, New beside Today; its Motion sliders (fade, actSpeed, jumpStretch, landSquash, lean, ...) tune how it feels.');
|
|
25
|
+
C.call('game_lab', { game: sc.game, take: take }).then(function (res) { state.busy = null; C.take(res); }).catch(function () { state.busy = 'The Game Lab did not start.'; C.render(); });
|
|
26
|
+
}));
|
|
27
|
+
return t;
|
|
28
|
+
}
|
|
29
|
+
function render(sc) {
|
|
30
|
+
var card = C.el('main', 'card');
|
|
31
|
+
var rows = sc.rows || [];
|
|
32
|
+
var missing = rows.reduce(function (n, r) { return n + (r.missing || []).length; }, 0);
|
|
33
|
+
C.add(card, C.top('Clips', C.add(C.el('div', 'actions'), C.pill(missing ? missing + ' missing' : 'All there', missing ? 'warn' : 'ok'), C.fullscreenButton())),
|
|
34
|
+
C.el('h1', '', 'How the characters of ' + sc.game + ' move'),
|
|
35
|
+
C.el('p', 'sub', 'The game needs ' + (sc.verbs || []).join(', ') + '. Clips are baked into one library per skeleton at build time; every character on that skeleton shares it.'));
|
|
36
|
+
if (state.busy && !/^[a-z0-9-]+[a-z]+$/.test(state.busy)) card.appendChild(C.el('p', 'note', state.busy));
|
|
37
|
+
rows.forEach(function (r) {
|
|
38
|
+
var sec = C.el('section', 'chars');
|
|
39
|
+
sec.appendChild(C.add(C.el('div', 'dirhead'), C.el('b', '', r.id), C.el('span', '', r.familyLabel + ' · ' + r.bones + ' bones' + (r.animsKB ? ' · clips ' + r.animsKB + ' KB' : '') + ' · ' + r.route)));
|
|
40
|
+
if (r.shown) {
|
|
41
|
+
var grid = C.el('div', 'clips');
|
|
42
|
+
(r.clips || []).forEach(function (c) { grid.appendChild(tile(r, c, sc)); });
|
|
43
|
+
(r.extra || []).forEach(function (v) { grid.appendChild(tile(r, { verb: v, have: true, loop: false, source: null }, sc)); });
|
|
44
|
+
sec.appendChild(grid);
|
|
45
|
+
if (!r.previewed) sec.appendChild(C.btn(state.busy === 'draw' + r.id ? 'Drawing…' : 'Draw its clips (free)', 'ghost small', function () { state.busy = 'draw' + r.id; C.render(); C.call('anim_preview', { game: sc.game, asset: r.id }).then(function (res) { state.busy = null; C.take(res); }).catch(function () { state.busy = null; C.render(); }); }));
|
|
46
|
+
} else sec.appendChild(C.btn('Show its clips', 'ghost small', function () { C.call('anim_plan', { game: sc.game, asset: r.id }).then(C.take).catch(C.render); }));
|
|
47
|
+
card.appendChild(sec);
|
|
48
|
+
});
|
|
49
|
+
(sc.unrigged || []).forEach(function (u) { card.appendChild(C.el('p', 'fine', u + ' has no rig: a still model (the animate guide says how to rig one).')); });
|
|
50
|
+
var foot = C.el('div', 'foot');
|
|
51
|
+
foot.appendChild(C.el('span', 'when', Object.keys(sc.feel || {}).length ? 'Feel opens the Game Lab on this computer' : 'No Game Lab take yet: the lab guide adds one'));
|
|
52
|
+
card.appendChild(foot);
|
|
53
|
+
return card;
|
|
54
|
+
}
|
|
55
|
+
C.start({ kind: 'animation', label: 'Clips', fullscreen: true, render: render, poll: null });
|
|
56
|
+
})();
|
package/mcp/ui/card.css
CHANGED
|
@@ -88,3 +88,7 @@ input[type=text]{flex:1;min-width:0;font:14px/1.2 var(--f-sans);padding:8px 10px
|
|
|
88
88
|
.budget .track{height:8px;border-radius:4px;background:var(--c-bg3);overflow:hidden}.budget .track i{display:block;height:100%;background:var(--c-ok)}.budget .track i.over{background:var(--c-bad)}
|
|
89
89
|
details summary{cursor:pointer;color:var(--c-fg2);font-size:13px;margin-top:10px}
|
|
90
90
|
@media (max-width:420px){.card{padding:14px 12px 12px;border-radius:var(--r-md)}h1{font-size:18px}.stages{grid-auto-flow:row;grid-auto-columns:auto}.frame{height:520px}}
|
|
91
|
+
.chars{margin:12px 0 0;padding:10px;border-radius:var(--r-md);background:var(--c-bg2)}.chars .dirhead{margin-bottom:8px}
|
|
92
|
+
.clips{display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:8px}
|
|
93
|
+
.clip{display:grid;gap:3px;align-content:start;padding:6px;border-radius:var(--r-sm);background:var(--c-bg)}.clip .art{aspect-ratio:1;border-radius:var(--r-sm);background:var(--c-bg3);display:grid;place-items:center;overflow:hidden;color:var(--c-fg3);font-size:20px}.clip .art img{width:100%;height:100%;object-fit:cover}
|
|
94
|
+
.clip b{font-size:13px}.clip span{font-size:11.5px;color:var(--c-fg2);overflow-wrap:anywhere}.clip.missing{opacity:.8;box-shadow:inset 0 0 0 1px var(--c-line2)}.clip button{justify-self:start}
|
package/mcp/ui/cast.js
CHANGED
|
@@ -48,12 +48,46 @@
|
|
|
48
48
|
card.appendChild(grid);
|
|
49
49
|
card.appendChild(C.el('p', 'fine', 'Every item is CC0 (free for any use, no credit required), already phone-sized. Adding one copies it into the game: it never loads from anywhere else.'));
|
|
50
50
|
}
|
|
51
|
+
// The characters: the decisions that shape them, then each one made or planned, with its skeleton and clips.
|
|
52
|
+
function characters(card, sc) {
|
|
53
|
+
var d = sc.decisions || {};
|
|
54
|
+
var facts = C.el('ul', 'list decisions');
|
|
55
|
+
[['style.proportions', 'Proportions'], ['style.shape', 'Shape and silhouette'], ['cast.family', 'Library family'], ['rig.skeleton', 'Skeleton'], ['rig.source', 'Rigs from'], ['rig.bones', 'Bones']].forEach(function (k) {
|
|
56
|
+
var v = d[k[0]]; if (!v) return;
|
|
57
|
+
var li = C.el('li', v.state === 'locked' ? 'locked' : '');
|
|
58
|
+
li.appendChild(C.add(C.el('div', 'cb'), C.el('b', '', k[1] + ': ' + v.label), C.el('span', '', v.why || '')));
|
|
59
|
+
li.appendChild(C.pill(v.state, v.state === 'locked' ? 'ok' : ''));
|
|
60
|
+
facts.appendChild(li);
|
|
61
|
+
});
|
|
62
|
+
card.appendChild(facts);
|
|
63
|
+
var chips = C.el('div', 'chips');
|
|
64
|
+
(sc.palette || []).forEach(function (hex) { var s = C.el('span', 'swatch'); s.style.background = hex; s.title = hex; chips.appendChild(s); });
|
|
65
|
+
card.appendChild(C.add(C.el('p', 'fine', 'Palette and silhouette rule: ' + (sc.silhouette || '') + ' '), chips));
|
|
66
|
+
var list = C.el('ul', 'list cast');
|
|
67
|
+
(sc.rows || []).forEach(function (r) {
|
|
68
|
+
var li = C.el('li', r.state === 'made' ? 'done' : 'todo');
|
|
69
|
+
li.appendChild(thumb(r.thumb, r.id));
|
|
70
|
+
li.appendChild(C.add(C.el('div', 'cb'), C.el('b', '', r.id + (r.card ? ' · ' + r.card : '')), C.el('span', '', r.state === 'made' ? r.kind + ' · ' + (r.route === 'generated' ? 'generated (US$' + r.usd + ')' : r.route + (r.source ? ': ' + r.source : '')) + ' · ' + r.family + ' skeleton, ' + r.bones + ' bones · ' + r.tris + ' triangles · ' + r.heightM + ' m' : r.kind + ' · planned, ' + (r.heightM ? r.heightM + ' m' : 'no model yet')), r.verbs && r.verbs.length ? C.el('span', '', 'Clips: ' + r.verbs.join(', ')) : null));
|
|
71
|
+
var side = C.el('div', 'actions');
|
|
72
|
+
if (r.state === 'made') side.appendChild(C.btn('Clips', 'ghost small', function () { C.call('anim_plan', { game: sc.game, asset: r.id }).then(C.take).catch(C.render); }));
|
|
73
|
+
else {
|
|
74
|
+
side.appendChild(C.btn('Find in the library', 'small', function () { C.call('assets_find', { game: sc.game, query: r.id.replace(/-/g, ' '), kind: r.kind }).then(C.take).catch(C.render); }));
|
|
75
|
+
side.appendChild(C.btn('Generate', 'ghost small', function () { C.tell('char:' + r.id, 'The person wants ' + r.id + ' (' + (r.card || r.kind) + ') generated as a rigged character: price it with character_make (a dry run, about US$1.44) and ask before paying.'); busy[r.id] = 'Asked your AI to price it'; C.render(); }));
|
|
76
|
+
}
|
|
77
|
+
if (busy[r.id]) side.appendChild(C.el('span', 'fine', busy[r.id]));
|
|
78
|
+
li.appendChild(side);
|
|
79
|
+
list.appendChild(li);
|
|
80
|
+
});
|
|
81
|
+
card.appendChild(list);
|
|
82
|
+
card.appendChild(C.el('p', 'fine', 'Library characters are free (CC0); a generated one is about US$1.44 on your own fal account, priced and asked for first. Spent so far: US$' + (sc.spend ? sc.spend.used.toFixed(2) : '0.00') + '.'));
|
|
83
|
+
}
|
|
51
84
|
function render(sc) {
|
|
52
85
|
var card = C.el('main', 'card');
|
|
53
|
-
var title = sc.mode === 'find' ? 'Free models for “' + sc.query + '”' : sc.mode === 'added' ? 'Added ' + (sc.added && sc.added.asset) : 'The cast of ' + sc.game;
|
|
54
|
-
C.add(card, C.top('Cast', sc.mode === 'plan' ? C.pill((sc.rows || []).filter(function (r) { return r.state === 'made'; }).length + ' of ' + (sc.rows || []).length + ' made', '') : null), C.el('h1', '', title));
|
|
86
|
+
var title = sc.mode === 'find' ? 'Free models for “' + sc.query + '”' : sc.mode === 'added' ? 'Added ' + (sc.added && sc.added.asset) : sc.mode === 'characters' ? 'The characters of ' + sc.game : 'The cast of ' + sc.game;
|
|
87
|
+
C.add(card, C.top('Cast', sc.mode === 'characters' ? C.pill((sc.rows || []).filter(function (r) { return r.state === 'made'; }).length + ' made', '') : sc.mode === 'plan' ? C.pill((sc.rows || []).filter(function (r) { return r.state === 'made'; }).length + ' of ' + (sc.rows || []).length + ' made', '') : null), C.el('h1', '', title));
|
|
55
88
|
if (sc.library && sc.library.error) card.appendChild(C.el('p', 'note', 'Starter library: ' + sc.library.error));
|
|
56
89
|
if (sc.mode === 'plan') plan(card, sc);
|
|
90
|
+
else if (sc.mode === 'characters') characters(card, sc);
|
|
57
91
|
else if (sc.mode === 'find') find(card, sc);
|
|
58
92
|
else if (sc.added) card.appendChild(C.el('p', 'sub', sc.added.asset + ' is in games/' + sc.game + (sc.added.after ? ': ' + sc.added.after.tris + ' triangles, ' + sc.added.after.kb + ' KB' : '') + '. Licence: ' + (sc.added.license || 'CC0') + '.'));
|
|
59
93
|
return card;
|
package/mcp/ui/lineup.js
CHANGED
|
@@ -37,25 +37,25 @@
|
|
|
37
37
|
var a = { game: sc.game, asset: sc.asset };
|
|
38
38
|
for (var q in (sc.request || {})) a[q] = sc.request[q];
|
|
39
39
|
for (var k in args) a[k] = args[k];
|
|
40
|
-
C.call('asset_make', a).then(function (r) { state.busy = null; C.take(r); }).catch(function () { state.busy = 'That did not reach the studio.'; C.render(); });
|
|
40
|
+
C.call(sc.character ? 'character_make' : 'asset_make', a).then(function (r) { state.busy = null; C.take(r); }).catch(function () { state.busy = 'That did not reach the studio.'; C.render(); });
|
|
41
41
|
};
|
|
42
42
|
if (sc.concept) { var i = C.img(sc.concept, 'The concept for ' + sc.asset); if (i) { i.style.maxHeight = '360px'; i.style.objectFit = 'contain'; card.appendChild(C.add(C.el('div', 'preview'), i)); } }
|
|
43
43
|
if (sc.dryRun) {
|
|
44
|
-
card.appendChild(C.el('p', 'sub', (sc.step === 'mesh' ? 'The 3D model' : 'The concept image') + ' costs about US$' + (sc.price ? sc.price.usd : '?') + (sc.total ? '; the whole prop about US$' + sc.total : '') + ', on your own fal account.'));
|
|
44
|
+
card.appendChild(C.el('p', 'sub', (sc.step === 'mesh' ? (sc.character ? 'The rigged character' : 'The 3D model') : 'The concept image') + ' costs about US$' + (sc.price ? sc.price.usd : '?') + (sc.total ? '; the whole ' + (sc.character ? 'character' : 'prop') + ' about US$' + sc.total : '') + ', on your own fal account.'));
|
|
45
45
|
card.appendChild(C.add(C.el('div', 'actions'), C.btn('Make it (US$' + (sc.price ? sc.price.usd : '?') + ')', '', function () { C.tell('approve:' + sc.asset + sc.step, 'The person approved US$' + (sc.price && sc.price.usd) + ' for the ' + sc.step + ' of ' + sc.asset + ' on the card.'); run({ step: sc.step, approve: true }, 'Making it…'); })));
|
|
46
46
|
} else if (sc.needs === 'approval' || sc.why) card.appendChild(C.el('p', 'note', sc.why || 'Needs the person\'s yes.'));
|
|
47
47
|
else if ((sc.step === 'concept' || sc.step === 'look') && sc.concept) {
|
|
48
|
-
card.appendChild(C.el('p', 'sub', 'Look at it: the thing, whole, in the game\'s style, on a plain background? Then the 3D model (about US$' + (sc.then ? sc.then.usd : '0.50') + ').'));
|
|
49
|
-
card.appendChild(C.add(C.el('div', 'actions'), C.btn('Make the 3D model', '', function () { C.tell('mesh:' + sc.asset, 'The person approved the 3D model of ' + sc.asset + ' (about US$' + (sc.then ? sc.then.usd : '0.50') + ') on the card.'); run({ step: 'mesh', approve: true }, 'Making the model (1 to 3 minutes)…'); })));
|
|
48
|
+
card.appendChild(C.el('p', 'sub', sc.character ? 'Look at it: one character, whole, in an A-pose, in the game\'s style, on a plain background? Then the rigged 3D character (about US$' + (sc.then ? sc.then.usd : '1.40') + '); its clips come from the free library.' : 'Look at it: the thing, whole, in the game\'s style, on a plain background? Then the 3D model (about US$' + (sc.then ? sc.then.usd : '0.50') + ').'));
|
|
49
|
+
card.appendChild(C.add(C.el('div', 'actions'), C.btn(sc.character ? 'Make the rigged character' : 'Make the 3D model', '', function () { C.tell('mesh:' + sc.asset, 'The person approved the ' + (sc.character ? 'rigged character' : '3D model') + ' of ' + sc.asset + ' (about US$' + (sc.then ? sc.then.usd : sc.character ? '1.40' : '0.50') + ') on the card.'); run({ step: 'mesh', approve: true }, sc.character ? 'Making the rigged character (2 to 5 minutes)…' : 'Making the model (1 to 3 minutes)…'); })));
|
|
50
50
|
} else if (sc.step === 'mesh' && sc.after) {
|
|
51
|
-
card.appendChild(C.el('p', 'note ok', sc.asset + ': ' + sc.before.tris + ' → ' + sc.after.tris + ' triangles, ' + sc.after.kb + ' KB, ' + sc.after.heightM + ' m. Recorded with its receipts and licence.'));
|
|
52
|
-
card.appendChild(C.add(C.el('div', 'actions'), C.btn('Show the lineup', 'ghost', function () { state.busy = 'Drawing the lineup…'; C.render(); C.call('asset_lineup', { game: sc.game }).then(function (r) { state.busy = null; C.take(r); }); })));
|
|
51
|
+
card.appendChild(C.el('p', 'note ok', sc.asset + ': ' + sc.before.tris + ' → ' + sc.after.tris + ' triangles, ' + sc.after.kb + ' KB, ' + sc.after.heightM + ' m. Recorded with its receipts and licence.' + (sc.character && sc.verbs && sc.verbs.length ? ' Clips (the library\'s, retargeted, free): ' + sc.verbs.join(', ') + '.' : '')));
|
|
52
|
+
card.appendChild(C.add(C.el('div', 'actions'), C.btn('Show the lineup', 'ghost', function () { state.busy = 'Drawing the lineup…'; C.render(); C.call('asset_lineup', { game: sc.game }).then(function (r) { state.busy = null; C.take(r); }); }), sc.character ? C.btn('Show its clips', 'ghost', function () { state.busy = 'Drawing its clips…'; C.render(); C.call('anim_preview', { game: sc.game, asset: sc.asset }).then(function (r) { state.busy = null; C.take(r); }); }) : null));
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
function render(sc) {
|
|
56
56
|
var card = C.el('main', 'card');
|
|
57
57
|
var flagged = sc.mode === 'lineup' ? sc.flagged : null;
|
|
58
|
-
C.add(card, C.top(sc.mode === 'make' ? 'Make a prop' : 'Lineup', C.add(C.el('div', 'actions'), flagged !== null && flagged !== undefined ? C.pill(flagged ? flagged + ' flagged' : 'One look', flagged ? 'warn' : 'ok') : null, C.fullscreenButton())),
|
|
58
|
+
C.add(card, C.top(sc.mode === 'make' ? (sc.character ? 'Make a character' : 'Make a prop') : 'Lineup', C.add(C.el('div', 'actions'), flagged !== null && flagged !== undefined ? C.pill(flagged ? flagged + ' flagged' : 'One look', flagged ? 'warn' : 'ok') : null, C.fullscreenButton())),
|
|
59
59
|
C.el('h1', '', sc.mode === 'make' ? sc.asset : 'The cast of ' + sc.game + ', at true scale'));
|
|
60
60
|
if (state.busy) card.appendChild(C.el('p', 'note', state.busy));
|
|
61
61
|
if (sc.mode === 'make') make(card, sc); else lineup(card, sc);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homie-rocks/studio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "A studio for games, music and video on its own Cloudflare: scaffold, multiplayer starters on the netplay contract, a port toolkit that makes existing single-player web games multiplayer, the site Worker with public rooms (Table/Lobby Durable Objects and D1, all on the free Workers plan, no payment method), a site with the hub's sections (Home, Games, Music, Videos, Rooms, Posts with feeds) in the studio's own look, an epic landing page for every game, big songs and videos in the studio's own R2 once it has storage (uploaded, checked by SHA-256, served at the same addresses), deploy, and the homie.rocks directory.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
"types": "./assets/assets.ts",
|
|
47
47
|
"default": "./assets/assets.ts"
|
|
48
48
|
},
|
|
49
|
+
"./animate": {
|
|
50
|
+
"types": "./assets/animate.ts",
|
|
51
|
+
"default": "./assets/animate.ts"
|
|
52
|
+
},
|
|
49
53
|
"./worker": "./worker/index.mjs",
|
|
50
54
|
"./package.json": "./package.json"
|
|
51
55
|
},
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Rights: Hero Rush 3D
|
|
2
|
+
|
|
3
|
+
Every file this game ships, where it came from, its licence and what that allows. Written by `homie-studio assets rights hero-rush-3d` from `assets/manifest.json`; do not edit it by hand.
|
|
4
|
+
|
|
5
|
+
## What the providers' terms say
|
|
6
|
+
|
|
7
|
+
Read on the dates shown. Terms change: read the live page again before publishing anything commercial.
|
|
8
|
+
|
|
9
|
+
- **KayKit** (https://kaylousberg.itch.io/, read 2026-10-02): CC0 1.0: free for personal and commercial use, no attribution required.
|
|
10
|
+
|
|
11
|
+
## Files
|
|
12
|
+
|
|
13
|
+
### barbarian
|
|
14
|
+
|
|
15
|
+
- **What:** character (Players/Barbarian): `public/models/barbarian.glb`
|
|
16
|
+
- **From:** from the Homie starter library: KayKit Adventurers, barbarian (https://github.com/KayKit-Game-Assets/KayKit-Character-Pack-Adventures-1.0); library
|
|
17
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
18
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
19
|
+
- **Notes:** From the Homie starter library (kaykit-adventurers); the pack's own licence text: licenses/kaykit-adventurers.txt. `game new` fetches the file, makes the character phone-sized and bakes its clips; the repository holds no models.
|
|
20
|
+
|
|
21
|
+
### barrel
|
|
22
|
+
|
|
23
|
+
- **What:** prop (Places/Barrel): `public/models/barrel.glb`
|
|
24
|
+
- **From:** from the Homie starter library: KayKit Medieval Hexagon, barrel (https://github.com/KayKit-Game-Assets/KayKit-Medieval-Hexagon-Pack-1.0); library
|
|
25
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
26
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
27
|
+
- **Notes:** From the Homie starter library (kaykit-medieval-hexagon); the pack's own licence text: licenses/kaykit-medieval-hexagon.txt. `game new` fetches the file; the repository holds no models.
|
|
28
|
+
|
|
29
|
+
### chest
|
|
30
|
+
|
|
31
|
+
- **What:** prop (Places/Treasure Chest): `public/models/chest.glb`
|
|
32
|
+
- **From:** from the Homie starter library: KayKit Dungeon Remastered, chest-gold (https://github.com/KayKit-Game-Assets/KayKit-Dungeon-Remastered-1.0); library
|
|
33
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
34
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
35
|
+
- **Notes:** From the Homie starter library (kaykit-dungeon); the pack's own licence text: licenses/kaykit-dungeon.txt. `game new` fetches the file; the repository holds no models.
|
|
36
|
+
|
|
37
|
+
### coin
|
|
38
|
+
|
|
39
|
+
- **What:** prop (Items/Coin): `public/models/coin.glb`
|
|
40
|
+
- **From:** from the Homie starter library: KayKit Dungeon Remastered, coin (https://github.com/KayKit-Game-Assets/KayKit-Dungeon-Remastered-1.0); library
|
|
41
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
42
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
43
|
+
- **Notes:** From the Homie starter library (kaykit-dungeon); the pack's own licence text: licenses/kaykit-dungeon.txt. `game new` fetches the file; the repository holds no models.
|
|
44
|
+
|
|
45
|
+
### crate
|
|
46
|
+
|
|
47
|
+
- **What:** prop (Places/Crate): `public/models/crate.glb`
|
|
48
|
+
- **From:** from the Homie starter library: KayKit Medieval Hexagon, crate-a-big (https://github.com/KayKit-Game-Assets/KayKit-Medieval-Hexagon-Pack-1.0); library
|
|
49
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
50
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
51
|
+
- **Notes:** From the Homie starter library (kaykit-medieval-hexagon); the pack's own licence text: licenses/kaykit-medieval-hexagon.txt. `game new` fetches the file; the repository holds no models.
|
|
52
|
+
|
|
53
|
+
### grove
|
|
54
|
+
|
|
55
|
+
- **What:** prop (Places/Grove): `public/models/grove.glb`
|
|
56
|
+
- **From:** from the Homie starter library: KayKit Medieval Hexagon, trees-a-medium (https://github.com/KayKit-Game-Assets/KayKit-Medieval-Hexagon-Pack-1.0); library
|
|
57
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
58
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
59
|
+
- **Notes:** From the Homie starter library (kaykit-medieval-hexagon); the pack's own licence text: licenses/kaykit-medieval-hexagon.txt. `game new` fetches the file; the repository holds no models.
|
|
60
|
+
|
|
61
|
+
### knight
|
|
62
|
+
|
|
63
|
+
- **What:** character (Players/Knight): `public/models/knight.glb`
|
|
64
|
+
- **From:** from the Homie starter library: KayKit Adventurers, knight (https://github.com/KayKit-Game-Assets/KayKit-Character-Pack-Adventures-1.0); library
|
|
65
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
66
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
67
|
+
- **Notes:** From the Homie starter library (kaykit-adventurers); the pack's own licence text: licenses/kaykit-adventurers.txt. `game new` fetches the file, makes the character phone-sized and bakes its clips; the repository holds no models.
|
|
68
|
+
|
|
69
|
+
### mage
|
|
70
|
+
|
|
71
|
+
- **What:** character (Players/Mage): `public/models/mage.glb`
|
|
72
|
+
- **From:** from the Homie starter library: KayKit Adventurers, mage (https://github.com/KayKit-Game-Assets/KayKit-Character-Pack-Adventures-1.0); library
|
|
73
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
74
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
75
|
+
- **Notes:** From the Homie starter library (kaykit-adventurers); the pack's own licence text: licenses/kaykit-adventurers.txt. `game new` fetches the file, makes the character phone-sized and bakes its clips; the repository holds no models.
|
|
76
|
+
|
|
77
|
+
### pine
|
|
78
|
+
|
|
79
|
+
- **What:** prop (Places/Pine): `public/models/pine.glb`
|
|
80
|
+
- **From:** from the Homie starter library: KayKit Medieval Hexagon, tree-single-a (https://github.com/KayKit-Game-Assets/KayKit-Medieval-Hexagon-Pack-1.0); library
|
|
81
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
82
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
83
|
+
- **Notes:** From the Homie starter library (kaykit-medieval-hexagon); the pack's own licence text: licenses/kaykit-medieval-hexagon.txt. `game new` fetches the file; the repository holds no models.
|
|
84
|
+
|
|
85
|
+
### pine-round
|
|
86
|
+
|
|
87
|
+
- **What:** prop (Places/Round Pine): `public/models/pine-round.glb`
|
|
88
|
+
- **From:** from the Homie starter library: KayKit Medieval Hexagon, tree-single-b (https://github.com/KayKit-Game-Assets/KayKit-Medieval-Hexagon-Pack-1.0); library
|
|
89
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
90
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
91
|
+
- **Notes:** From the Homie starter library (kaykit-medieval-hexagon); the pack's own licence text: licenses/kaykit-medieval-hexagon.txt. `game new` fetches the file; the repository holds no models.
|
|
92
|
+
|
|
93
|
+
### rock
|
|
94
|
+
|
|
95
|
+
- **What:** prop (Places/Rock): `public/models/rock.glb`
|
|
96
|
+
- **From:** from the Homie starter library: KayKit Medieval Hexagon, rock-single-e (https://github.com/KayKit-Game-Assets/KayKit-Medieval-Hexagon-Pack-1.0); library
|
|
97
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
98
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
99
|
+
- **Notes:** From the Homie starter library (kaykit-medieval-hexagon); the pack's own licence text: licenses/kaykit-medieval-hexagon.txt. `game new` fetches the file; the repository holds no models.
|
|
100
|
+
|
|
101
|
+
### rocks
|
|
102
|
+
|
|
103
|
+
- **What:** prop (Places/Rocks): `public/models/rocks.glb`
|
|
104
|
+
- **From:** from the Homie starter library: KayKit Medieval Hexagon, rock-single-c (https://github.com/KayKit-Game-Assets/KayKit-Medieval-Hexagon-Pack-1.0); library
|
|
105
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
106
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
107
|
+
- **Notes:** From the Homie starter library (kaykit-medieval-hexagon); the pack's own licence text: licenses/kaykit-medieval-hexagon.txt. `game new` fetches the file; the repository holds no models.
|
|
108
|
+
|
|
109
|
+
### rogue
|
|
110
|
+
|
|
111
|
+
- **What:** character (Players/Rogue): `public/models/rogue.glb`
|
|
112
|
+
- **From:** from the Homie starter library: KayKit Adventurers, rogue (https://github.com/KayKit-Game-Assets/KayKit-Character-Pack-Adventures-1.0); library
|
|
113
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
114
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
115
|
+
- **Notes:** From the Homie starter library (kaykit-adventurers); the pack's own licence text: licenses/kaykit-adventurers.txt. `game new` fetches the file, makes the character phone-sized and bakes its clips; the repository holds no models.
|
|
116
|
+
|
|
117
|
+
### rogue-hooded
|
|
118
|
+
|
|
119
|
+
- **What:** character (Players/Hooded Rogue): `public/models/rogue-hooded.glb`
|
|
120
|
+
- **From:** from the Homie starter library: KayKit Adventurers, rogue-hooded (https://github.com/KayKit-Game-Assets/KayKit-Character-Pack-Adventures-1.0); library
|
|
121
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
122
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
123
|
+
- **Notes:** From the Homie starter library (kaykit-adventurers); the pack's own licence text: licenses/kaykit-adventurers.txt. `game new` fetches the file, makes the character phone-sized and bakes its clips; the repository holds no models.
|
|
124
|
+
|
|
125
|
+
### skeleton-mage
|
|
126
|
+
|
|
127
|
+
- **What:** creature (Creatures/Skeleton Mage): `public/models/skeleton-mage.glb`
|
|
128
|
+
- **From:** from the Homie starter library: KayKit Skeletons, skeleton-mage (https://github.com/KayKit-Game-Assets/KayKit-Character-Pack-Skeletons-1.0); library
|
|
129
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
130
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
131
|
+
- **Notes:** From the Homie starter library (kaykit-skeletons); the pack's own licence text: licenses/kaykit-skeletons.txt. `game new` fetches the file, makes the character phone-sized and bakes its clips; the repository holds no models.
|
|
132
|
+
|
|
133
|
+
### skeleton-minion
|
|
134
|
+
|
|
135
|
+
- **What:** creature (Creatures/Skeleton Minion): `public/models/skeleton-minion.glb`
|
|
136
|
+
- **From:** from the Homie starter library: KayKit Skeletons, skeleton-minion (https://github.com/KayKit-Game-Assets/KayKit-Character-Pack-Skeletons-1.0); library
|
|
137
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
138
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
139
|
+
- **Notes:** From the Homie starter library (kaykit-skeletons); the pack's own licence text: licenses/kaykit-skeletons.txt. `game new` fetches the file, makes the character phone-sized and bakes its clips; the repository holds no models.
|
|
140
|
+
|
|
141
|
+
### skeleton-rogue
|
|
142
|
+
|
|
143
|
+
- **What:** creature (Creatures/Skeleton Rogue): `public/models/skeleton-rogue.glb`
|
|
144
|
+
- **From:** from the Homie starter library: KayKit Skeletons, skeleton-rogue (https://github.com/KayKit-Game-Assets/KayKit-Character-Pack-Skeletons-1.0); library
|
|
145
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
146
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
147
|
+
- **Notes:** From the Homie starter library (kaykit-skeletons); the pack's own licence text: licenses/kaykit-skeletons.txt. `game new` fetches the file, makes the character phone-sized and bakes its clips; the repository holds no models.
|
|
148
|
+
|
|
149
|
+
### skeleton-warrior
|
|
150
|
+
|
|
151
|
+
- **What:** creature (Creatures/Skeleton Warrior): `public/models/skeleton-warrior.glb`
|
|
152
|
+
- **From:** from the Homie starter library: KayKit Skeletons, skeleton-warrior (https://github.com/KayKit-Game-Assets/KayKit-Character-Pack-Skeletons-1.0); library
|
|
153
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
154
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
155
|
+
- **Notes:** From the Homie starter library (kaykit-skeletons); the pack's own licence text: licenses/kaykit-skeletons.txt. `game new` fetches the file, makes the character phone-sized and bakes its clips; the repository holds no models.
|
|
156
|
+
|
|
157
|
+
### torch
|
|
158
|
+
|
|
159
|
+
- **What:** prop (Places/Torch): `public/models/torch.glb`
|
|
160
|
+
- **From:** from the Homie starter library: KayKit Dungeon Remastered, torch-lit (https://github.com/KayKit-Game-Assets/KayKit-Dungeon-Remastered-1.0); library
|
|
161
|
+
- **Licence:** CC0 1.0 (public domain) (https://creativecommons.org/publicdomain/zero/1.0/); owner: KayKit
|
|
162
|
+
- **A remix:** a remix gets this file (fetched from this studio's site, checked by SHA-256)
|
|
163
|
+
- **Notes:** From the Homie starter library (kaykit-dungeon); the pack's own licence text: licenses/kaykit-dungeon.txt. `game new` fetches the file; the repository holds no models.
|