← City3D assetfogon/tarp-cargo
Carga con lona
Drag to turn it// «Carga con lona» · propio
// A lumpy pile of cargo covered by an olive green tarp with soft folds, tied down with tan ropes, a corner of a wooden crate showing underneath.
// Cargo under an olive tarp: the cloth hangs from whatever is below it, in folds, tied down with ropes. At the front
// left corner it has been pulled up and the wooden crate underneath shows.
const OLIVE = ["#3d4430", "#4d573a", "#5e6946", "#707c54", "#838f64"]; // dark → light
const ROPE = "#d4b77e", ROPE_DARK = "#a98d58";
const WOOD = { plank: ["#b9803f", "#c68d49", "#d29a55"], groove: "#8a5a28", frame: "#9c6730", frameDark: "#744a20", nail: "#2e2a26" };
const G = 48;
// How high the cargo is at each place (0: nothing there): a tall crate, lower boxes beside it, a drum, some sacks.
function cargo(x, y) {
let h = 0;
if (x >= 9 && x <= 25 && y >= 9 && y <= 38) h = 21;
if (x >= 26 && x <= 40 && y >= 12 && y <= 30) h = Math.max(h, 14);
if (x >= 26 && x <= 37 && y >= 31 && y <= 38) h = Math.max(h, 9);
const drum = Math.hypot(x - 33, y - 21);
if (drum < 6) h = Math.max(h, 14 + Math.sqrt(36 - drum * drum) * 0.9);
const sacks = Math.pow((x - 17) / 7, 2) + Math.pow((y - 28) / 8, 2);
if (sacks < 1) h = Math.max(h, 21 + 4 * Math.sqrt(1 - sacks));
return h;
}
const raw = [], top = [];
for (let x = 0; x < G; x++) {
raw.push([]);
for (let y = 0; y < G; y++) raw[x].push(cargo(x, y));
}
// The cloth: over the cargo it lies on it; around it, it falls steeply to the ground, waving a little (the folds).
const fold = (x, y) => Math.sin(x * 1.1) + Math.sin(y * 1.1);
for (let x = 0; x < G; x++) {
top.push([]);
for (let y = 0; y < G; y++) {
let best = -1;
for (let i = Math.max(0, x - 6); i <= Math.min(G - 1, x + 6); i++)
for (let j = Math.max(0, y - 6); j <= Math.min(G - 1, y + 6); j++)
if (raw[i][j] > 0) best = Math.max(best, raw[i][j] - 4 * Math.hypot(i - x, j - y));
if (raw[x][y] === 0 && best > 0) best += fold(x, y) * 0.7;
top[x].push(best > 0.3 ? Math.round(best) : -1);
}
}
const at = (x, y) => (x < 0 || y < 0 || x >= G || y >= G ? -1 : top[x][y]);
const pulledUp = (x, y, z) => x + y + 0.9 * z < 31;
// The crate that shows: boards, a frame, nails.
for (let x = 9; x <= 25; x++)
for (let y = 9; y <= 38; y++)
for (let z = 0; z <= 20; z++) {
if (x > 10 && y > 10 && z < 19) continue;
const a = x <= 10 ? y - 9 : x - 9, A = x <= 10 ? 30 : 17;
let c = a % 4 === 0 ? WOOD.groove : WOOD.plank[(Math.floor(a / 4) * 5) % 3];
if (a < 2 || a >= A - 2 || z < 2 || z > 18) c = z === 0 || a === 0 ? WOOD.frameDark : WOOD.frame;
if ((a === 1 || a === A - 2) && (z === 1 || z === 19)) c = WOOD.nail;
set(x, y, z, c);
}
for (let x = 0; x < G; x++)
for (let y = 0; y < G; y++) {
const t = top[x][y];
if (t < 0) continue;
const low = Math.min(at(x - 1, y), at(x + 1, y), at(x, y - 1), at(x, y + 1));
const flat = raw[x][y] > 0, f = fold(x, y);
for (let z = Math.max(0, low - 1); z <= t; z++) {
if (pulledUp(x, y, z)) continue;
let k = flat && z === t ? (t >= 23 ? 4 : 3) : f > 0.6 ? 3 : f < -0.6 ? 1 : 2;
if (!flat && z < 3) k -= 1;
if (flat && z < t) k = 2;
if (rand() < 0.08) k -= 1;
if (pulledUp(x, y, z - 2.3)) k = 4; // the hem of the lifted corner
set(x, y, z, OLIVE[Math.max(0, Math.min(4, k))]);
}
}
// The ropes: over the top and down both sides, one block above the cloth.
function rope(cells) {
for (let n = 0; n < cells.length; n++) {
const [x, y] = cells[n], t = top[x][y];
if (t < 0) continue;
let low = t;
for (const m of [n - 1, n + 1]) if (cells[m] && at(cells[m][0], cells[m][1]) >= 0) low = Math.min(low, at(cells[m][0], cells[m][1]));
for (let z = low + 1; z <= t + 1; z++) if (!pulledUp(x, y, z - 1)) set(x, y, z, (x + y + z) % 3 === 0 ? ROPE_DARK : ROPE);
}
}
const along = (fixed, isX) => Array.from({ length: G }, (_, i) => (isX ? [fixed, i] : [i, fixed]));
rope(along(16, true));
rope(along(33, true));
rope(along(17, false));
rope(along(32, false));import { draw } from "./assets/voxel";
import { tarpCargo } from "./assets/tarp-cargo";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, tarpCargo, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: its longest side, in px
// angle: turn on the floor, in radians
// t: seconds; if it moves, that animates it# In a terminal, in the game's folder
fogon assets add tarp-cargo
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["tarp-cargo"] })
# It lands in client/assets/tarp-cargo.ts
# https://fogon.gg/object/tarp-cargo