All files / renderer/village MinorIsland.tsx

0% Statements 0/40
100% Branches 1/1
100% Functions 1/1
0% Lines 0/40

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>
  );
}