Skip to content

src/models.ts

96 lines · 2.5 KB · TypeScript
Raw
import * as THREE from 'three'

export type EnemyKind = 'drone' | 'weaver' | 'tank'

export type EnemyStats = {
  radius: number
  hp: number
  speed: number
  score: number
  color: number
}

export function getEnemyStats(kind: EnemyKind): EnemyStats {
  switch (kind) {
    case 'drone':
      return { radius: 0.8, hp: 1, speed: 6, score: 100, color: 0xf87171 }
    case 'weaver':
      return { radius: 0.8, hp: 2, speed: 4.5, score: 150, color: 0xe879f9 }
    case 'tank':
      return { radius: 1.1, hp: 5, speed: 2.2, score: 300, color: 0xfbbf24 }
    default: {
      const unknownKind: never = kind
      throw new Error(`Unknown enemy kind: ${String(unknownKind)}`)
    }
  }
}

export const ENEMY_EMISSIVE_INTENSITY = 0.35

function createGlowMaterial(color: number, emissiveIntensity: number) {
  return new THREE.MeshStandardMaterial({
    color,
    emissive: color,
    emissiveIntensity,
    flatShading: true,
    roughness: 0.45,
    metalness: 0.2,
  })
}

const enemyGeometries: Record<EnemyKind, THREE.BufferGeometry> = {
  drone: new THREE.OctahedronGeometry(0.85),
  weaver: new THREE.TorusGeometry(0.6, 0.24, 6, 12),
  tank: new THREE.IcosahedronGeometry(1.15),
}

export function createEnemyMesh(kind: EnemyKind) {
  const { color } = getEnemyStats(kind)
  // Each enemy gets its own material so hit flashes don't affect the others.
  return new THREE.Mesh(
    enemyGeometries[kind],
    createGlowMaterial(color, ENEMY_EMISSIVE_INTENSITY),
  )
}

export const playerBulletGeometry = new THREE.BoxGeometry(0.16, 0.9, 0.16)
export const playerBulletMaterial = new THREE.MeshBasicMaterial({ color: 0xa5f3fc })

export type PlayerShip = {
  group: THREE.Group
  flame: THREE.Mesh
}

export function createPlayerShip(): PlayerShip {
  const group = new THREE.Group()

  const hull = new THREE.Mesh(
    new THREE.ConeGeometry(0.6, 2.2, 6),
    createGlowMaterial(0x7dd3fc, 0.25),
  )
  group.add(hull)

  const wings = new THREE.Mesh(
    new THREE.BoxGeometry(2.4, 0.6, 0.15),
    createGlowMaterial(0x38bdf8, 0.2),
  )
  wings.position.y = -0.4
  group.add(wings)

  const cockpit = new THREE.Mesh(
    new THREE.SphereGeometry(0.28, 10, 8),
    createGlowMaterial(0xf0abfc, 0.6),
  )
  cockpit.position.set(0, 0.15, 0.35)
  group.add(cockpit)

  const flame = new THREE.Mesh(
    new THREE.ConeGeometry(0.35, 1, 8),
    new THREE.MeshBasicMaterial({ color: 0xfb923c, transparent: true, opacity: 0.85 }),
  )
  flame.rotation.z = Math.PI
  flame.position.y = -1.6
  group.add(flame)

  return { group, flame }
}