@plasius/renderer 1.0.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.
- package/CHANGELOG.md +59 -0
- package/CODE_OF_CONDUCT.md +79 -0
- package/CONTRIBUTORS.md +27 -0
- package/LICENSE +203 -0
- package/README.md +70 -0
- package/SECURITY.md +17 -0
- package/dist/adaptivedpr.d.ts +2 -0
- package/dist/adaptivedpr.d.ts.map +1 -0
- package/dist/adaptivedpr.js +65 -0
- package/dist/camera/cameraRigProfile.d.ts +12 -0
- package/dist/camera/cameraRigProfile.d.ts.map +1 -0
- package/dist/camera/cameraRigProfile.js +18 -0
- package/dist/camera/managedCameraController.d.ts +49 -0
- package/dist/camera/managedCameraController.d.ts.map +1 -0
- package/dist/camera/managedCameraController.js +271 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/landscape.d.ts +2 -0
- package/dist/landscape.d.ts.map +1 -0
- package/dist/landscape.js +120 -0
- package/dist/player/player.d.ts +8 -0
- package/dist/player/player.d.ts.map +1 -0
- package/dist/player/player.js +203 -0
- package/dist/player/playerstore.d.ts +205 -0
- package/dist/player/playerstore.d.ts.map +1 -0
- package/dist/player/playerstore.js +500 -0
- package/dist/renderStateProvider.d.ts +57 -0
- package/dist/renderStateProvider.d.ts.map +1 -0
- package/dist/renderStateProvider.js +50 -0
- package/dist/renderer.d.ts +9 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +165 -0
- package/dist/scene.d.ts +7 -0
- package/dist/scene.d.ts.map +1 -0
- package/dist/scene.js +10 -0
- package/dist/shaders/fragment/landscapeFragmentShader.js +141 -0
- package/dist/shaders/landscapeShader.d.ts +13 -0
- package/dist/shaders/landscapeShader.d.ts.map +1 -0
- package/dist/shaders/landscapeShader.js +25 -0
- package/dist/shaders/vertex/landscapeVertexShader.js +67 -0
- package/dist/styles/renderer.module.css +90 -0
- package/dist/worldSpaceCompositor.d.ts +50 -0
- package/dist/worldSpaceCompositor.d.ts.map +1 -0
- package/dist/worldSpaceCompositor.js +159 -0
- package/dist/xr/rendererXrBridge.d.ts +12 -0
- package/dist/xr/rendererXrBridge.d.ts.map +1 -0
- package/dist/xr/rendererXrBridge.js +17 -0
- package/docs/adrs/adr-0001-renderer-package-scope.md +21 -0
- package/docs/adrs/adr-0002-public-repo-governance.md +24 -0
- package/docs/adrs/adr-0003-world-space-compositor-contracts.md +34 -0
- package/docs/adrs/adr-template.md +35 -0
- package/docs/design/0001-public-package-scope.md +18 -0
- package/docs/tdrs/index.md +3 -0
- package/docs/tdrs/tdr-0001-renderer-public-package-standards-alignment.md +19 -0
- package/legal/CLA-REGISTRY.csv +1 -0
- package/legal/CLA.md +22 -0
- package/legal/CORPORATE_CLA.md +57 -0
- package/legal/INDIVIDUAL_CLA.md +91 -0
- package/package.json +117 -0
- package/src/adaptivedpr.tsx +74 -0
- package/src/camera/cameraRigProfile.ts +29 -0
- package/src/camera/managedCameraController.tsx +401 -0
- package/src/global.d.ts +10 -0
- package/src/index.ts +3 -0
- package/src/landscape.tsx +321 -0
- package/src/player/player.tsx +257 -0
- package/src/player/playerstore.tsx +733 -0
- package/src/renderStateProvider.tsx +121 -0
- package/src/renderer.tsx +294 -0
- package/src/scene.tsx +42 -0
- package/src/shaders/fragment/landscapeFragmentShader.d.ts +4 -0
- package/src/shaders/fragment/landscapeFragmentShader.js +141 -0
- package/src/shaders/landscapeShader.tsx +39 -0
- package/src/shaders/vertex/landscapeVertexShader.d.ts +4 -0
- package/src/shaders/vertex/landscapeVertexShader.js +67 -0
- package/src/styles/renderer.module.css +90 -0
- package/src/worldSpaceCompositor.ts +265 -0
- package/src/xr/rendererXrBridge.ts +44 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import { createScopedStoreContext } from "@plasius/react-state";
|
|
2
|
+
export const defaultAttributesBase = {
|
|
3
|
+
physical: { strength: 33, dexterity: 33, endurance: 33 },
|
|
4
|
+
mental: { intellect: 33, willpower: 33, creativity: 33 },
|
|
5
|
+
spiritual: { spirit: 33, wisdom: 33, charisma: 33 },
|
|
6
|
+
};
|
|
7
|
+
export const zeroFlat = {
|
|
8
|
+
strength: 0,
|
|
9
|
+
dexterity: 0,
|
|
10
|
+
endurance: 0,
|
|
11
|
+
intellect: 0,
|
|
12
|
+
willpower: 0,
|
|
13
|
+
creativity: 0,
|
|
14
|
+
spirit: 0,
|
|
15
|
+
wisdom: 0,
|
|
16
|
+
charisma: 0,
|
|
17
|
+
};
|
|
18
|
+
export const defaultState = {
|
|
19
|
+
position: { x: 0, y: 1.6, z: 0 },
|
|
20
|
+
lookAt: { x: 0, y: 1.6, z: -1 },
|
|
21
|
+
velocity: { x: 0, y: 0, z: 0 },
|
|
22
|
+
attributesBase: { ...defaultAttributesBase },
|
|
23
|
+
attributesGear: { ...zeroFlat },
|
|
24
|
+
attributesEffects: { ...zeroFlat },
|
|
25
|
+
resources: { health: 100, healthMax: 100, energy: 100, energyMax: 100 },
|
|
26
|
+
items: {},
|
|
27
|
+
inventory: [],
|
|
28
|
+
inventoryCapacity: 24,
|
|
29
|
+
equipment: {},
|
|
30
|
+
skills: {},
|
|
31
|
+
effects: {},
|
|
32
|
+
lastActiveAt: undefined,
|
|
33
|
+
};
|
|
34
|
+
// ====== Helpers ======
|
|
35
|
+
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
|
|
36
|
+
function stackableCount(item) {
|
|
37
|
+
if (!item)
|
|
38
|
+
return 1;
|
|
39
|
+
if (!item.stackable)
|
|
40
|
+
return 1;
|
|
41
|
+
return item.maxStack ?? 99;
|
|
42
|
+
}
|
|
43
|
+
function addToInventory(state, itemId, qty) {
|
|
44
|
+
if (qty <= 0)
|
|
45
|
+
return state;
|
|
46
|
+
const item = state.items[itemId];
|
|
47
|
+
const maxStack = stackableCount(item);
|
|
48
|
+
let remaining = qty;
|
|
49
|
+
// Fill existing stacks first
|
|
50
|
+
const inv = state.inventory.map((slot) => {
|
|
51
|
+
if (remaining === 0)
|
|
52
|
+
return slot;
|
|
53
|
+
if (slot.itemId !== itemId)
|
|
54
|
+
return slot;
|
|
55
|
+
const space = maxStack - slot.qty;
|
|
56
|
+
if (space <= 0)
|
|
57
|
+
return slot;
|
|
58
|
+
const add = Math.min(space, remaining);
|
|
59
|
+
remaining -= add;
|
|
60
|
+
return { ...slot, qty: slot.qty + add };
|
|
61
|
+
});
|
|
62
|
+
// Create new stacks while capacity allows
|
|
63
|
+
const result = [...inv];
|
|
64
|
+
while (remaining > 0 && result.length < state.inventoryCapacity) {
|
|
65
|
+
const add = Math.min(remaining, maxStack);
|
|
66
|
+
result.push({ itemId, qty: add });
|
|
67
|
+
remaining -= add;
|
|
68
|
+
}
|
|
69
|
+
return { ...state, inventory: result };
|
|
70
|
+
}
|
|
71
|
+
function removeFromInventory(state, itemId, qty) {
|
|
72
|
+
if (qty <= 0)
|
|
73
|
+
return state;
|
|
74
|
+
let remaining = qty;
|
|
75
|
+
const result = [];
|
|
76
|
+
for (const slot of state.inventory) {
|
|
77
|
+
if (slot.itemId !== itemId) {
|
|
78
|
+
result.push(slot);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (remaining === 0) {
|
|
82
|
+
result.push(slot);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (slot.qty > remaining) {
|
|
86
|
+
result.push({ itemId, qty: slot.qty - remaining });
|
|
87
|
+
remaining = 0;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
remaining -= slot.qty; // drop this slot
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { ...state, inventory: result };
|
|
94
|
+
}
|
|
95
|
+
function clampAttr(n) {
|
|
96
|
+
return Math.max(1, Math.min(49, Math.round(n)));
|
|
97
|
+
}
|
|
98
|
+
function sum3(a, b, c) {
|
|
99
|
+
return a + b + c;
|
|
100
|
+
}
|
|
101
|
+
function normalizeTriangle(a, b, c) {
|
|
102
|
+
// Ensure sum=99 and each in [1,49]
|
|
103
|
+
let x = clampAttr(a), y = clampAttr(b), z = clampAttr(c);
|
|
104
|
+
let total = x + y + z;
|
|
105
|
+
if (total === 99)
|
|
106
|
+
return [x, y, z];
|
|
107
|
+
// Scale to 99, then clamp again, then adjust leftovers greedily
|
|
108
|
+
const scale = 99 / total;
|
|
109
|
+
x = clampAttr(x * scale);
|
|
110
|
+
y = clampAttr(y * scale);
|
|
111
|
+
z = clampAttr(z * scale);
|
|
112
|
+
total = x + y + z;
|
|
113
|
+
// Distribute deficit/excess (typed tuples + stable resorting)
|
|
114
|
+
let order = [
|
|
115
|
+
["x", x],
|
|
116
|
+
["y", y],
|
|
117
|
+
["z", z],
|
|
118
|
+
];
|
|
119
|
+
const sortDesc = () => {
|
|
120
|
+
order.sort((a, b) => b[1] - a[1]);
|
|
121
|
+
};
|
|
122
|
+
sortDesc();
|
|
123
|
+
while (total !== 99) {
|
|
124
|
+
if (total > 99) {
|
|
125
|
+
// reduce the largest above 1
|
|
126
|
+
for (const [k, v] of order) {
|
|
127
|
+
if (v > 1) {
|
|
128
|
+
if (k === "x")
|
|
129
|
+
x--;
|
|
130
|
+
else if (k === "y")
|
|
131
|
+
y--;
|
|
132
|
+
else
|
|
133
|
+
z--;
|
|
134
|
+
total--;
|
|
135
|
+
// update order values and resort
|
|
136
|
+
if (k === "x")
|
|
137
|
+
order[0][1] = x;
|
|
138
|
+
else if (k === "y")
|
|
139
|
+
order[1][1] = y;
|
|
140
|
+
else
|
|
141
|
+
order[2][1] = z;
|
|
142
|
+
sortDesc();
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// increase the smallest below 49
|
|
149
|
+
for (const [k, v] of [...order].reverse()) {
|
|
150
|
+
if (v < 49) {
|
|
151
|
+
if (k === "x")
|
|
152
|
+
x++;
|
|
153
|
+
else if (k === "y")
|
|
154
|
+
y++;
|
|
155
|
+
else
|
|
156
|
+
z++;
|
|
157
|
+
total++;
|
|
158
|
+
// update order values and resort
|
|
159
|
+
if (k === "x")
|
|
160
|
+
order[0][1] = x;
|
|
161
|
+
else if (k === "y")
|
|
162
|
+
order[1][1] = y;
|
|
163
|
+
else
|
|
164
|
+
order[2][1] = z;
|
|
165
|
+
sortDesc();
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return [x, y, z];
|
|
172
|
+
}
|
|
173
|
+
function flattenBase(base) {
|
|
174
|
+
const [S, D, E] = normalizeTriangle(base.physical.strength, base.physical.dexterity, base.physical.endurance);
|
|
175
|
+
const [I, W, C] = normalizeTriangle(base.mental.intellect, base.mental.willpower, base.mental.creativity);
|
|
176
|
+
const [Sp, Wi, Ch] = normalizeTriangle(base.spiritual.spirit, base.spiritual.wisdom, base.spiritual.charisma);
|
|
177
|
+
return {
|
|
178
|
+
strength: S,
|
|
179
|
+
dexterity: D,
|
|
180
|
+
endurance: E,
|
|
181
|
+
intellect: I,
|
|
182
|
+
willpower: W,
|
|
183
|
+
creativity: C,
|
|
184
|
+
spirit: Sp,
|
|
185
|
+
wisdom: Wi,
|
|
186
|
+
charisma: Ch,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// === Skill-side cross-triangle modifiers ===
|
|
190
|
+
function lnContribution(x, s = 25) {
|
|
191
|
+
return Math.log(1 + Math.max(0, x) / s); // safe-guard
|
|
192
|
+
}
|
|
193
|
+
function emptyFlat() {
|
|
194
|
+
return {
|
|
195
|
+
strength: 0,
|
|
196
|
+
dexterity: 0,
|
|
197
|
+
endurance: 0,
|
|
198
|
+
intellect: 0,
|
|
199
|
+
willpower: 0,
|
|
200
|
+
creativity: 0,
|
|
201
|
+
spirit: 0,
|
|
202
|
+
wisdom: 0,
|
|
203
|
+
charisma: 0,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// Map rare/hidden skills to cross-triangle penalties (and potential future bonuses) using ln scaling
|
|
207
|
+
// Data-driven skill → attribute modifier map (multiplier per ln unit)
|
|
208
|
+
const SKILL_ATTR_MODS = {
|
|
209
|
+
shadowstep: { charisma: -2.0 },
|
|
210
|
+
dreamwalking: { wisdom: -1.0, charisma: -1.0 },
|
|
211
|
+
soulbinding: { creativity: -1.5, endurance: -1.5 },
|
|
212
|
+
chronomancy: { endurance: -1.5, spirit: -0.5 },
|
|
213
|
+
astral_projection: { strength: -1.0, endurance: -1.0 },
|
|
214
|
+
bloodcraft: { charisma: -2.0, spirit: -2.0 },
|
|
215
|
+
telepathy: { charisma: -0.5, wisdom: -0.5 },
|
|
216
|
+
telekinesis: { endurance: -0.5 },
|
|
217
|
+
possession: { spirit: -2.5, charisma: -2.5 },
|
|
218
|
+
};
|
|
219
|
+
function addFlatInto(dst, src) {
|
|
220
|
+
dst.strength += src.strength;
|
|
221
|
+
dst.dexterity += src.dexterity;
|
|
222
|
+
dst.endurance += src.endurance;
|
|
223
|
+
dst.intellect += src.intellect;
|
|
224
|
+
dst.willpower += src.willpower;
|
|
225
|
+
dst.creativity += src.creativity;
|
|
226
|
+
dst.spirit += src.spirit;
|
|
227
|
+
dst.wisdom += src.wisdom;
|
|
228
|
+
dst.charisma += src.charisma;
|
|
229
|
+
}
|
|
230
|
+
function skillAttributeModifiers(state) {
|
|
231
|
+
const out = emptyFlat();
|
|
232
|
+
for (const s of Object.values(state.skills)) {
|
|
233
|
+
if (!s || !(s.active ?? false) || (s.level ?? 0) <= 0)
|
|
234
|
+
continue;
|
|
235
|
+
const lnP = lnContribution(s.level ?? 0, 25);
|
|
236
|
+
const mods = SKILL_ATTR_MODS[s.id.toLowerCase()];
|
|
237
|
+
if (!mods)
|
|
238
|
+
continue;
|
|
239
|
+
for (const [k, mult] of Object.entries(mods)) {
|
|
240
|
+
const key = k;
|
|
241
|
+
const delta = Math.trunc(mult * lnP);
|
|
242
|
+
out[key] = (out[key] ?? 0) + delta;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
247
|
+
function effectiveAttributes(state) {
|
|
248
|
+
// Start with normalized base
|
|
249
|
+
const base = flattenBase(state.attributesBase);
|
|
250
|
+
// Add gear/effects layers
|
|
251
|
+
const eff = { ...base };
|
|
252
|
+
const addFlat = (src) => {
|
|
253
|
+
eff.strength += src.strength;
|
|
254
|
+
eff.dexterity += src.dexterity;
|
|
255
|
+
eff.endurance += src.endurance;
|
|
256
|
+
eff.intellect += src.intellect;
|
|
257
|
+
eff.willpower += src.willpower;
|
|
258
|
+
eff.creativity += src.creativity;
|
|
259
|
+
eff.spirit += src.spirit;
|
|
260
|
+
eff.wisdom += src.wisdom;
|
|
261
|
+
eff.charisma += src.charisma;
|
|
262
|
+
};
|
|
263
|
+
addFlat(state.attributesGear);
|
|
264
|
+
addFlat(state.attributesEffects);
|
|
265
|
+
// Add equipment modifiers
|
|
266
|
+
for (const slot of Object.keys(state.equipment)) {
|
|
267
|
+
const itemId = state.equipment[slot];
|
|
268
|
+
if (!itemId)
|
|
269
|
+
continue;
|
|
270
|
+
const item = state.items[itemId];
|
|
271
|
+
if (item?.modifiers) {
|
|
272
|
+
for (const [k, v] of Object.entries(item.modifiers)) {
|
|
273
|
+
const key = k;
|
|
274
|
+
const val = v ?? 0;
|
|
275
|
+
eff[key] = (eff[key] ?? 0) + val;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Add status effects
|
|
280
|
+
for (const effx of Object.values(state.effects)) {
|
|
281
|
+
const mods = effx.modifiers?.attributes;
|
|
282
|
+
if (!mods)
|
|
283
|
+
continue;
|
|
284
|
+
for (const [k, v] of Object.entries(mods)) {
|
|
285
|
+
const key = k;
|
|
286
|
+
const val = v ?? 0;
|
|
287
|
+
eff[key] = (eff[key] ?? 0) + val;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Apply cross-triangle penalties/bonuses derived from active skills (rare/hidden)
|
|
291
|
+
const skillMods = skillAttributeModifiers(state);
|
|
292
|
+
addFlatInto(eff, skillMods);
|
|
293
|
+
return eff;
|
|
294
|
+
}
|
|
295
|
+
// ====== Reducer ======
|
|
296
|
+
function reducer(state, action) {
|
|
297
|
+
switch (action.type) {
|
|
298
|
+
case "set_position":
|
|
299
|
+
return { ...state, position: { ...action.payload } };
|
|
300
|
+
case "set_look_at":
|
|
301
|
+
return { ...state, lookAt: { ...action.payload } };
|
|
302
|
+
case "set_velocity":
|
|
303
|
+
return { ...state, velocity: { ...action.payload } };
|
|
304
|
+
case "set_resource": {
|
|
305
|
+
const next = { ...state.resources, ...action.payload };
|
|
306
|
+
next.health = clamp(next.health, 0, next.healthMax);
|
|
307
|
+
next.energy = clamp(next.energy, 0, next.energyMax);
|
|
308
|
+
return { ...state, resources: next };
|
|
309
|
+
}
|
|
310
|
+
case "set_attributes_base": {
|
|
311
|
+
const next = { ...state.attributesBase };
|
|
312
|
+
const apply = (k, v) => {
|
|
313
|
+
switch (k) {
|
|
314
|
+
case "strength":
|
|
315
|
+
next.physical.strength = v;
|
|
316
|
+
break;
|
|
317
|
+
case "dexterity":
|
|
318
|
+
next.physical.dexterity = v;
|
|
319
|
+
break;
|
|
320
|
+
case "endurance":
|
|
321
|
+
next.physical.endurance = v;
|
|
322
|
+
break;
|
|
323
|
+
case "intellect":
|
|
324
|
+
next.mental.intellect = v;
|
|
325
|
+
break;
|
|
326
|
+
case "willpower":
|
|
327
|
+
next.mental.willpower = v;
|
|
328
|
+
break;
|
|
329
|
+
case "creativity":
|
|
330
|
+
next.mental.creativity = v;
|
|
331
|
+
break;
|
|
332
|
+
case "spirit":
|
|
333
|
+
next.spiritual.spirit = v;
|
|
334
|
+
break;
|
|
335
|
+
case "wisdom":
|
|
336
|
+
next.spiritual.wisdom = v;
|
|
337
|
+
break;
|
|
338
|
+
case "charisma":
|
|
339
|
+
next.spiritual.charisma = v;
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
for (const [k, v] of Object.entries(action.payload))
|
|
344
|
+
apply(k, clampAttr(v));
|
|
345
|
+
return { ...state, attributesBase: next };
|
|
346
|
+
}
|
|
347
|
+
case "set_attributes_gear": {
|
|
348
|
+
const next = {
|
|
349
|
+
...state.attributesGear,
|
|
350
|
+
};
|
|
351
|
+
for (const [k, v] of Object.entries(action.payload))
|
|
352
|
+
next[k] = (next[k] ?? 0) + v;
|
|
353
|
+
return { ...state, attributesGear: next };
|
|
354
|
+
}
|
|
355
|
+
case "set_attributes_effects": {
|
|
356
|
+
const next = {
|
|
357
|
+
...state.attributesEffects,
|
|
358
|
+
};
|
|
359
|
+
for (const [k, v] of Object.entries(action.payload))
|
|
360
|
+
next[k] = (next[k] ?? 0) + v;
|
|
361
|
+
return { ...state, attributesEffects: next };
|
|
362
|
+
}
|
|
363
|
+
case "register_item": {
|
|
364
|
+
const it = action.payload;
|
|
365
|
+
return { ...state, items: { ...state.items, [it.id]: it } };
|
|
366
|
+
}
|
|
367
|
+
case "add_item":
|
|
368
|
+
return addToInventory(state, action.payload.itemId, action.payload.qty);
|
|
369
|
+
case "remove_item":
|
|
370
|
+
return removeFromInventory(state, action.payload.itemId, action.payload.qty);
|
|
371
|
+
case "equip": {
|
|
372
|
+
const { slot, itemId } = action.payload;
|
|
373
|
+
// Ensure we have the item and it exists in inventory or is otherwise obtainable
|
|
374
|
+
if (!state.items[itemId])
|
|
375
|
+
return state;
|
|
376
|
+
return { ...state, equipment: { ...state.equipment, [slot]: itemId } };
|
|
377
|
+
}
|
|
378
|
+
case "unequip": {
|
|
379
|
+
const { slot } = action.payload;
|
|
380
|
+
const eq = { ...state.equipment };
|
|
381
|
+
delete eq[slot];
|
|
382
|
+
return { ...state, equipment: eq };
|
|
383
|
+
}
|
|
384
|
+
case "learn_skill": {
|
|
385
|
+
const s = action.payload;
|
|
386
|
+
return { ...state, skills: { ...state.skills, [s.id]: s } };
|
|
387
|
+
}
|
|
388
|
+
case "set_skill_active": {
|
|
389
|
+
const { id, active } = action.payload;
|
|
390
|
+
const prev = state.skills[id];
|
|
391
|
+
if (!prev)
|
|
392
|
+
return state;
|
|
393
|
+
return {
|
|
394
|
+
...state,
|
|
395
|
+
skills: { ...state.skills, [id]: { ...prev, active } },
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
case "set_skill_level": {
|
|
399
|
+
const { id, level } = action.payload;
|
|
400
|
+
const prev = state.skills[id];
|
|
401
|
+
if (!prev)
|
|
402
|
+
return state;
|
|
403
|
+
return {
|
|
404
|
+
...state,
|
|
405
|
+
skills: {
|
|
406
|
+
...state.skills,
|
|
407
|
+
[id]: {
|
|
408
|
+
...prev,
|
|
409
|
+
level: Math.max(0, Math.min(100, Math.round(level))),
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
case "forget_skill": {
|
|
415
|
+
const { id } = action.payload;
|
|
416
|
+
const next = { ...state.skills };
|
|
417
|
+
delete next[id];
|
|
418
|
+
return { ...state, skills: next };
|
|
419
|
+
}
|
|
420
|
+
case "apply_effect": {
|
|
421
|
+
const e = action.payload;
|
|
422
|
+
return { ...state, effects: { ...state.effects, [e.id]: e } };
|
|
423
|
+
}
|
|
424
|
+
case "remove_effect": {
|
|
425
|
+
const { id } = action.payload;
|
|
426
|
+
const next = { ...state.effects };
|
|
427
|
+
delete next[id];
|
|
428
|
+
return { ...state, effects: next };
|
|
429
|
+
}
|
|
430
|
+
default:
|
|
431
|
+
return state;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
export const PlayerStore = createScopedStoreContext(reducer, defaultState);
|
|
435
|
+
// Convenience re-exports for common actions (keeps call sites tidy)
|
|
436
|
+
export const PlayerActions = {
|
|
437
|
+
setPosition: (payload) => ({
|
|
438
|
+
type: "set_position",
|
|
439
|
+
payload,
|
|
440
|
+
}),
|
|
441
|
+
setLookAt: (payload) => ({
|
|
442
|
+
type: "set_look_at",
|
|
443
|
+
payload,
|
|
444
|
+
}),
|
|
445
|
+
setVelocity: (payload) => ({
|
|
446
|
+
type: "set_velocity",
|
|
447
|
+
payload,
|
|
448
|
+
}),
|
|
449
|
+
setResource: (payload) => ({
|
|
450
|
+
type: "set_resource",
|
|
451
|
+
payload,
|
|
452
|
+
}),
|
|
453
|
+
setAttributesBase: (payload) => ({ type: "set_attributes_base", payload }),
|
|
454
|
+
setAttributesGear: (payload) => ({ type: "set_attributes_gear", payload }),
|
|
455
|
+
setAttributesEffects: (payload) => ({ type: "set_attributes_effects", payload }),
|
|
456
|
+
registerItem: (payload) => ({
|
|
457
|
+
type: "register_item",
|
|
458
|
+
payload,
|
|
459
|
+
}),
|
|
460
|
+
addItem: (itemId, qty) => ({
|
|
461
|
+
type: "add_item",
|
|
462
|
+
payload: { itemId, qty },
|
|
463
|
+
}),
|
|
464
|
+
removeItem: (itemId, qty) => ({
|
|
465
|
+
type: "remove_item",
|
|
466
|
+
payload: { itemId, qty },
|
|
467
|
+
}),
|
|
468
|
+
equip: (slot, itemId) => ({
|
|
469
|
+
type: "equip",
|
|
470
|
+
payload: { slot, itemId },
|
|
471
|
+
}),
|
|
472
|
+
unequip: (slot) => ({
|
|
473
|
+
type: "unequip",
|
|
474
|
+
payload: { slot },
|
|
475
|
+
}),
|
|
476
|
+
learnSkill: (payload) => ({
|
|
477
|
+
type: "learn_skill",
|
|
478
|
+
payload,
|
|
479
|
+
}),
|
|
480
|
+
setSkillActive: (id, active) => ({
|
|
481
|
+
type: "set_skill_active",
|
|
482
|
+
payload: { id, active },
|
|
483
|
+
}),
|
|
484
|
+
setSkillLevel: (id, level) => ({
|
|
485
|
+
type: "set_skill_level",
|
|
486
|
+
payload: { id, level },
|
|
487
|
+
}),
|
|
488
|
+
forgetSkill: (id) => ({
|
|
489
|
+
type: "forget_skill",
|
|
490
|
+
payload: { id },
|
|
491
|
+
}),
|
|
492
|
+
applyEffect: (payload) => ({
|
|
493
|
+
type: "apply_effect",
|
|
494
|
+
payload,
|
|
495
|
+
}),
|
|
496
|
+
removeEffect: (id) => ({
|
|
497
|
+
type: "remove_effect",
|
|
498
|
+
payload: { id },
|
|
499
|
+
}),
|
|
500
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createScopedStoreContext } from "@plasius/react-state";
|
|
2
|
+
import type { IState } from "@plasius/react-state";
|
|
3
|
+
import type { CameraRigProfile } from "./camera/cameraRigProfile.js";
|
|
4
|
+
type RenderAction = {
|
|
5
|
+
type: "set_camera_profile";
|
|
6
|
+
payload: CameraRigProfile;
|
|
7
|
+
} | {
|
|
8
|
+
type: "set_frame";
|
|
9
|
+
payload: number;
|
|
10
|
+
} | {
|
|
11
|
+
type: "set_last_render_time";
|
|
12
|
+
payload: number;
|
|
13
|
+
} | {
|
|
14
|
+
type: "update";
|
|
15
|
+
} | {
|
|
16
|
+
type: "reset";
|
|
17
|
+
} | {
|
|
18
|
+
type: "set_frame_rate";
|
|
19
|
+
payload: number;
|
|
20
|
+
} | {
|
|
21
|
+
type: "increment_accumulated_frames";
|
|
22
|
+
} | {
|
|
23
|
+
type: "set_performance_tier";
|
|
24
|
+
payload: "low" | "medium" | "high" | "ultra";
|
|
25
|
+
} | {
|
|
26
|
+
type: "set_is_animating";
|
|
27
|
+
payload: boolean;
|
|
28
|
+
} | {
|
|
29
|
+
type: "set_pause_render";
|
|
30
|
+
payload: boolean;
|
|
31
|
+
} | {
|
|
32
|
+
type: "set_scene_hash";
|
|
33
|
+
payload: string;
|
|
34
|
+
} | {
|
|
35
|
+
type: "set_vr_mode";
|
|
36
|
+
payload: boolean;
|
|
37
|
+
};
|
|
38
|
+
interface RenderState extends IState {
|
|
39
|
+
frame: number;
|
|
40
|
+
lastRenderTime: number;
|
|
41
|
+
debugEnabled: boolean;
|
|
42
|
+
frameRate: number;
|
|
43
|
+
accumulatedFrames: number;
|
|
44
|
+
performanceTier: "low" | "medium" | "high" | "ultra";
|
|
45
|
+
isAnimating: boolean;
|
|
46
|
+
pauseRender: boolean;
|
|
47
|
+
sceneHash: string;
|
|
48
|
+
cameraRigProfile: CameraRigProfile;
|
|
49
|
+
useVR: boolean;
|
|
50
|
+
}
|
|
51
|
+
export type RenderStoreContext = ReturnType<typeof createScopedStoreContext<RenderState, RenderAction>>;
|
|
52
|
+
export declare const RenderStore: RenderStoreContext;
|
|
53
|
+
export declare const RenderProvider: ({ children }: {
|
|
54
|
+
children: React.ReactNode;
|
|
55
|
+
}) => import("react").JSX.Element;
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=renderStateProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderStateProvider.d.ts","sourceRoot":"","sources":["../src/renderStateProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,KAAK,YAAY,GACb;IACE,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,gBAAgB,CAAC;CAC3B,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;CAChB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;CACf,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,8BAA8B,CAAC;CACtC,GACD;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;CAC9C,GACD;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB,GACD;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEN,UAAU,WAAY,SAAQ,MAAM;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACrD,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,KAAK,EAAE,OAAO,CAAC;CAChB;AA+CD,MAAM,MAAM,kBAAkB,GAAG,UAAU,CACzC,OAAO,wBAAwB,CAAC,WAAW,EAAE,YAAY,CAAC,CAC3D,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,kBACkD,CAAC;AAE7E,eAAO,MAAM,cAAc,GAAI,cAAc;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,gCAEzE,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createScopedStoreContext } from "@plasius/react-state";
|
|
3
|
+
import { VIEW_PROFILE } from "./camera/cameraRigProfile.js";
|
|
4
|
+
const initialState = {
|
|
5
|
+
frame: 0,
|
|
6
|
+
lastRenderTime: 0,
|
|
7
|
+
debugEnabled: false,
|
|
8
|
+
frameRate: 0,
|
|
9
|
+
accumulatedFrames: 0,
|
|
10
|
+
performanceTier: "high",
|
|
11
|
+
isAnimating: true,
|
|
12
|
+
pauseRender: false,
|
|
13
|
+
sceneHash: "",
|
|
14
|
+
cameraRigProfile: VIEW_PROFILE,
|
|
15
|
+
useVR: false,
|
|
16
|
+
};
|
|
17
|
+
const reducer = (state, action) => {
|
|
18
|
+
switch (action.type) {
|
|
19
|
+
case "update":
|
|
20
|
+
return { ...state };
|
|
21
|
+
case "reset":
|
|
22
|
+
return initialState;
|
|
23
|
+
case "set_frame_rate":
|
|
24
|
+
return { ...state, frameRate: action.payload };
|
|
25
|
+
case "increment_accumulated_frames":
|
|
26
|
+
return { ...state, accumulatedFrames: state.accumulatedFrames + 1 };
|
|
27
|
+
case "set_performance_tier":
|
|
28
|
+
return { ...state, performanceTier: action.payload };
|
|
29
|
+
case "set_is_animating":
|
|
30
|
+
return { ...state, isAnimating: action.payload };
|
|
31
|
+
case "set_pause_render":
|
|
32
|
+
return { ...state, pauseRender: action.payload };
|
|
33
|
+
case "set_scene_hash":
|
|
34
|
+
return { ...state, sceneHash: action.payload };
|
|
35
|
+
case "set_camera_profile":
|
|
36
|
+
return { ...state, cameraRigProfile: action.payload };
|
|
37
|
+
case "set_frame":
|
|
38
|
+
return { ...state, frame: action.payload };
|
|
39
|
+
case "set_last_render_time":
|
|
40
|
+
return { ...state, lastRenderTime: action.payload };
|
|
41
|
+
case "set_vr_mode":
|
|
42
|
+
return { ...state, useVR: action.payload };
|
|
43
|
+
default:
|
|
44
|
+
return state;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export const RenderStore = createScopedStoreContext(reducer, initialState);
|
|
48
|
+
export const RenderProvider = ({ children }) => {
|
|
49
|
+
return _jsx(RenderStore.Provider, { children: children });
|
|
50
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Vector3 } from "three";
|
|
2
|
+
export interface RendererProps {
|
|
3
|
+
cameraPosition: Vector3;
|
|
4
|
+
cameraRotation: Vector3;
|
|
5
|
+
multiview: boolean;
|
|
6
|
+
}
|
|
7
|
+
declare function WrappedRenderer(props: React.PropsWithChildren<RendererProps>): import("react").JSX.Element;
|
|
8
|
+
export { WrappedRenderer as Renderer };
|
|
9
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAc,MAAM,OAAO,CAAC;AAuDjD,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB;AAkOD,iBAAS,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,iBAAiB,CAAC,aAAa,CAAC,+BAMrE;AAED,OAAO,EAAE,eAAe,IAAI,QAAQ,EAAE,CAAC"}
|