let-them-talk 4.2.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +640 -540
  2. package/README.md +592 -415
  3. package/cli.js +1089 -589
  4. package/conversation-templates/autonomous-feature.json +22 -0
  5. package/conversation-templates/code-review.json +21 -11
  6. package/conversation-templates/debug-squad.json +21 -11
  7. package/conversation-templates/feature-build.json +21 -11
  8. package/conversation-templates/research-write.json +21 -11
  9. package/dashboard.html +9250 -7771
  10. package/dashboard.js +1232 -29
  11. package/office/agents.js +148 -4
  12. package/office/animation.js +68 -0
  13. package/office/assets.js +431 -0
  14. package/office/builder.js +355 -0
  15. package/office/building-interior.js +261 -0
  16. package/office/campus-env.js +119 -23
  17. package/office/car-hud.js +368 -0
  18. package/office/daynight.js +221 -0
  19. package/office/economy-hud.js +432 -0
  20. package/office/economy-ui.js +238 -0
  21. package/office/environment.js +818 -808
  22. package/office/face.js +65 -0
  23. package/office/fast-travel.js +215 -0
  24. package/office/hq-building.js +295 -0
  25. package/office/index.js +1095 -423
  26. package/office/instancing.js +160 -0
  27. package/office/lod-manager.js +165 -0
  28. package/office/multiplayer-hud.js +428 -0
  29. package/office/net-client.js +299 -0
  30. package/office/particles.js +172 -0
  31. package/office/player.js +658 -436
  32. package/office/post-processing.js +82 -0
  33. package/office/sky.js +319 -0
  34. package/office/street-furniture.js +308 -0
  35. package/office/vehicle.js +455 -0
  36. package/office/world-save.js +91 -0
  37. package/package.json +59 -59
  38. package/server.js +7190 -4685
  39. package/conversation-templates/managed-team.json +0 -12
@@ -0,0 +1,172 @@
1
+ import * as THREE from 'three';
2
+
3
+ /**
4
+ * Particle effects for AI City.
5
+ * Industrial smoke, chimney steam, ambient dust.
6
+ * Uses THREE.Points for minimal draw calls (1 per effect).
7
+ * Target: 120fps — max 200 particles per emitter.
8
+ */
9
+
10
+ const emitters = [];
11
+
12
+ /**
13
+ * Create a smoke emitter at a position (e.g., factory chimney).
14
+ * @param {THREE.Scene} scene
15
+ * @param {THREE.Vector3} position - World position of the emitter
16
+ * @param {Object} options
17
+ * @param {number} options.count - Particle count (default 80)
18
+ * @param {number} options.spread - Horizontal spread (default 1)
19
+ * @param {number} options.height - Max rise height (default 8)
20
+ * @param {number} options.speed - Rise speed (default 0.5)
21
+ * @param {number} options.size - Particle size (default 0.4)
22
+ * @param {number} options.color - Hex color (default 0x888888)
23
+ * @param {number} options.opacity - Max opacity (default 0.3)
24
+ * @returns {Object} Emitter handle for update/dispose
25
+ */
26
+ export function createSmokeEmitter(scene, position, options) {
27
+ const opts = Object.assign({
28
+ count: 80,
29
+ spread: 1,
30
+ height: 8,
31
+ speed: 0.5,
32
+ size: 0.4,
33
+ color: 0x888888,
34
+ opacity: 0.3,
35
+ }, options);
36
+
37
+ const count = opts.count;
38
+ const positions = new Float32Array(count * 3);
39
+ const velocities = new Float32Array(count * 3);
40
+ const lifetimes = new Float32Array(count);
41
+ const maxLifetimes = new Float32Array(count);
42
+
43
+ // Initialize particles
44
+ for (let i = 0; i < count; i++) {
45
+ resetParticle(i, positions, velocities, lifetimes, maxLifetimes, position, opts);
46
+ // Stagger initial lifetimes so particles don't all spawn at once
47
+ lifetimes[i] = Math.random() * maxLifetimes[i];
48
+ }
49
+
50
+ const geometry = new THREE.BufferGeometry();
51
+ geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
52
+
53
+ const material = new THREE.PointsMaterial({
54
+ color: opts.color,
55
+ size: opts.size,
56
+ transparent: true,
57
+ opacity: opts.opacity,
58
+ depthWrite: false,
59
+ sizeAttenuation: true,
60
+ fog: true,
61
+ });
62
+
63
+ const points = new THREE.Points(geometry, material);
64
+ points.name = 'smoke-emitter';
65
+ points.frustumCulled = true;
66
+ scene.add(points);
67
+
68
+ const emitter = {
69
+ points,
70
+ positions,
71
+ velocities,
72
+ lifetimes,
73
+ maxLifetimes,
74
+ origin: position.clone(),
75
+ opts,
76
+ active: true,
77
+ };
78
+ emitters.push(emitter);
79
+ return emitter;
80
+ }
81
+
82
+ function resetParticle(i, positions, velocities, lifetimes, maxLifetimes, origin, opts) {
83
+ const i3 = i * 3;
84
+ positions[i3] = origin.x + (Math.random() - 0.5) * opts.spread * 0.3;
85
+ positions[i3 + 1] = origin.y;
86
+ positions[i3 + 2] = origin.z + (Math.random() - 0.5) * opts.spread * 0.3;
87
+
88
+ velocities[i3] = (Math.random() - 0.5) * opts.spread * 0.1;
89
+ velocities[i3 + 1] = opts.speed * (0.7 + Math.random() * 0.6);
90
+ velocities[i3 + 2] = (Math.random() - 0.5) * opts.spread * 0.1;
91
+
92
+ maxLifetimes[i] = opts.height / opts.speed * (0.8 + Math.random() * 0.4);
93
+ lifetimes[i] = 0;
94
+ }
95
+
96
+ /**
97
+ * Update all active smoke emitters. Call once per frame.
98
+ * @param {number} dt - Delta time in seconds
99
+ */
100
+ export function updateParticles(dt) {
101
+ for (const emitter of emitters) {
102
+ if (!emitter.active) continue;
103
+ const { positions, velocities, lifetimes, maxLifetimes, origin, opts, points } = emitter;
104
+ const count = lifetimes.length;
105
+ const posAttr = points.geometry.getAttribute('position');
106
+
107
+ for (let i = 0; i < count; i++) {
108
+ lifetimes[i] += dt;
109
+ if (lifetimes[i] >= maxLifetimes[i]) {
110
+ resetParticle(i, positions, velocities, lifetimes, maxLifetimes, origin, opts);
111
+ continue;
112
+ }
113
+
114
+ const i3 = i * 3;
115
+ // Wind drift + rise
116
+ positions[i3] += velocities[i3] * dt;
117
+ positions[i3 + 1] += velocities[i3 + 1] * dt;
118
+ positions[i3 + 2] += velocities[i3 + 2] * dt;
119
+
120
+ // Slow horizontal drift increases with height
121
+ velocities[i3] += (Math.random() - 0.5) * 0.02;
122
+ velocities[i3 + 2] += (Math.random() - 0.5) * 0.02;
123
+ }
124
+
125
+ posAttr.needsUpdate = true;
126
+
127
+ // Fade opacity based on average lifetime progress
128
+ const avgProgress = lifetimes.reduce((s, l, i) => s + l / maxLifetimes[i], 0) / count;
129
+ points.material.opacity = opts.opacity * (1 - avgProgress * 0.5);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Set emitter active/inactive.
135
+ * @param {Object} emitter
136
+ * @param {boolean} active
137
+ */
138
+ export function setEmitterActive(emitter, active) {
139
+ emitter.active = active;
140
+ emitter.points.visible = active;
141
+ }
142
+
143
+ /**
144
+ * Dispose a single emitter.
145
+ * @param {Object} emitter
146
+ * @param {THREE.Scene} scene
147
+ */
148
+ export function disposeEmitter(emitter, scene) {
149
+ scene.remove(emitter.points);
150
+ emitter.points.geometry.dispose();
151
+ emitter.points.material.dispose();
152
+ const idx = emitters.indexOf(emitter);
153
+ if (idx >= 0) emitters.splice(idx, 1);
154
+ }
155
+
156
+ /**
157
+ * Dispose all emitters.
158
+ * @param {THREE.Scene} scene
159
+ */
160
+ export function disposeAllEmitters(scene) {
161
+ while (emitters.length > 0) {
162
+ disposeEmitter(emitters[0], scene);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Get active emitter count (for performance monitoring).
168
+ * @returns {number}
169
+ */
170
+ export function getEmitterCount() {
171
+ return emitters.filter(e => e.active).length;
172
+ }