Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | /* eslint-disable react/no-unknown-property -- react-three-fiber extends JSX with three.js props */
import type { MinorIslandLayout } from "./minorIslands";
/**
* Visual for one secondary island - grass cap sitting on a stone/dirt
* side wall, with a cluster of pine trees (cone canopy on a cylinder
* trunk) scattered on top. Purely decorative: no click handlers, no
* tooltip userData, no pathfinding impact.
*/
export function MinorIsland({ layout }: { layout: MinorIslandLayout }) {
const [cx, , cz] = layout.center;
const baseY = -layout.height / 2; // drop the cylinder so its top face sits at y=0
const grassCapThickness = 0.2;
const grassTopY = 0; // visual top of the island (grass surface)
return (
<group position={[cx, 0, cz]}>
{/* Stone / dirt side wall - a slightly narrower cylinder under
the grass so the grass cap overhangs and reads as a separate
material band. */}
<mesh position={[0, baseY, 0]} receiveShadow>
<cylinderGeometry args={[layout.radius * 0.95, layout.radius, layout.height, 24]} />
<meshStandardMaterial color="#6b553b" roughness={0.95} />
</mesh>
{/* Grass cap. */}
<mesh position={[0, grassTopY - grassCapThickness / 2, 0]} receiveShadow>
<cylinderGeometry args={[layout.radius, layout.radius, grassCapThickness, 24]} />
<meshStandardMaterial color="#6b8e23" roughness={0.9} />
</mesh>
{layout.trees.map((tree, i) => (
<Tree key={i} tree={tree} grassTopY={grassTopY} />
))}
</group>
);
}
function Tree({
tree,
grassTopY
}: {
tree: {
offset: [number, number];
trunkHeight: number;
trunkRadius: number;
canopyHeight: number;
canopyRadius: number;
};
grassTopY: number;
}) {
const [ox, oz] = tree.offset;
const trunkY = grassTopY + tree.trunkHeight / 2;
const canopyY = grassTopY + tree.trunkHeight + tree.canopyHeight / 2;
return (
<group position={[ox, 0, oz]}>
<mesh position={[0, trunkY, 0]} castShadow>
<cylinderGeometry args={[tree.trunkRadius, tree.trunkRadius, tree.trunkHeight, 8]} />
<meshStandardMaterial color="#5a3a1c" roughness={0.9} />
</mesh>
<mesh position={[0, canopyY, 0]} castShadow>
<coneGeometry args={[tree.canopyRadius, tree.canopyHeight, 10]} />
<meshStandardMaterial color="#2f5d34" roughness={0.85} />
</mesh>
</group>
);
}
|