-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathworkerClient.js
More file actions
150 lines (132 loc) Β· 4.41 KB
/
workerClient.js
File metadata and controls
150 lines (132 loc) Β· 4.41 KB
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import {
GRID_W,
GRID_H,
buildingAt,
mapSettings,
appState,
elevations,
cubes,
extrusions,
lemmings,
} from './state.js';
import {
uploadElevations,
rebuildBuildingInstances,
rebuildCubeBuffers,
canvas,
} from './renderer.js';
import { getTileScreenPos } from './selectionTools.js';
import { saveMapToLocal } from './storage.js';
export const worker = new Worker('lemmingWorker.js');
export let workerBusy = false;
export function setWorkerBusy(value) { workerBusy = value }
export let currentSyncId = 0;
export function postTick(dtLemming) {
if (dtLemming > 0 && !workerBusy) {
setWorkerBusy(true);
worker.postMessage({ type: 'tick', dt: dtLemming });
}
}
worker.onmessage = (e) => {
const msg = e.data;
if (msg.type === 'tick_result') {
workerBusy = false;
// Drop stale results from before a map reset/sync
if (msg.syncId !== currentSyncId) return;
lemmings.length = 0;
lemmings.push(...msg.lemmings);
if (msg.terrainChanged) {
elevations.set(msg.elevations);
uploadElevations();
}
if (msg.buildingsChanged) {
buildingAt.set(msg.buildingAt);
rebuildBuildingInstances();
}
if (msg.needsBufferRebuild) {
cubes.length = 0;
cubes.push(...msg.cubes);
rebuildCubeBuffers();
}
if (msg.terrainChanged || msg.buildingsChanged || msg.needsBufferRebuild) {
saveMapToLocal(true);
}
} else if(['true_love', 'rejection', 'birth', 'death', 'party_pooper'].includes(msg.type)) {
console.info(msg);
appState.eventNotifications && spawnEventEffect(msg);
}
};
function spawnEventEffect(msg) {
// Convert the tile coordinates where it happened to screen space
const tx = Math.floor(msg.lem.x);
const ty = Math.floor(msg.lem.y);
const [sx, sy] = getTileScreenPos(tx, ty);
// Map internal canvas buffer coordinates perfectly to CSS logical pixels
const cssX = (sx / canvas.width) * canvas.clientWidth;
const cssY = (sy / canvas.height) * canvas.clientHeight;
const container = document.createElement('div');
container.className = 'event-effect-container';
container.style.left = cssX + 'px';
container.style.top = cssY + 'px';
// Create the main text
const text = document.createElement('div');
text.className = `event-text ${msg.type}`;
let emojiChar;
if (msg.type === 'true_love') {
text.innerHTML = `π True Love! π<br>${msg.lem.id} & ${msg.other.id}`;
emojiChar = 'π';
} else if (msg.type === 'birth') {
text.innerHTML = `πΌ Newborn! πΌ<br>${msg.lem.id}`;
emojiChar = 'πΌ';
} else if (msg.type === 'death') {
text.innerHTML = `πͺ¦ RIP πͺ¦<br>${msg.lem.id} at age ${Math.floor(msg.lem.age)}`;
emojiChar = 'π';
} else if (msg.type === 'party_pooper') {
text.innerHTML = `π© Party Pooper! π©<br>${msg.other.id} stopped ${msg.lem.id}`;
emojiChar = 'π©';
} else {
text.innerHTML = `π Rejection! π<br>${msg.lem.id} & ${msg.other.id}`;
emojiChar = 'π';
}
container.appendChild(text);
// Spawn the exploding emojis
for (let i = 0; i < 8; i++) {
const emoji = document.createElement('div');
emoji.className = 'event-emoji';
emoji.textContent = emojiChar;
// Calculate a random explosion trajectory
const angle = Math.random() * Math.PI * 2;
const dist = 40 + Math.random() * 60;
const tx = Math.cos(angle) * dist;
const ty = Math.sin(angle) * dist - 40; // Bias slightly upwards
emoji.style.setProperty('--tx', `${tx}px`);
emoji.style.setProperty('--ty', `${ty}px`);
container.appendChild(emoji);
}
document.body.appendChild(container);
// Clean up the DOM element after the animation finishes
setTimeout(() => container.remove(), 3000);
}
export function syncWorkerState() {
currentSyncId++;
worker.postMessage({
type: 'sync',
syncId: currentSyncId,
GRID_W, GRID_H,
elevations, buildingAt, mapSettings,
extrusions, cubes, lemmings,
enableReproduction: appState.enableReproduction,
simParams: {
loveChance: appState.loveChance,
ageGapPenalty: appState.ageGapPenalty,
babyChance: appState.babyChance,
babyCooldown: appState.babyCooldown,
maxBirthAge: appState.maxBirthAge,
deathAge: appState.deathAge,
deathChance: appState.deathChance,
maxAdditions: appState.maxAdditions,
enableDestressShocks: appState.enableDestressShocks,
enableDanceSmoothing: appState.enableDanceSmoothing,
},
});
}