diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 0000000..7920962 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# TOASTSIM — deploy to partly.party/toastsim (games VPS, forum-nginx pattern). +# ./scripts/deploy.sh +# Build with subpath base → rsync to VPS staging → docker cp into web root → curl verify. +set -euo pipefail + +VPS="humanjing@100.71.119.27" +CONTAINER="forum-nginx" +REMOTE_WEB_ROOT="/usr/share/nginx/html/toastsim" +STAGING="/tmp/toastsim_deploy" +REPO="$(cd "$(dirname "$0")/.." && pwd)" + +echo "[1/4] Building (base=/toastsim/)..." +cd "$REPO" +./node_modules/.bin/tsc --noEmit +./node_modules/.bin/vite build --base=/toastsim/ + +echo "[2/4] Rsync dist → $VPS:$STAGING ..." +ssh "$VPS" "rm -rf '$STAGING' && mkdir -p '$STAGING'" +rsync -az dist/ "$VPS:$STAGING/" + +echo "[3/4] Swap into $CONTAINER:$REMOTE_WEB_ROOT ..." +ssh "$VPS" "docker exec '$CONTAINER' rm -rf '$REMOTE_WEB_ROOT' \ + && docker cp '$STAGING' '$CONTAINER:$REMOTE_WEB_ROOT'" + +echo "[4/4] Verify..." +code=$(curl -so /dev/null -w '%{http_code}' "https://partly.party/toastsim/?cb=$(date +%s)") +echo "https://partly.party/toastsim/ → HTTP $code" +[ "$code" = 200 ] || { echo "NOT DEPLOYED (non-200)"; exit 1; } +echo "done." diff --git a/src/scenes/prep.ts b/src/scenes/prep.ts index e71de66..e6e70e7 100644 --- a/src/scenes/prep.ts +++ b/src/scenes/prep.ts @@ -429,18 +429,62 @@ export class PrepView implements View { color: new THREE.Color().setRGB(...this.ing.colors.flesh, THREE.SRGBColorSpace), roughness: 0.5, }); - const edges = [-0.5, ...[...s.cuts].sort((a, b) => a - b), 0.5]; - let x = -this.ing.size * 0.55 - edges.length * 0.02; - for (let i = 0; i + 1 < edges.length; i++) { - const w = (edges[i + 1] - edges[i]) * this.ing.size; - const mid = (edges[i] + edges[i + 1]) / 2; - const rHere = Math.sqrt(Math.max(0.05, 0.25 - mid * mid)) * this.ing.size; - const piece = new THREE.Mesh(new THREE.CylinderGeometry(rHere, rHere, Math.max(0.02, w * 0.92), 20), flesh); - piece.rotation.z = Math.PI / 2; - piece.castShadow = true; - piece.position.set(x + w / 2, BOARD_Y * 2 + rHere, 0.1); - this.pieces.add(piece); - x += w + 0.05; + if (s.pattern.kind === 'dice') { + // A real grid of chunks: pass-A cuts × pass-B cuts, jittered like a board. + // Drawn ~1.6x true size and spread wide — honest proportions, readable + // at game camera distance (true-scale cubes vanish against the board). + const CHUNK = 1.6; + const ea = [-0.5, ...[...s.diceCutsA].sort((a, b) => a - b), 0.5]; + const eb = [-0.5, ...[...s.cuts].sort((a, b) => a - b), 0.5]; + for (let i = 0; i + 1 < ea.length; i++) { + for (let j = 0; j + 1 < eb.length; j++) { + const wa = (ea[i + 1] - ea[i]) * this.ing.size * CHUNK; + const wb = (eb[j + 1] - eb[j]) * this.ing.size * CHUNK; + const h = Math.min(wa, wb, this.ing.size * 0.55); + const piece = new THREE.Mesh(new THREE.BoxGeometry(wa * 0.85, h, wb * 0.85), flesh); + piece.castShadow = true; + piece.position.set( + ((ea[i] + ea[i + 1]) / 2) * this.ing.size * CHUNK * 1.1 + (this.rng.next() - 0.5) * 0.06, + BOARD_Y * 2 + h / 2, + ((eb[j] + eb[j + 1]) / 2) * this.ing.size * CHUNK * 1.1 + (this.rng.next() - 0.5) * 0.06, + ); + piece.rotation.y = (this.rng.next() - 0.5) * 0.4; + this.pieces.add(piece); + } + } + } else if (s.pattern.kind === 'wedges') { + // Diametral cuts: each angle makes two wedges. Sectors exploded slightly. + const angs = [...s.cuts].sort((a, b) => a - b).flatMap((c) => [c * Math.PI, c * Math.PI + Math.PI]); + const r = this.ing.size * 0.5; + const h = this.ing.size * 0.35; + for (let i = 0; i < angs.length; i++) { + const a0 = angs[i]; + const a1 = i + 1 < angs.length ? angs[i + 1] : angs[0] + Math.PI * 2; + const mid = (a0 + a1) / 2; + const piece = new THREE.Mesh( + new THREE.CylinderGeometry(r, r, h, 16, 1, false, a0, a1 - a0), + flesh, + ); + piece.castShadow = true; + // Exploded well apart so the gaps (and any uneven angles) read. + piece.position.set(Math.cos(mid) * 0.28, BOARD_Y * 2 + h / 2, -Math.sin(mid) * 0.28); + piece.rotation.y = (this.rng.next() - 0.5) * 0.15; + this.pieces.add(piece); + } + } else { + const edges = [-0.5, ...[...s.cuts].sort((a, b) => a - b), 0.5]; + let x = -this.ing.size * 0.55 - edges.length * 0.02; + for (let i = 0; i + 1 < edges.length; i++) { + const w = (edges[i + 1] - edges[i]) * this.ing.size; + const mid = (edges[i] + edges[i + 1]) / 2; + const rHere = Math.sqrt(Math.max(0.05, 0.25 - mid * mid)) * this.ing.size; + const piece = new THREE.Mesh(new THREE.CylinderGeometry(rHere, rHere, Math.max(0.02, w * 0.92), 20), flesh); + piece.rotation.z = Math.PI / 2; + piece.castShadow = true; + piece.position.set(x + w / 2, BOARD_Y * 2 + rHere, 0.1); + this.pieces.add(piece); + x += w + 0.05; + } } this.root.add(this.pieces); }