Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions docs/games/blockhaven/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Graphing Calculator</title>
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='6' fill='%232f72dc'/%3E%3Cpath d='M5 25 C11 25 12 7 16 7 S21 25 27 25' fill='none' stroke='white' stroke-width='2.6' stroke-linecap='round'/%3E%3C/svg%3E">
<link rel="preload" href="fonts/minecraft-regular.woff2?v=muop1mmj" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="fonts/minecraft-regular.woff2?v=STAMP" as="font" type="font/woff2" crossorigin>
<style>
/* The "Minecraft" font (public domain, fonts/LICENSE-minecraft-font.txt; built by tools/font.py): 7-px caps and a
1-px descender on a 10-px em, so 10 GUI px of font-size puts one font pixel on one GUI pixel. Its bold is the
original's bold (each glyph drawn twice, 1 px apart). Pixelify Sans (OFL) covers characters it lacks. */
@font-face { font-family: "Minecraft"; font-style: normal; font-weight: 400; font-display: block; src: url(fonts/minecraft-regular.woff2?v=muop1mmj) format("woff2"); }
@font-face { font-family: "Minecraft"; font-style: normal; font-weight: 700; font-display: block; src: url(fonts/minecraft-bold.woff2?v=muop1mmj) format("woff2"); }
@font-face { font-family: "Pixelify Sans"; font-style: normal; font-weight: 400 700; font-display: swap; src: url(fonts/pixelify-sans-latin.woff2?v=muoh3kij) format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Pixelify Sans"; font-style: normal; font-weight: 400 700; font-display: swap; src: url(fonts/pixelify-sans-latin-ext.woff2?v=muoh3kij) format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "Pixelify Sans"; font-style: normal; font-weight: 400 700; font-display: swap; src: url(fonts/pixelify-sans-cyrillic.woff2?v=muoh3kij) format("woff2"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
@font-face { font-family: "Minecraft"; font-style: normal; font-weight: 400; font-display: block; src: url(fonts/minecraft-regular.woff2?v=STAMP) format("woff2"); }
@font-face { font-family: "Minecraft"; font-style: normal; font-weight: 700; font-display: block; src: url(fonts/minecraft-bold.woff2?v=STAMP) format("woff2"); }
@font-face { font-family: "Pixelify Sans"; font-style: normal; font-weight: 400 700; font-display: swap; src: url(fonts/pixelify-sans-latin.woff2?v=STAMP) format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Pixelify Sans"; font-style: normal; font-weight: 400 700; font-display: swap; src: url(fonts/pixelify-sans-latin-ext.woff2?v=STAMP) format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "Pixelify Sans"; font-style: normal; font-weight: 400 700; font-display: swap; src: url(fonts/pixelify-sans-cyrillic.woff2?v=STAMP) format("woff2"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
:root {
--gs: 3; /* GUI scale, set from the window size like Minecraft's "Auto" */
--u: calc(var(--gs) * 1px); /* one GUI pixel */
Expand Down Expand Up @@ -326,7 +326,10 @@
.icon-strip { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 2); margin: calc(var(--u) * 4) 0; }
.icon-strip img { width: calc(var(--u) * 16); height: calc(var(--u) * 16); }
/* ---------- multiplayer ---------- */
.mp-skins { display: flex; gap: calc(var(--u) * 4); }
.mp-skins { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 4); }
.mp-skins .row { width: 100%; display: flex; gap: calc(var(--u) * 4); }
.mp-skins .row button { flex: 1; }
.mp-skins .row button.on { box-shadow: inset 0 calc(var(--u) * -2) 0 #fff; }
.mp-skin { width: calc(var(--u) * 47); height: calc(var(--u) * 48); padding: calc(var(--u) * 2) 0; flex-direction: column; gap: var(--u); }
.mp-skin canvas { width: calc(var(--u) * 16); height: calc(var(--u) * 32); image-rendering: pixelated; }
.mp-box { width: 100%; flex: none; display: flex; flex-direction: column; align-items: center; gap: calc(var(--u) * 4); padding: calc(var(--u) * 4) 0; }
Expand Down Expand Up @@ -356,7 +359,7 @@
<body>
<div id="boot"><canvas id="boot-canvas"></canvas></div>
<!-- data-modules: how many modules the page loads, kept up to date by tools/stamp.mjs (the splash's download progress) -->
<script type="module" id="boot-script" src="js/splash.js?v=murpnhgy" data-modules="81"></script>
<script type="module" id="boot-script" src="js/splash.js?v=mush3qz5" data-modules="84"></script>
<canvas id="game"></canvas>

<div id="demo" class="hidden">
Expand Down Expand Up @@ -538,7 +541,7 @@ <h3>Blockhaven Guide</h3>
</div>

<div id="error" class="screen dirt hidden"></div>
<script defer src="net-config.js?v=murpnhgy"></script>
<script type="module" src="js/main.js?v=murpnhgy"></script>
<script defer src="net-config.js?v=mush3qz5"></script>
<script type="module" src="js/main.js?v=mush3qz5"></script>
</body>
</html>
72 changes: 35 additions & 37 deletions docs/games/blockhaven/js/data/armor.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// Worn-armor models: inflated boxes that share part names and pivots with the humanoid body,
// so they follow every pose (walking, swinging, sneaking) automatically. One painted skin per
// material and piece; `thin` matches skeleton-style limbs.
import { D, pal } from '../render/mobtex.js?v=murpnhgy';
// Worn armor as Java draws it (HumanoidArmorLayer): two layers per material on Java's armor model
// (entity/humanoid.js). Layer 1 (the helmet, chestplate and boots) sits 1 px out from the body,
// layer 2 (the leggings) half a pixel; each is one 64x32 texture in Java's layout, so a resource
// pack's textures/models/armor/<material>_layer_<n>.png replaces it directly. Without a pack the
// layers are painted here, clear wherever the real textures are clear.
import { D, pal } from '../render/mobtex.js?v=mush3qz5';
import { armorModel as javaArmorModel, armorHide } from '../entity/humanoid.js?v=mush3qz5';

export const ARMOR_MATERIALS = {
leather: { c: '#8e5a34', pattern: 'noise', trim: '#6a4024' },
leather: { c: '#a06540', pattern: 'noise', trim: '#7a4a2c' },
chainmail: { c: '#a4a6ae', pattern: 'scales', trim: '#6e7078' },
iron: { c: '#d6d6d6', pattern: 'noise', trim: '#9a9a9a' },
golden: { c: '#f2cc3a', pattern: 'noise', trim: '#c89a1c' },
Expand All @@ -13,39 +16,40 @@ export const ARMOR_MATERIALS = {
turtle: { c: '#4aa246', pattern: 'scales', trim: '#2e6a2c' },
};
export const ARMOR_PIECES = ['helmet', 'chestplate', 'leggings', 'boots'];
// Java's default leather colour (DyeableLeatherItem.DEFAULT_LEATHER_COLOR), which tints the
// greyscale leather layers of a pack.
export const LEATHER_COLOR = [0xa0, 0x65, 0x40];

const box = (o, s, style, extra = {}) => ({ o, s, style, ...extra });
const part = (pivot, boxes) => ({ pivot, boxes });
// Transparent pixels (visor opening, open bottoms).
// Transparent pixels: a share of a face (rows or a rectangle), or the whole face.
const clear = (rx, ry, rw, rh) => (p, x, y, w, h) => p.rect(x + Math.floor(rx * w), y + Math.floor(ry * h), Math.max(1, Math.round(rw * w)), Math.max(1, Math.round(rh * h)), '#000000', 0);
const clearAll = (p, x, y, w, h) => p.rect(x, y, w, h, '#000000', 0);
const rows = (from, to) => clear(0, from, 1, to - from);

export function armorModel(material, piece, thin = false) {
// The painted layers of one material: layer 1 (helmet head, chestplate body and shoulders, boots)
// and layer 2 (leggings: the belt and legs). Left limbs share the right ones' pixels, as in Java.
export function armorLayerModel(material, layer) {
const m = ARMOR_MATERIALS[material] || ARMOR_MATERIALS.iron;
const st = decor => ({ pal: pal(m.c, 0.1), pattern: m.pattern, decor });
const limb = thin ? 2 : 4, armX = thin ? 5 : 6, legX = 2;
const edge = D.frame(m.trim);
const parts = {};
switch (piece) {
case 'helmet':
parts.head = part([0, 24, 0], [box([-4, 0, -4], [8, 8, 8], st({ front: D.all(edge, clear(0.125, 0.45, 0.75, 0.55)), all: edge, bottom: clearAll }), { inflate: 1 })]);
break;
case 'chestplate':
parts.body = part([0, 12, 0], [box([-4, 0, -2], [8, 12, 4], st({ all: edge, top: clear(0.25, 0.25, 0.5, 0.5), bottom: clearAll }), { inflate: 1 })]);
parts.rightArm = part([armX, 22, 0], [box([-limb / 2, -6, -limb / 2], [limb, 8, limb], st({ all: D.band(0.85, 1, m.trim), bottom: clearAll }), { inflate: 1 })]);
parts.leftArm = part([-armX, 22, 0], [box([-limb / 2, -6, -limb / 2], [limb, 8, limb], st({ all: D.band(0.85, 1, m.trim), bottom: clearAll }), { inflate: 1, mirror: true })]);
break;
case 'leggings':
parts.body = part([0, 12, 0], [box([-4, 0, -2], [8, 4, 4], st({ all: D.band(0, 0.3, m.trim), top: clearAll }), { inflate: 0.5 })]);
parts.rightLeg = part([legX, 12, 0], [box([-limb / 2, -9, -limb / 2], [limb, 9, limb], st({ bottom: clearAll }), { inflate: 0.5 })]);
parts.leftLeg = part([-legX, 12, 0], [box([-limb / 2, -9, -limb / 2], [limb, 9, limb], st({ bottom: clearAll }), { inflate: 0.5, mirror: true })]);
break;
case 'boots':
parts.rightLeg = part([legX, 12, 0], [box([-limb / 2, -12, -limb / 2], [limb, 5, limb], st({ all: D.band(0, 0.25, m.trim), top: clearAll }), { inflate: 1 })]);
parts.leftLeg = part([-legX, 12, 0], [box([-limb / 2, -12, -limb / 2], [limb, 5, limb], st({ all: D.band(0, 0.25, m.trim), top: clearAll }), { inflate: 1, mirror: true })]);
break;
}
return { anim: 'biped', parts, eye: 0 };
if (layer === 1) return javaArmorModel(false, {
head: st({ front: D.all(edge, clear(0.125, 0.45, 0.75, 0.55)), all: edge, bottom: clearAll }),
body: st({ all: edge, top: clear(0.25, 0.25, 0.5, 0.5), bottom: clearAll }),
arm: st({ all: D.all(D.band(0.35, 0.45, m.trim), rows(0.45, 1)), top: edge, bottom: clearAll }),
leg: st({ all: D.all(rows(0, 0.6), D.band(0.6, 0.7, m.trim)), top: clearAll, bottom: edge }),
});
return javaArmorModel(true, {
body: st({ all: D.all(rows(0, 0.67), D.band(0.67, 0.78, m.trim)), top: clearAll, bottom: clearAll }),
leg: st({ all: rows(0.75, 1), top: clearAll, bottom: clearAll }),
});
}

// Which layer a worn item draws and the parts it shows: { skin, model, hide } (null for things that
// are not drawn as armor, like elytra or a pumpkin).
export function armorLayer(itemKey) {
const m = /^(leather|chainmail|iron|golden|diamond|netherite|turtle)_(helmet|chestplate|leggings|boots)$/.exec(itemKey);
if (!m) return null;
const slot = ARMOR_PIECES.indexOf(m[2]), inner = slot === 2;
return { skin: `armor_${m[1]}_${inner ? 2 : 1}`, model: inner ? 'armor_inner' : 'armor_outer', hide: armorHide(slot) };
}

// Elytra wings hang from the shoulders; pose them with wingL / wingR.
Expand All @@ -59,9 +63,3 @@ export function elytraModel() {
wingR: { pivot: [5, 24, 2], boxes: [{ o: [-10, -20, 0], s: [10, 20, 2], inflate: 1, style: mem, mirror: true }] },
} };
}

// Skin key for a worn stack (null for things that are not drawn as armor, like elytra or a pumpkin).
export function armorSkinKey(itemKey, thin = false) {
const m = /^(leather|chainmail|iron|golden|diamond|netherite|turtle)_(helmet|chestplate|leggings|boots)$/.exec(itemKey);
return m ? `armor_${m[1]}_${m[2]}${thin ? '_thin' : ''}` : null;
}
4 changes: 2 additions & 2 deletions docs/games/blockhaven/js/data/enchantments.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// rolled in, which items take them, which ones exclude each other, and the enchanting-table
// and anvil arithmetic. A stack's enchantments live in stack.tag.ench ({ id: level }); an
// enchanted book keeps them in stack.tag.stored. stack.tag.rc is the anvil's prior-work cost.
import { I } from './items.js?v=murpnhgy';
import { mulberry32 } from '../core/noise.js?v=murpnhgy';
import { I } from './items.js?v=mush3qz5';
import { mulberry32 } from '../core/noise.js?v=mush3qz5';

const W = { common: 10, uncommon: 5, rare: 2, very_rare: 1 };
// [id, max level, rarity, category, minCost(level), maxCost(level), flags]
Expand Down
4 changes: 2 additions & 2 deletions docs/games/blockhaven/js/data/items.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Item registry: every block item plus tools, weapons, armor, food and materials.
import { BLOCK_ITEMS, BLOCKS, COLORS, SHAPE, st } from './blocks.js?v=murpnhgy';
import { EGG_MOBS } from './mobs.js?v=murpnhgy';
import { BLOCK_ITEMS, BLOCKS, COLORS, SHAPE, st } from './blocks.js?v=mush3qz5';
import { EGG_MOBS } from './mobs.js?v=mush3qz5';

export const ITEMS = [];
export const I = {}; // key -> item
Expand Down
Loading
Loading