← Asian3D assetfogon/junco-chino
Junco chino
Drag to turn it// «Junco chino» · gpt-6-sol (medium)
// A Chinese junk sailing ship: a curved brown wooden hull with a high raised stern, red and gold trim, two masts with big ribbed orange-red battened sails, a small cabin at the back, and a painted eye on the bow.
part("ship", [24, 24, 0]);
const woodDark = "#57301f";
const wood = "#79432a";
const woodLight = "#a56238";
const deck = "#bd8450";
const red = "#a82825";
const redLight = "#d44830";
const gold = "#edb94e";
function halfWidth(y) {
if (y < 5) return 2 + (y - 2);
if (y < 15) return 5 + Math.floor((y - 5) * 0.6);
if (y < 35) return 11;
return Math.max(4, 11 - Math.floor((y - 35) * 0.85));
}
function deckHeight(y) {
return 12 + Math.max(0, Math.floor((9 - y) / 3))
+ Math.max(0, Math.floor((y - 33) / 3));
}
// Solid, curved hull with a rising bow and raised stern.
for (let y = 2; y <= 43; y++) {
const w = halfWidth(y);
const top = deckHeight(y);
for (let x = 24 - w; x <= 24 + w; x++) {
const edge = Math.abs(x - 24) / w;
const bottom = 3 + Math.floor(4 * edge * edge) + (y < 6 ? 1 : 0);
for (let z = bottom; z <= top; z++) {
let color = z < 7 ? woodDark : wood;
if (z >= top - 1) color = woodLight;
if (z < top - 1 && rand() < 0.08) color = rand() < 0.5 ? woodDark : woodLight;
set(x, y, z, color);
}
set(x, y, top, (y % 3 === 0) ? "#a56b40" : deck);
}
for (const side of [-1, 1]) {
const x = 24 + side * w;
box(x, y, top, x, y, top + 2, red);
set(x, y, top + 1, redLight);
set(x, y, top - 2, gold);
if (y % 4 === 0 && y > 8 && y < 40) {
box(x, y, top + 2, x, y, top + 4, woodDark);
set(x, y, top + 4, gold);
}
}
}
// A narrow keel joins the underside of the hull and reaches the floor at z=0.
box(23, 11, 0, 25, 37, 3, woodDark);
box(23, 7, 2, 25, 10, 4, woodDark);
box(23, 38, 2, 25, 40, 4, woodDark);
// Horizontal hull planks and the sweeping lower gold accent.
for (let y = 8; y <= 40; y++) {
const w = halfWidth(y);
for (const side of [-1, 1]) {
const x = 24 + side * w;
const top = deckHeight(y);
for (const z of [7, 10]) {
if (z < top - 2) set(x, y, z, y % 5 === 0 ? "#c07842" : woodDark);
}
if (top > 10) set(x, y, top - 4, y % 6 === 0 ? "#ffe092" : gold);
}
}
// Upturned carved prow and stern transom.
for (let y = 2; y <= 7; y++) {
const h = 19 - Math.floor((y - 2) / 2);
box(22, y, deckHeight(y), 26, y, h, y < 4 ? red : woodLight);
box(23, y, h, 25, y, h, gold);
}
box(21, 2, 18, 27, 3, 19, red);
box(23, 1, 18, 25, 2, 19, gold);
for (let x = 19; x <= 29; x++) {
if (Math.abs(x - 24) <= 4) {
box(x, 42, 16, x, 43, 20, x % 2 ? woodLight : red);
set(x, 43, 20, gold);
}
}
// Painted eyes on both sides of the bow.
for (const side of [-1, 1]) {
for (let y = 8; y <= 11; y++) {
const x = 24 + side * halfWidth(y);
box(x, y, 12, x, y, 14, "#fff0c5");
set(x + side, y, 13, "#182d2a");
if (y === 9) set(x + side, y, 14, gold);
}
const x = 24 + side * halfWidth(9);
set(x + side, 9, 13, "#182d2a");
}
// Deck boards, raised stern steps, and ornamental posts.
for (let y = 12; y <= 40; y += 3) {
const w = halfWidth(y) - 2;
for (let x = 24 - w; x <= 24 + w; x++) {
set(x, y, deckHeight(y), "#db9b5a");
}
}
for (let y = 33; y <= 40; y++) {
const z = deckHeight(y);
box(19, y, z + 1, 29, y, z + 1, woodLight);
if (y % 2 === 0) box(19, y, z + 1, 29, y, z + 1, deck);
}
// Small lacquered cabin at the stern.
box(19, 35, 18, 30, 41, 22, red);
box(20, 35, 19, 29, 35, 21, "#632d27");
for (const x of [21, 26]) {
box(x, 34, 19, x + 2, 34, 21, gold);
box(x + 1, 33, 19, x + 1, 33, 20, "#f6d490");
set(x + 1, 33, 21, woodDark);
}
for (const x of [19, 30]) {
box(x, 34, 18, x, 42, 23, redLight);
box(x, 34, 23, x, 42, 23, gold);
}
// Glazed green tiled roof with upturned corners.
for (let x = 16; x <= 33; x++) {
const d = Math.abs(x - 24.5);
const z = 27 - Math.floor(d * 0.48);
for (let y = 33; y <= 44; y++) {
const edgeLift = (y === 33 || y === 44) ? 1 : 0;
set(x, y, z + edgeLift, (x + y) % 3 === 0 ? "#457c65" : "#285648");
set(x, y, z + edgeLift - 1, "#193e3a");
}
}
box(23, 33, 28, 26, 44, 28, gold);
for (const x of [15, 34]) {
for (let y = 33; y <= 44; y++) {
set(x, y, 25 + ((y === 33 || y === 44) ? 1 : 0), "#3d7560");
}
}
for (const x of [15, 34]) for (const y of [33, 44]) {
set(x, y, 27, gold);
}
// Two tall dark masts.
box(23, 15, 13, 24, 16, 44, woodDark);
box(25, 28, 14, 26, 29, 39, woodDark);
for (let z = 17; z <= 43; z += 5) box(23, 14, z, 24, 17, z, gold);
for (let z = 18; z <= 38; z += 5) box(25, 27, z, 26, 30, z, gold);
// Ribbed junk sails.
function sail(mastY, low, high, leftSide) {
for (let z = low; z <= high; z++) {
const t = (z - low) / (high - low);
const width = Math.round(13 + 6 * Math.sin(Math.PI * t));
const near = leftSide ? 23 : 26;
const far = leftSide ? near - width : near + width;
const y = mastY - 2 - Math.round(t * 2);
const lo = Math.min(near, far), hi = Math.max(near, far);
for (let x = lo; x <= hi; x++) {
const border = x === lo || x === hi || z === low || z === high;
const rib = (z - low) % 4 === 0;
let color = rib ? "#873327" : ((z + Math.floor(x / 4)) % 7 < 3 ? "#db6536" : "#e7773a");
if (border) color = "#aa3828";
if (!border && !rib && rand() < 0.075) color = "#f59a51";
set(x, y, z, color);
if (rib) set(x, y - 1, z, x % 5 === 0 ? gold : "#773525");
}
if ((z - low) % 4 === 0) {
set(far, y - 1, z, gold);
set(near, y - 1, z, gold);
}
}
}
sail(15, 22, 42, true);
sail(28, 21, 37, false);
// Yard ends, rigging, and golden mast finials.
for (const [my, lo, hi, left] of [[15, 22, 42, true], [28, 21, 37, false]]) {
const near = left ? 23 : 26;
for (let z = lo; z <= hi; z += 4) {
const t = (z - lo) / (hi - lo);
const w = Math.round(13 + 6 * Math.sin(Math.PI * t));
const far = near + (left ? -w : w);
line(near, my - 2 - Math.round(t * 2) - 1, z,
far, my - 2 - Math.round(t * 2) - 1, z, 0, "#7b3325");
}
}
box(23, 15, 44, 24, 16, 45, gold);
box(25, 28, 39, 26, 29, 40, gold);
line(24, 15, 43, 24, 5, 19, 0, "#e4bd75");
line(26, 29, 38, 31, 40, 20, 0, "#e4bd75");
animate("ship", { type: "bob", axis: "z", speed: 1, amplitude: 1 });import { draw } from "./assets/voxel";
import { juncoChino } from "./assets/junco-chino";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, juncoChino, 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 junco-chino
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["junco-chino"] })
# It lands in client/assets/junco-chino.ts
# https://fogon.page/object/junco-chino