JavaScript · 2479 bytes Raw Blame History
1 // Bread bits - 3D floating bread with Wind Waker styling
2 import * as THREE from 'three'
3
4 class BreadBit {
5 constructor(scene, gradientMap, x, z) {
6 this.scene = scene
7 this.eaten = false
8 this.fadeOut = false
9 this.fadeAlpha = 1
10
11 // Random size
12 const size = 0.08 + Math.random() * 0.06
13
14 // Bread material - warm tan
15 const material = new THREE.MeshToonMaterial({
16 color: 0xe8c878,
17 gradientMap: gradientMap,
18 transparent: true,
19 opacity: 1
20 })
21
22 // Simple box geometry for bread chunk
23 const geometry = new THREE.BoxGeometry(size, size * 0.6, size)
24
25 this.mesh = new THREE.Mesh(geometry, material)
26 this.mesh.position.set(
27 x + (Math.random() - 0.5) * 0.4,
28 0.05,
29 z + (Math.random() - 0.5) * 0.4
30 )
31 this.mesh.rotation.y = Math.random() * Math.PI * 2
32
33 // Movement
34 this.bobOffset = Math.random() * Math.PI * 2
35 this.driftX = (Math.random() - 0.5) * 0.02
36 this.driftZ = (Math.random() - 0.5) * 0.02
37
38 scene.add(this.mesh)
39 }
40
41 get position() {
42 return this.mesh.position
43 }
44
45 update(delta, elapsed) {
46 if (this.eaten) {
47 this.fadeAlpha -= delta * 3
48 this.mesh.material.opacity = Math.max(0, this.fadeAlpha)
49 this.mesh.position.y -= delta * 0.2 // Sink slightly
50 return this.fadeAlpha <= 0
51 }
52
53 // Bob on water
54 this.mesh.position.y = 0.05 + Math.sin(elapsed * 2 + this.bobOffset) * 0.02
55
56 // Gentle drift
57 this.mesh.position.x += this.driftX * delta
58 this.mesh.position.z += this.driftZ * delta
59
60 // Slow rotation
61 this.mesh.rotation.y += delta * 0.3
62
63 return false
64 }
65
66 dispose() {
67 this.scene.remove(this.mesh)
68 this.mesh.geometry.dispose()
69 this.mesh.material.dispose()
70 }
71 }
72
73 export class BreadManager {
74 constructor(scene, gradientMap) {
75 this.scene = scene
76 this.gradientMap = gradientMap
77 this.bits = []
78 }
79
80 spawnBread(x, z, count = null) {
81 const numBits = count || Math.floor(3 + Math.random() * 3)
82
83 for (let i = 0; i < numBits; i++) {
84 this.bits.push(new BreadBit(this.scene, this.gradientMap, x, z))
85 }
86 }
87
88 update(delta, elapsed) {
89 for (let i = this.bits.length - 1; i >= 0; i--) {
90 const done = this.bits[i].update(delta, elapsed)
91 if (done) {
92 this.bits[i].dispose()
93 this.bits.splice(i, 1)
94 }
95 }
96 }
97
98 getActiveBits() {
99 return this.bits.filter(b => !b.eaten)
100 }
101
102 getMeshes() {
103 return this.bits.map(b => b.mesh)
104 }
105 }