// 3D monkey model with sword
display3D() {
push();
// Convert from screen coords (0..width, 0..height) to WEBGL coords (centered)
translate(this.pos.x - width / 2, this.pos.y - height / 2, 0);
// Small up‑down bob
const bob = sin(frameCount * 0.08 + this.pos.x * 0.01) * 4;
translate(0, bob, 0);
// Slight Y rotation based on velocity (leans when moving left/right)
const yaw = map(this.vel.x, -this.maxSpeed, this.maxSpeed, -0.5, 0.5);
rotateY(yaw);
// Body color changes slightly when chasing (always chasing now)
let bodyR = 180;
let bodyG = 110;
let bodyB = 60;
// Removed: Temporary debug color for isBackup
// --- Body ---
push();
translate(0, this.size * 0.25, 0);
ambientMaterial(bodyR, bodyG, bodyB);
// Reduced detailX from 32 to 24 to remove console warning
sphere(this.size * 0.4, 24, 24);
pop();
// --- Head ---
push();
ambientMaterial(bodyR, bodyG, bodyB);
translate(0, -this.size * 0.05, 0);
// Reduced detailX from 32 to 24 to remove console warning
sphere(this.size * 0.35, 24, 24);
// Ears
push();
ambientMaterial(130, 80, 45);
translate(-this.size * 0.32, -this.size * 0.02, -this.size * 0.05);
sphere(this.size * 0.16, 20, 16);
pop();
push();
ambientMaterial(130, 80, 45);
translate(this.size * 0.32, -this.size * 0.02, -this.size * 0.05);
sphere(this.size * 0.16, 20, 16);
pop();
// Inner ears
push();
ambientMaterial(190, 140, 90);
translate(-this.size * 0.32, -this.size * 0.02, 0);
sphere(this.size * 0.1, 18, 14);
pop();
push();
ambientMaterial(190, 140, 90);
translate(this.size * 0.32, -this.size * 0.02, 0);
sphere(this.size * 0.1, 18, 14);
pop();
// Face / muzzle patch
push();
ambientMaterial(220, 190, 150);
translate(0, this.size * 0.05, this.size * 0.24);
sphere(this.size * 0.24, 24, 18);
pop();
// Eyes (white)
const eyeOffsetX = this.size * 0.16;
const eyeOffsetY = -this.size * 0.04;
const eyeZ = this.size * 0.34;
push();
ambientMaterial(255);
translate(-eyeOffsetX, eyeOffsetY, eyeZ);
sphere(this.size * 0.08, 16, 12);
pop();
push();
ambientMaterial(255);
translate(eyeOffsetX, eyeOffsetY, eyeZ);
sphere(this.size * 0.08, 16, 12);
pop();
// Pupils look toward mouse (2D direction mapped to small offset)
const look = createVector(mouseX - this.pos.x, mouseY - this.pos.y);
look.limit(this.size * 0.06);
const pupilOffsetX = look.x * 0.15;
const pupilOffsetY = look.y * 0.15;
push();
ambientMaterial(0);
translate(
-eyeOffsetX + pupilOffsetX,
eyeOffsetY + pupilOffsetY,
eyeZ + this.size * 0.03
);
sphere(this.size * 0.03, 10, 8);
pop();
push();
ambientMaterial(0);
translate(
eyeOffsetX + pupilOffsetY,
eyeOffsetY + pupilOffsetY,
eyeZ + this.size * 0.03
);
sphere(this.size * 0.03, 10, 8);
pop();
// Nose
push();
ambientMaterial(80, 50, 40);
translate(0, this.size * 0.11, this.size * 0.35);
sphere(this.size * 0.06, 16, 12);
pop();
// Simple mouth: a darker patch that changes size when chasing
push();
ambientMaterial(90, 40, 30); // Always chasing now
translate(0, this.size * 0.18, this.size * 0.36);
sphere(this.size * 0.08, 16, 12);
pop();
pop(); // end head
// Tail
push();
ambientMaterial(bodyR, bodyG, bodyB);
translate(0, this.size * 0.3, -this.size * 0.35);
rotateX(-PI / 3);
cylinder(this.size * 0.05, this.size * 0.8, 8, 1);
pop();
// --- Sword (held/mounted in front, between head and torso) ---
push();
ambientMaterial(180, 180, 180); // Metallic gray for blade and crossguard
translate(0, 0, this.size * 0.1); // Slightly in front of the origin (neck area)
rotateX(PI / 2); // Rotate to point forward along Z-axis
// Now, the "origin" for the sword is where the handle meets the crossguard.
// The handle extends downwards from this point.
// The blade extends upwards from this point.
// Handle
push();
ambientMaterial(100, 70, 40); // Brown handle
translate(0, this.size * 0.075, 0); // Move down half its length from sword origin
cylinder(this.size * 0.06, this.size * 0.15, 12, 1);
pop();
// Crossguard (already at sword origin)
ambientMaterial(180, 180, 180); // Metallic gray
box(this.size * 0.12, this.size * 0.03, this.size * 0.03);
// Blade
push();
translate(0, -this.size * 0.25, 0); // Move up half its length from sword origin
ambientMaterial(220, 220, 220); // Lighter metallic gray for blade
cylinder(this.size * 0.03, this.size * 0.5, 10, 1);
pop();
pop(); // End sword
pop();
}