mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-03-23 22:29:31 +00:00
input and tour improvements, bug fixes
This commit is contained in:
parent
fe90146e1e
commit
90ec7cbae2
110 changed files with 2802 additions and 1286 deletions
|
|
@ -1 +1 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{o as i}from"./react-three-fiber.esm-dhSWjERg.js";import{a}from"./SettingsProvider-xrmxG700.js";import{l as o,u as s}from"./three.module-CwgFV8Kd.js";import{r as c}from"./engineStore-Cio8vU1L.js";var l=t(),u=e(n(),1),d=r(),f=(0,u.createContext)(void 0);function p(e){let t=(0,l.c)(11),{children:n}=e,r=i(g),{audioVolume:p}=a(),_;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(_={audioLoader:null,audioListener:null},t[0]=_):_=t[0];let[v,y]=(0,u.useState)(_),b,x;t[1]===r?(b=t[2],x=t[3]):(b=()=>{let e=new s,t=r.children.find(h);t||(t=new o,r.add(t)),y({audioLoader:e,audioListener:t});let n=()=>{let e=t.context;!e||e.state!==`suspended`||e.resume().finally(()=>{document.removeEventListener(`click`,n),document.removeEventListener(`keydown`,n),document.removeEventListener(`touchend`,n)})};document.addEventListener(`click`,n),document.addEventListener(`keydown`,n),document.addEventListener(`touchend`,n);let i=c.subscribe(m,e=>{let n=t.context;n&&(e===`paused`?n.suspend():n.state===`suspended`&&n.resume())});return()=>{document.removeEventListener(`click`,n),document.removeEventListener(`keydown`,n),document.removeEventListener(`touchend`,n),i(),t&&r.remove(t)}},x=[r],t[1]=r,t[2]=b,t[3]=x),(0,u.useEffect)(b,x);let S,C;t[4]!==v.audioListener||t[5]!==p?(S=()=>{v.audioListener?.setMasterVolume(p)},C=[p,v.audioListener],t[4]=v.audioListener,t[5]=p,t[6]=S,t[7]=C):(S=t[6],C=t[7]),(0,u.useEffect)(S,C);let w;return t[8]!==v||t[9]!==n?(w=(0,d.jsx)(f.Provider,{value:v,children:n}),t[8]=v,t[9]=n,t[10]=w):w=t[10],w}function m(e){return e.playback.status}function h(e){return e instanceof o}function g(e){return e.camera}function _(){let e=(0,u.useContext)(f);if(e===void 0)throw Error(`useAudio must be used within AudioProvider`);return e}export{_ as n,p as t};
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{o as i}from"./react-three-fiber.esm-CgPHUpXo.js";import{a}from"./SettingsProvider-CCHVZuSg.js";import{l as o,u as s}from"./three.module-07hRbor4.js";import{r as c}from"./engineStore-Dkm20jvr.js";var l=t(),u=e(n(),1),d=r(),f=(0,u.createContext)(void 0);function p(e){let t=(0,l.c)(11),{children:n}=e,r=i(g),{audioVolume:p}=a(),_;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(_={audioLoader:null,audioListener:null},t[0]=_):_=t[0];let[v,y]=(0,u.useState)(_),b,x;t[1]===r?(b=t[2],x=t[3]):(b=()=>{let e=new s,t=r.children.find(h);t||(t=new o,r.add(t)),y({audioLoader:e,audioListener:t});let n=()=>{let e=t.context;!e||e.state!==`suspended`||e.resume().finally(()=>{document.removeEventListener(`click`,n),document.removeEventListener(`keydown`,n),document.removeEventListener(`touchend`,n)})};document.addEventListener(`click`,n),document.addEventListener(`keydown`,n),document.addEventListener(`touchend`,n);let i=c.subscribe(m,e=>{let n=t.context;n&&(e===`paused`?n.suspend():n.state===`suspended`&&n.resume())});return()=>{document.removeEventListener(`click`,n),document.removeEventListener(`keydown`,n),document.removeEventListener(`touchend`,n),i(),t&&r.remove(t)}},x=[r],t[1]=r,t[2]=b,t[3]=x),(0,u.useEffect)(b,x);let S,C;t[4]!==v.audioListener||t[5]!==p?(S=()=>{v.audioListener?.setMasterVolume(p)},C=[p,v.audioListener],t[4]=v.audioListener,t[5]=p,t[6]=S,t[7]=C):(S=t[6],C=t[7]),(0,u.useEffect)(S,C);let w;return t[8]!==v||t[9]!==n?(w=(0,d.jsx)(f.Provider,{value:v,children:n}),t[8]=v,t[9]=n,t[10]=w):w=t[10],w}function m(e){return e.playback.status}function h(e){return e instanceof o}function g(e){return e.camera}function _(){let e=(0,u.useContext)(f);if(e===void 0)throw Error(`useAudio must be used within AudioProvider`);return e}export{_ as n,p as t};
|
||||
|
|
@ -1 +1 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{r as t,t as n}from"./jsx-runtime-BpGWiA-R.js";import{i as r,o as i}from"./react-three-fiber.esm-dhSWjERg.js";import{a,i as o}from"./SettingsProvider-xrmxG700.js";import{t as s}from"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import{Ut as c,bt as l,c as u}from"./three.module-CwgFV8Kd.js";import"./mission-C2iDKeMo.js";import{r as d}from"./engineStore-Cio8vU1L.js";import{r as f}from"./loaders-0nekNxyt.js";import{n as p}from"./AudioContext-Dn9ry4KA.js";import{t as m}from"./FloatingLabel-D3hPNzZS.js";var h=e(t(),1),g=n(),_=s(`AudioEmitter`),v=new Map,y=new Map;function b(e,t=1){y.set(e,t)}function x(e){y.delete(e)}var S=0;function C(){return S}function w(){S++;for(let[e]of y){try{e.stop()}catch{}try{e.disconnect()}catch{}}y.clear()}d.subscribe(e=>e.playback.rate,e=>{for(let[t,n]of y)try{t.setPlaybackRate(n*e)}catch{}});function T(e,t){let n=t(e),r=n?.filename;if(!r)return null;let i=r.endsWith(`.wav`)?r:`${r}.wav`,a=n.description,o=a==null?void 0:t(a);return{filename:i,is3D:o?.is3D??!0,isLooping:o?.isLooping??!1,refDist:o?.referenceDistance??20,maxDist:o?.maxDistance??100,volume:o?.volume??1}}function E(e,t,n,r,i){let a;try{a=f(e.filename)}catch{return}let o=d.getState().playback.rate,s=S;D(a,n,n=>{if(s===S)try{if(e.is3D&&i){let a=new l(t);a.setBuffer(n),a.setDistanceModel(`inverse`),a.setRefDistance(e.refDist),a.setMaxDistance(e.maxDist),a.setRolloffFactor(1),a.setVolume(e.volume),a.setPlaybackRate(o),r&&a.position.copy(r),i.add(a),y.set(a,1),a.play(),a.source.onended=()=>{y.delete(a);try{a.disconnect()}catch{}i.remove(a)}}else{let r=new u(t);r.setBuffer(n),r.setVolume(e.volume),r.setPlaybackRate(o),y.set(r,1),r.play(),r.source.onended=()=>{y.delete(r);try{r.disconnect()}catch{}}}}catch{}})}function D(e,t,n){v.has(e)?n(v.get(e)):t.load(e,t=>{v.set(e,t),n(t)},void 0,t=>{_.error(`Audio load error %s: %o`,e,t)})}var O=(0,h.memo)(function({entity:e}){let{debugMode:t}=o(),n=e.audioFileName??``,s=e.audioVolume??1,d=e.audioMinDistance??1,_=e.audioMaxDistance??1,v=e.audioMinLoopGap??0,y=e.audioMaxLoopGap??0,b=e.audioIs3D??!0?1:0,x=e.audioIsLooping??!0,[S,C,w]=e.position??[0,0,0],T=i(e=>e.scene),E=i(e=>e.camera),{audioLoader:O,audioListener:k}=p(),{audioEnabled:A}=a(),j=(0,h.useRef)(null),M=(0,h.useRef)(null),N=(0,h.useRef)(null),P=(0,h.useRef)(!1),F=(0,h.useRef)(!1),I=(0,h.useRef)(new c(S,C,w)),L=(0,h.useRef)(0),R=()=>{M.current!=null&&(clearTimeout(M.current),M.current=null),N.current!=null&&(clearTimeout(N.current),N.current=null)},[z]=(0,h.useState)(()=>Math.random());(0,h.useEffect)(()=>{if(!O||!k)return;L.current++;let e;if(b){let t=new l(k);t.position.copy(I.current),t.setDistanceModel(`inverse`),t.setRefDistance(d),t.setMaxDistance(_),t.setRolloffFactor(1),t.setVolume(s),e=t,T.add(e)}else e=new u(k),e.setVolume(s);return j.current=e,()=>{R();try{e.stop()}catch{}try{e.disconnect()}catch{}b&&T.remove(e),j.current=null,P.current=!1,F.current=!1}},[O,k,b,d,_,s,T]);let B=(e,t)=>{if(x)if(v>0||y>0){let n=Math.max(0,v),r=Math.max(n,y),i=n===r?n:z*(r-n)+n;e.loop=!1;let a=()=>{t===L.current&&(e.isPlaying===!1?M.current=setTimeout(()=>{if(t===L.current)try{e.play(),B(e,t)}catch{}},i):N.current=setTimeout(a,100))};N.current=setTimeout(a,100)}else e.setLoop(!0)},V=(0,h.useEffectEvent)(e=>{if(!O)return;let t=L.current;if(P.current)try{e.isPlaying||(e.play(),B(e,t))}catch{}else{let r;try{r=f(n)}catch{return}D(r,O,n=>{if(t===L.current&&!e.buffer){e.setBuffer(n),P.current=!0;try{e.play(),B(e,t)}catch{}}})}});return(0,h.useEffect)(()=>{let e=j.current;!e||b||!A||!n||V(e)},[A,b,n,O,k]),r(()=>{let e=j.current;if(!e||!b||!A||!n)return;let t=E.position.distanceTo(I.current),r=F.current,i=t<=_;if(i&&!r)F.current=!0,V(e);else if(!i&&r){F.current=!1,R();try{e.stop()}catch{}}}),(0,h.useEffect)(()=>{let e=j.current;if(e&&!A){R();try{e.stop()}catch{}F.current=!1}},[A]),t?(0,g.jsxs)(`mesh`,{position:I.current,children:[(0,g.jsx)(`sphereGeometry`,{args:[d,12,12]}),(0,g.jsx)(`meshBasicMaterial`,{color:`#00ff00`,wireframe:!0,opacity:.05,transparent:!0,toneMapped:!1}),(0,g.jsx)(m,{color:`#00ff00`,position:[0,d+1,0],children:n})]}):null});export{O as AudioEmitter,v as audioBufferCache,D as getCachedAudioBuffer,C as getSoundGeneration,E as playOneShotSound,T as resolveAudioProfile,w as stopAllTrackedSounds,b as trackSound,x as untrackSound};
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{r as t,t as n}from"./jsx-runtime-BpGWiA-R.js";import{i as r,o as i}from"./react-three-fiber.esm-CgPHUpXo.js";import{a,i as o}from"./SettingsProvider-CCHVZuSg.js";import{t as s}from"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import{Ut as c,bt as l,c as u}from"./three.module-07hRbor4.js";import"./mission-D8vr00S1.js";import{r as d}from"./engineStore-Dkm20jvr.js";import{r as f}from"./loaders-VxR5Bl13.js";import{n as p}from"./AudioContext-CMp1T7r9.js";import{t as m}from"./FloatingLabel-C7nyg5oz.js";var h=e(t(),1),g=n(),_=s(`AudioEmitter`),v=new Map,y=new Map;function b(e,t=1){y.set(e,t)}function x(e){y.delete(e)}var S=0;function C(){return S}function w(){S++;for(let[e]of y){try{e.stop()}catch{}try{e.disconnect()}catch{}}y.clear()}d.subscribe(e=>e.playback.rate,e=>{for(let[t,n]of y)try{t.setPlaybackRate(n*e)}catch{}});function T(e,t){let n=t(e),r=n?.filename;if(!r)return null;let i=r.endsWith(`.wav`)?r:`${r}.wav`,a=n.description,o=a==null?void 0:t(a);return{filename:i,is3D:o?.is3D??!0,isLooping:o?.isLooping??!1,refDist:o?.referenceDistance??20,maxDist:o?.maxDistance??100,volume:o?.volume??1}}function E(e,t,n,r,i){let a;try{a=f(e.filename)}catch{return}let o=d.getState().playback.rate,s=S;D(a,n,n=>{if(s===S)try{if(e.is3D&&i){let a=new l(t);a.setBuffer(n),a.setDistanceModel(`inverse`),a.setRefDistance(e.refDist),a.setMaxDistance(e.maxDist),a.setRolloffFactor(1),a.setVolume(e.volume),a.setPlaybackRate(o),r&&a.position.copy(r),i.add(a),y.set(a,1),a.play(),a.source.onended=()=>{y.delete(a);try{a.disconnect()}catch{}i.remove(a)}}else{let r=new u(t);r.setBuffer(n),r.setVolume(e.volume),r.setPlaybackRate(o),y.set(r,1),r.play(),r.source.onended=()=>{y.delete(r);try{r.disconnect()}catch{}}}}catch{}})}function D(e,t,n){v.has(e)?n(v.get(e)):t.load(e,t=>{v.set(e,t),n(t)},void 0,t=>{_.error(`Audio load error %s: %o`,e,t)})}var O=(0,h.memo)(function({entity:e}){let{debugMode:t}=o(),n=e.audioFileName??``,s=e.audioVolume??1,d=e.audioMinDistance??1,_=e.audioMaxDistance??1,v=e.audioMinLoopGap??0,y=e.audioMaxLoopGap??0,b=e.audioIs3D??!0?1:0,x=e.audioIsLooping??!0,[S,C,w]=e.position??[0,0,0],T=i(e=>e.scene),E=i(e=>e.camera),{audioLoader:O,audioListener:k}=p(),{audioEnabled:A}=a(),j=(0,h.useRef)(null),M=(0,h.useRef)(null),N=(0,h.useRef)(null),P=(0,h.useRef)(!1),F=(0,h.useRef)(!1),I=(0,h.useRef)(new c(S,C,w)),L=(0,h.useRef)(0),R=()=>{M.current!=null&&(clearTimeout(M.current),M.current=null),N.current!=null&&(clearTimeout(N.current),N.current=null)},[z]=(0,h.useState)(()=>Math.random());(0,h.useEffect)(()=>{if(!O||!k)return;L.current++;let e;if(b){let t=new l(k);t.position.copy(I.current),t.setDistanceModel(`inverse`),t.setRefDistance(d),t.setMaxDistance(_),t.setRolloffFactor(1),t.setVolume(s),e=t,T.add(e)}else e=new u(k),e.setVolume(s);return j.current=e,()=>{R();try{e.stop()}catch{}try{e.disconnect()}catch{}b&&T.remove(e),j.current=null,P.current=!1,F.current=!1}},[O,k,b,d,_,s,T]);let B=(e,t)=>{if(x)if(v>0||y>0){let n=Math.max(0,v),r=Math.max(n,y),i=n===r?n:z*(r-n)+n;e.loop=!1;let a=()=>{t===L.current&&(e.isPlaying===!1?M.current=setTimeout(()=>{if(t===L.current)try{e.play(),B(e,t)}catch{}},i):N.current=setTimeout(a,100))};N.current=setTimeout(a,100)}else e.setLoop(!0)},V=(0,h.useEffectEvent)(e=>{if(!O)return;let t=L.current;if(P.current)try{e.isPlaying||(e.play(),B(e,t))}catch{}else{let r;try{r=f(n)}catch{return}D(r,O,n=>{if(t===L.current&&!e.buffer){e.setBuffer(n),P.current=!0;try{e.play(),B(e,t)}catch{}}})}});return(0,h.useEffect)(()=>{let e=j.current;!e||b||!A||!n||V(e)},[A,b,n,O,k]),r(()=>{let e=j.current;if(!e||!b||!A||!n)return;let t=E.position.distanceTo(I.current),r=F.current,i=t<=_;if(i&&!r)F.current=!0,V(e);else if(!i&&r){F.current=!1,R();try{e.stop()}catch{}}}),(0,h.useEffect)(()=>{let e=j.current;if(e&&!A){R();try{e.stop()}catch{}F.current=!1}},[A]),t?(0,g.jsxs)(`mesh`,{position:I.current,children:[(0,g.jsx)(`sphereGeometry`,{args:[d,12,12]}),(0,g.jsx)(`meshBasicMaterial`,{color:`#00ff00`,wireframe:!0,opacity:.05,transparent:!0,toneMapped:!1}),(0,g.jsx)(m,{color:`#00ff00`,position:[0,d+1,0],children:n})]}):null});export{O as AudioEmitter,v as audioBufferCache,D as getCachedAudioBuffer,C as getSoundGeneration,E as playOneShotSound,T as resolveAudioProfile,w as stopAllTrackedSounds,b as trackSound,x as untrackSound};
|
||||
|
|
@ -1 +1 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import"./streamHelpers-Be29sBCp.js";import"./scene-C7IIl-c0.js";import"./mission-C2iDKeMo.js";import{g as i}from"./index-BMiY1uIn.js";var a=t(),o=e(n(),1),s={InputForm:`_InputForm_18kom_1`,Input:`_Input_18kom_1`},c=r();function l(){let e=(0,a.c)(8),[t,n]=(0,o.useState)(``),r;e[0]===t?r=e[1]:(r=e=>{e.preventDefault();let r=t.trim();r&&(i.getState().sendCommand(`messageSent`,r),n(``))},e[0]=t,e[1]=r);let l=r,f;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(f=e=>n(e.target.value),e[2]=f):f=e[2];let p;e[3]===t?p=e[4]:(p=(0,c.jsx)(`input`,{className:s.Input,type:`text`,placeholder:`Say something…`,value:t,onChange:f,onKeyDown:d,onKeyUp:u,maxLength:255}),e[3]=t,e[4]=p);let m;return e[5]!==l||e[6]!==p?(m=(0,c.jsx)(`form`,{className:s.InputForm,onSubmit:l,children:p}),e[5]=l,e[6]=p,e[7]=m):m=e[7],m}function u(e){return e.stopPropagation()}function d(e){return e.stopPropagation()}export{l as ChatInput};
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import"./streamHelpers-AIec78DP.js";import"./scene-BdOVRsxo.js";import"./mission-D8vr00S1.js";import{O as i}from"./index-DBtsNu05.js";var a=t(),o=e(n(),1),s={InputForm:`_InputForm_18kom_1`,Input:`_Input_18kom_1`},c=r();function l(){let e=(0,a.c)(8),[t,n]=(0,o.useState)(``),r;e[0]===t?r=e[1]:(r=e=>{e.preventDefault();let r=t.trim();r&&(i.getState().sendCommand(`messageSent`,r),n(``))},e[0]=t,e[1]=r);let l=r,f;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(f=e=>n(e.target.value),e[2]=f):f=e[2];let p;e[3]===t?p=e[4]:(p=(0,c.jsx)(`input`,{className:s.Input,type:`text`,placeholder:`Say something…`,value:t,onChange:f,onKeyDown:d,onKeyUp:u,maxLength:255}),e[3]=t,e[4]=p);let m;return e[5]!==l||e[6]!==p?(m=(0,c.jsx)(`form`,{className:s.InputForm,onSubmit:l,children:p}),e[5]=l,e[6]=p,e[7]=m):m=e[7],m}function u(e){return e.stopPropagation()}function d(e){return e.stopPropagation()}export{l as ChatInput};
|
||||
|
|
@ -1 +1 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{r as t}from"./jsx-runtime-BpGWiA-R.js";import"./react-three-fiber.esm-dhSWjERg.js";import{a as n}from"./SettingsProvider-xrmxG700.js";import"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import{c as r}from"./three.module-CwgFV8Kd.js";import"./mission-C2iDKeMo.js";import{i,r as a}from"./engineStore-Cio8vU1L.js";import{r as o}from"./loaders-0nekNxyt.js";import{n as s}from"./AudioContext-Dn9ry4KA.js";import"./FloatingLabel-D3hPNzZS.js";import{getCachedAudioBuffer as c,getSoundGeneration as l,trackSound as u,untrackSound as d}from"./AudioEmitter-_djhBYQd.js";var f=e(t(),1);function p(){let{audioLoader:e,audioListener:t}=s(),{audioEnabled:p}=n(),m=i(e=>e.playback.streamSnapshot?.chatMessages),h=i(e=>e.playback.streamSnapshot?.timeSec),g=(0,f.useRef)(new WeakSet),_=(0,f.useRef)(new Map);return(0,f.useEffect)(()=>{if(!p||!e||!t||!m?.length||h==null)return;let n=g.current,i=_.current;for(let s of m)if(!n.has(s)&&(n.add(s),s.soundPath&&!(Math.abs(h-s.timeSec)>2)))try{let n=o(s.soundPath),f=s.soundPitch??1,p=a.getState().playback.rate,m=s.sender,h=l();c(n,e,e=>{if(h!==l())return;if(m){let e=i.get(m);if(e){try{e.stop()}catch{}d(e);try{e.disconnect()}catch{}i.delete(m)}}let n=new r(t);n.setBuffer(e),n.setPlaybackRate(f*p),u(n,f),m&&i.set(m,n),n.play(),n.source.onended=()=>{d(n);try{n.disconnect()}catch{}m&&i.get(m)===n&&i.delete(m)}})}catch{}},[p,e,t,m,h]),null}export{p as ChatSoundPlayer};
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{r as t}from"./jsx-runtime-BpGWiA-R.js";import"./react-three-fiber.esm-CgPHUpXo.js";import{a as n}from"./SettingsProvider-CCHVZuSg.js";import"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import{c as r}from"./three.module-07hRbor4.js";import"./mission-D8vr00S1.js";import{i,r as a}from"./engineStore-Dkm20jvr.js";import{r as o}from"./loaders-VxR5Bl13.js";import{n as s}from"./AudioContext-CMp1T7r9.js";import"./FloatingLabel-C7nyg5oz.js";import{getCachedAudioBuffer as c,getSoundGeneration as l,trackSound as u,untrackSound as d}from"./AudioEmitter-awEqEuEC.js";var f=e(t(),1);function p(){let{audioLoader:e,audioListener:t}=s(),{audioEnabled:p}=n(),m=i(e=>e.playback.streamSnapshot?.chatMessages),h=i(e=>e.playback.streamSnapshot?.timeSec),g=(0,f.useRef)(new WeakSet),_=(0,f.useRef)(new Map);return(0,f.useEffect)(()=>{if(!p||!e||!t||!m?.length||h==null)return;let n=g.current,i=_.current;for(let s of m)if(!n.has(s)&&(n.add(s),s.soundPath&&!(Math.abs(h-s.timeSec)>2)))try{let n=o(s.soundPath),f=s.soundPitch??1,p=a.getState().playback.rate,m=s.sender,h=l();c(n,e,e=>{if(h!==l())return;if(m){let e=i.get(m);if(e){try{e.stop()}catch{}d(e);try{e.disconnect()}catch{}i.delete(m)}}let n=new r(t);n.setBuffer(e),n.setPlaybackRate(f*p),u(n,f),m&&i.set(m,n),n.play(),n.source.onended=()=>{d(n);try{n.disconnect()}catch{}m&&i.get(m)===n&&i.delete(m)}})}catch{}},[p,e,t,m,h]),null}export{p as ChatSoundPlayer};
|
||||
|
|
@ -1 +1 @@
|
|||
import{r as e,t}from"./chunk-DECur_0Z.js";import{n,r,t as i}from"./jsx-runtime-BpGWiA-R.js";import{n as a,r as o}from"./react-three-fiber.esm-dhSWjERg.js";import"./traditional-BTL5qX2E.js";import{t as s}from"./Html-B3dXZ0LN.js";var c=t(((e,t)=>{(function(n,r){typeof e==`object`&&t!==void 0?t.exports=r():typeof define==`function`&&define.amd?define(r):n.Stats=r()})(e,function(){var e=function(){function t(e){return i.appendChild(e.dom),e}function n(e){for(var t=0;t<i.children.length;t++)i.children[t].style.display=t===e?`block`:`none`;r=e}var r=0,i=document.createElement(`div`);i.style.cssText=`position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000`,i.addEventListener(`click`,function(e){e.preventDefault(),n(++r%i.children.length)},!1);var a=(performance||Date).now(),o=a,s=0,c=t(new e.Panel(`FPS`,`#0ff`,`#002`)),l=t(new e.Panel(`MS`,`#0f0`,`#020`));if(self.performance&&self.performance.memory)var u=t(new e.Panel(`MB`,`#f08`,`#201`));return n(0),{REVISION:16,dom:i,addPanel:t,showPanel:n,begin:function(){a=(performance||Date).now()},end:function(){s++;var e=(performance||Date).now();if(l.update(e-a,200),e>o+1e3&&(c.update(1e3*s/(e-o),100),o=e,s=0,u)){var t=performance.memory;u.update(t.usedJSHeapSize/1048576,t.jsHeapSizeLimit/1048576)}return e},update:function(){a=this.end()},domElement:i,setMode:n}};return e.Panel=function(e,t,n){var r=1/0,i=0,a=Math.round,o=a(window.devicePixelRatio||1),s=80*o,c=48*o,l=3*o,u=2*o,d=3*o,f=15*o,p=74*o,m=30*o,h=document.createElement(`canvas`);h.width=s,h.height=c,h.style.cssText=`width:80px;height:48px`;var g=h.getContext(`2d`);return g.font=`bold `+9*o+`px Helvetica,Arial,sans-serif`,g.textBaseline=`top`,g.fillStyle=n,g.fillRect(0,0,s,c),g.fillStyle=t,g.fillText(e,l,u),g.fillRect(d,f,p,m),g.fillStyle=n,g.globalAlpha=.9,g.fillRect(d,f,p,m),{dom:h,update:function(c,_){r=Math.min(r,c),i=Math.max(i,c),g.fillStyle=n,g.globalAlpha=1,g.fillRect(0,0,s,f),g.fillStyle=t,g.fillText(a(c)+` `+e+` (`+a(r)+`-`+a(i)+`)`,l,u),g.drawImage(h,d+o,f,p-o,m,d,f,p-o,m),g.fillRect(d+p-o,f,o,m),g.fillStyle=n,g.globalAlpha=.9,g.fillRect(d+p-o,f,o,a((1-c/_)*m))}}},e})})),l=e(r());function u(e,t){typeof e==`function`?e(t):e!=null&&(e.current=t)}function d(e,t=[],n){let[r,i]=l.useState();return l.useLayoutEffect(()=>{let t=e();return i(t),u(n,t),()=>u(n,null)},t),r}var f=e(c());function p({showPanel:e=0,className:t,parent:n}){let r=d(()=>new f.default,[]);return l.useEffect(()=>{if(r){let i=n&&n.current||document.body;r.showPanel(e),i?.appendChild(r.dom);let s=(t??``).split(` `).filter(e=>e);s.length&&r.dom.classList.add(...s);let c=o(()=>r.begin()),l=a(()=>r.end());return()=>{s.length&&r.dom.classList.remove(...s),i?.removeChild(r.dom),c(),l()}}},[n,r,t,e]),null}var m=n(),h={StatsPanel:`_StatsPanel_10m5i_1`,AxisLabel:`_AxisLabel_10m5i_8`},g=i();function _(){let e=(0,m.c)(10),t=(0,l.useRef)(null),n;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(n=()=>{let e=t.current;e&&e.setColors(`rgb(153, 255, 0)`,`rgb(0, 153, 255)`,`rgb(255, 153, 0)`)},e[0]=n):n=e[0],(0,l.useEffect)(n);let r;e[1]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,g.jsx)(p,{className:h.StatsPanel}),e[1]=r):r=e[1];let i;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(i=[70],e[2]=i):i=e[2];let a;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,g.jsx)(`axesHelper`,{ref:t,args:i,renderOrder:999,children:(0,g.jsx)(`lineBasicMaterial`,{depthTest:!1,depthWrite:!1,fog:!1,vertexColors:!0})}),e[3]=a):a=e[3];let o;e[4]===Symbol.for(`react.memo_cache_sentinel`)?(o=[80,0,0],e[4]=o):o=e[4];let c;e[5]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,g.jsx)(s,{position:o,center:!0,children:(0,g.jsx)(`span`,{className:h.AxisLabel,"data-axis":`y`,children:`Y`})}),e[5]=c):c=e[5];let u;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(u=[0,80,0],e[6]=u):u=e[6];let d;e[7]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,g.jsx)(s,{position:u,center:!0,children:(0,g.jsx)(`span`,{className:h.AxisLabel,"data-axis":`z`,children:`Z`})}),e[7]=d):d=e[7];let f;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(f=[0,0,80],e[8]=f):f=e[8];let _;return e[9]===Symbol.for(`react.memo_cache_sentinel`)?(_=(0,g.jsxs)(g.Fragment,{children:[r,a,c,d,(0,g.jsx)(s,{position:f,center:!0,children:(0,g.jsx)(`span`,{className:h.AxisLabel,"data-axis":`x`,children:`X`})})]}),e[9]=_):_=e[9],_}export{_ as DebugElements};
|
||||
import{r as e,t}from"./chunk-DECur_0Z.js";import{n,r,t as i}from"./jsx-runtime-BpGWiA-R.js";import{n as a,r as o}from"./react-three-fiber.esm-CgPHUpXo.js";import"./traditional-BTL5qX2E.js";import{t as s}from"./Html-BLmmc0n9.js";var c=t(((e,t)=>{(function(n,r){typeof e==`object`&&t!==void 0?t.exports=r():typeof define==`function`&&define.amd?define(r):n.Stats=r()})(e,function(){var e=function(){function t(e){return i.appendChild(e.dom),e}function n(e){for(var t=0;t<i.children.length;t++)i.children[t].style.display=t===e?`block`:`none`;r=e}var r=0,i=document.createElement(`div`);i.style.cssText=`position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000`,i.addEventListener(`click`,function(e){e.preventDefault(),n(++r%i.children.length)},!1);var a=(performance||Date).now(),o=a,s=0,c=t(new e.Panel(`FPS`,`#0ff`,`#002`)),l=t(new e.Panel(`MS`,`#0f0`,`#020`));if(self.performance&&self.performance.memory)var u=t(new e.Panel(`MB`,`#f08`,`#201`));return n(0),{REVISION:16,dom:i,addPanel:t,showPanel:n,begin:function(){a=(performance||Date).now()},end:function(){s++;var e=(performance||Date).now();if(l.update(e-a,200),e>o+1e3&&(c.update(1e3*s/(e-o),100),o=e,s=0,u)){var t=performance.memory;u.update(t.usedJSHeapSize/1048576,t.jsHeapSizeLimit/1048576)}return e},update:function(){a=this.end()},domElement:i,setMode:n}};return e.Panel=function(e,t,n){var r=1/0,i=0,a=Math.round,o=a(window.devicePixelRatio||1),s=80*o,c=48*o,l=3*o,u=2*o,d=3*o,f=15*o,p=74*o,m=30*o,h=document.createElement(`canvas`);h.width=s,h.height=c,h.style.cssText=`width:80px;height:48px`;var g=h.getContext(`2d`);return g.font=`bold `+9*o+`px Helvetica,Arial,sans-serif`,g.textBaseline=`top`,g.fillStyle=n,g.fillRect(0,0,s,c),g.fillStyle=t,g.fillText(e,l,u),g.fillRect(d,f,p,m),g.fillStyle=n,g.globalAlpha=.9,g.fillRect(d,f,p,m),{dom:h,update:function(c,_){r=Math.min(r,c),i=Math.max(i,c),g.fillStyle=n,g.globalAlpha=1,g.fillRect(0,0,s,f),g.fillStyle=t,g.fillText(a(c)+` `+e+` (`+a(r)+`-`+a(i)+`)`,l,u),g.drawImage(h,d+o,f,p-o,m,d,f,p-o,m),g.fillRect(d+p-o,f,o,m),g.fillStyle=n,g.globalAlpha=.9,g.fillRect(d+p-o,f,o,a((1-c/_)*m))}}},e})})),l=e(r());function u(e,t){typeof e==`function`?e(t):e!=null&&(e.current=t)}function d(e,t=[],n){let[r,i]=l.useState();return l.useLayoutEffect(()=>{let t=e();return i(t),u(n,t),()=>u(n,null)},t),r}var f=e(c());function p({showPanel:e=0,className:t,parent:n}){let r=d(()=>new f.default,[]);return l.useEffect(()=>{if(r){let i=n&&n.current||document.body;r.showPanel(e),i?.appendChild(r.dom);let s=(t??``).split(` `).filter(e=>e);s.length&&r.dom.classList.add(...s);let c=o(()=>r.begin()),l=a(()=>r.end());return()=>{s.length&&r.dom.classList.remove(...s),i?.removeChild(r.dom),c(),l()}}},[n,r,t,e]),null}var m=n(),h={StatsPanel:`_StatsPanel_10m5i_1`,AxisLabel:`_AxisLabel_10m5i_8`},g=i();function _(){let e=(0,m.c)(10),t=(0,l.useRef)(null),n;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(n=()=>{let e=t.current;e&&e.setColors(`rgb(153, 255, 0)`,`rgb(0, 153, 255)`,`rgb(255, 153, 0)`)},e[0]=n):n=e[0],(0,l.useEffect)(n);let r;e[1]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,g.jsx)(p,{className:h.StatsPanel}),e[1]=r):r=e[1];let i;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(i=[70],e[2]=i):i=e[2];let a;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,g.jsx)(`axesHelper`,{ref:t,args:i,renderOrder:999,children:(0,g.jsx)(`lineBasicMaterial`,{depthTest:!1,depthWrite:!1,fog:!1,vertexColors:!0})}),e[3]=a):a=e[3];let o;e[4]===Symbol.for(`react.memo_cache_sentinel`)?(o=[80,0,0],e[4]=o):o=e[4];let c;e[5]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,g.jsx)(s,{position:o,center:!0,children:(0,g.jsx)(`span`,{className:h.AxisLabel,"data-axis":`y`,children:`Y`})}),e[5]=c):c=e[5];let u;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(u=[0,80,0],e[6]=u):u=e[6];let d;e[7]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,g.jsx)(s,{position:u,center:!0,children:(0,g.jsx)(`span`,{className:h.AxisLabel,"data-axis":`z`,children:`Z`})}),e[7]=d):d=e[7];let f;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(f=[0,0,80],e[8]=f):f=e[8];let _;return e[9]===Symbol.for(`react.memo_cache_sentinel`)?(_=(0,g.jsxs)(g.Fragment,{children:[r,a,c,d,(0,g.jsx)(s,{position:f,center:!0,children:(0,g.jsx)(`span`,{className:h.AxisLabel,"data-axis":`x`,children:`X`})})]}),e[9]=_):_=e[9],_}export{_ as DebugElements};
|
||||
|
|
@ -1 +0,0 @@
|
|||
._Root_16j0q_1{z-index:2;align-items:center;gap:10px;padding:8px 12px;font-size:13px;display:flex}._PlayPause_16j0q_10{color:#fff;cursor:pointer;background:#03529399;border:1px solid #ffffff4d;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:32px;height:32px;padding:0;font-size:14px;display:flex}@media (hover:hover){._PlayPause_16j0q_10:hover{background:#0062b3cc}}._Time_16j0q_32{font-variant-numeric:tabular-nums;white-space:nowrap;flex-shrink:0}._Seek_16j0q_38[type=range]{flex:1 1 0;min-width:0;max-width:none}._Speed_16j0q_44{color:#fff;background:#0009;border:1px solid #ffffff4d;border-radius:3px;flex-shrink:0;padding:2px 4px;font-size:12px}
|
||||
1
docs/assets/DemoPlaybackControls-BVyF8z1u.css
Normal file
1
docs/assets/DemoPlaybackControls-BVyF8z1u.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
._Root_i1hot_1{z-index:2;align-items:center;gap:10px;padding:8px 12px;font-size:13px;display:flex}._PlayPause_i1hot_10{color:#fff;cursor:pointer;background:#03529399;border:1px solid #ffffff4d;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:32px;height:32px;padding:0;font-size:11px;display:flex}@media (hover:hover){._PlayPause_i1hot_10:hover{background:#0062b3cc}}._Time_i1hot_32{font-variant-numeric:tabular-nums;white-space:nowrap;flex-shrink:0}._Seek_i1hot_38[type=range]{flex:1 1 0;min-width:0;max-width:none}._Speed_i1hot_44,._CameraMode_i1hot_45{color:#fff;background:#0009;border:1px solid #ffffff4d;border-radius:3px;flex-shrink:0;padding:3px 4px;font-size:13px}._Field_i1hot_55{align-items:center;gap:8px;display:flex}._Field_i1hot_55 label{text-transform:uppercase;color:#fff9;font-size:11px;font-weight:500}
|
||||
1
docs/assets/DemoPlaybackControls-CiHVsxjI.js
Normal file
1
docs/assets/DemoPlaybackControls-CiHVsxjI.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import"./traditional-BTL5qX2E.js";import"./engineStore-Dkm20jvr.js";import{C as i,D as a,E as o,S as s,T as c,u as l,w as u,x as d}from"./index-DBtsNu05.js";import{n as f,t as p}from"./gr-1PcjgMj0.js";var m=t(),h=e(n(),1),g={Root:`_Root_i1hot_1`,PlayPause:`_PlayPause_i1hot_10`,Time:`_Time_i1hot_32`,Seek:`_Seek_i1hot_38`,Speed:`_Speed_i1hot_44`,CameraMode:`_CameraMode_i1hot_45`,Field:`_Field_i1hot_55`},_=r();function v(e){return`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,`0`)}`}function y(){let e=(0,m.c)(42),t=o(),n=u(),r=s(),y=i(),x=a(),{play:S,pause:C,seek:w,setSpeed:T}=c(),E,D;e[0]!==n||e[1]!==C||e[2]!==S||e[3]!==t?(E=()=>{if(!t)return;let e=e=>{if(e.code!==`Space`)return;let t=e.target;t.tagName===`INPUT`||t.tagName===`TEXTAREA`||t.tagName===`SELECT`||t.tagName===`BUTTON`||t.isContentEditable||(e.preventDefault(),n?C():S())};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},D=[t,n,S,C],e[0]=n,e[1]=C,e[2]=S,e[3]=t,e[4]=E,e[5]=D):(E=e[4],D=e[5]),(0,h.useEffect)(E,D);let O;e[6]!==T||e[7]!==x?(O=()=>{let e=d.indexOf(x);e>0&&T(d[e-1])},e[6]=T,e[7]=x,e[8]=O):O=e[8],l(`decreasePlaybackSpeed`,O);let k;e[9]!==T||e[10]!==x?(k=()=>{let e=d.indexOf(x);e<d.length-1&&T(d[e+1])},e[9]=T,e[10]=x,e[11]=k):k=e[11],l(`increasePlaybackSpeed`,k);let A;e[12]===w?A=e[13]:(A=e=>{w(parseFloat(e.target.value))},e[12]=w,e[13]=A);let j=A,M;e[14]===T?M=e[15]:(M=e=>{T(parseFloat(e.target.value))},e[14]=T,e[15]=M);let N=M;if(!t||!Number.isFinite(t.duration))return null;let P=n?C:S,F=n?`Pause`:`Play`,I;e[16]===n?I=e[17]:(I=n?(0,_.jsx)(p,{}):(0,_.jsx)(f,{}),e[16]=n,e[17]=I);let L;e[18]!==P||e[19]!==F||e[20]!==I?(L=(0,_.jsx)(`button`,{className:g.PlayPause,onClick:P,"aria-label":F,autoFocus:!0,children:I}),e[18]=P,e[19]=F,e[20]=I,e[21]=L):L=e[21];let R;e[22]===r?R=e[23]:(R=v(r),e[22]=r,e[23]=R);let z;e[24]===y?z=e[25]:(z=v(y),e[24]=y,e[25]=z);let B=`${R} / ${z}`,V;e[26]===B?V=e[27]:(V=(0,_.jsx)(`span`,{className:g.Time,children:B}),e[26]=B,e[27]=V);let H;e[28]!==r||e[29]!==y||e[30]!==j?(H=(0,_.jsx)(`input`,{className:g.Seek,type:`range`,min:0,max:y,step:.01,value:r,onChange:j}),e[28]=r,e[29]=y,e[30]=j,e[31]=H):H=e[31];let U;e[32]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,_.jsx)(`label`,{htmlFor:`playbackSpeed`,children:`Speed`}),e[32]=U):U=e[32];let W;e[33]===Symbol.for(`react.memo_cache_sentinel`)?(W=d.map(b),e[33]=W):W=e[33];let G;e[34]!==N||e[35]!==x?(G=(0,_.jsxs)(`div`,{className:g.Field,children:[U,(0,_.jsx)(`select`,{id:`playbackSpeed`,className:g.Speed,value:x,onChange:N,children:W})]}),e[34]=N,e[35]=x,e[36]=G):G=e[36];let K;return e[37]!==V||e[38]!==H||e[39]!==G||e[40]!==L?(K=(0,_.jsxs)(`div`,{className:g.Root,children:[L,V,H,G]}),e[37]=V,e[38]=H,e[39]=G,e[40]=L,e[41]=K):K=e[41],K}function b(e){return(0,_.jsxs)(`option`,{value:e,children:[e,`x`]},e)}export{y as DemoPlaybackControls};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import"./traditional-BTL5qX2E.js";import"./engineStore-Cio8vU1L.js";import{d as i,f as a,h as o,m as s,p as c,u as l}from"./index-BMiY1uIn.js";var u=t(),d=e(n(),1),f={Root:`_Root_16j0q_1`,PlayPause:`_PlayPause_16j0q_10`,Time:`_Time_16j0q_32`,Seek:`_Seek_16j0q_38`,Speed:`_Speed_16j0q_44`},p=r(),m=[.25,.5,1,2,4];function h(e){return`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,`0`)}`}function g(){let e=(0,u.c)(33),t=s(),n=a(),r=l(),g=i(),x=o(),{play:S,pause:C,seek:w,setSpeed:T}=c(),E,D;e[0]!==n||e[1]!==C||e[2]!==S||e[3]!==t?(E=()=>{if(!t)return;let e=e=>{if(e.code!==`Space`)return;let t=e.target;t.tagName===`INPUT`||t.tagName===`TEXTAREA`||t.tagName===`SELECT`||t.tagName===`BUTTON`||t.isContentEditable||(e.preventDefault(),n?C():S())};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},D=[t,n,S,C],e[0]=n,e[1]=C,e[2]=S,e[3]=t,e[4]=E,e[5]=D):(E=e[4],D=e[5]),(0,d.useEffect)(E,D);let O;e[6]===w?O=e[7]:(O=e=>{w(parseFloat(e.target.value))},e[6]=w,e[7]=O);let k=O,A;e[8]===T?A=e[9]:(A=e=>{T(parseFloat(e.target.value))},e[8]=T,e[9]=A);let j=A;if(!t||!Number.isFinite(t.duration))return null;let M=n?C:S,N=n?`Pause`:`Play`,P=n?`❚❚`:`▶`,F;e[10]!==M||e[11]!==N||e[12]!==P?(F=(0,p.jsx)(`button`,{className:f.PlayPause,onClick:M,"aria-label":N,autoFocus:!0,children:P}),e[10]=M,e[11]=N,e[12]=P,e[13]=F):F=e[13];let I;e[14]===r?I=e[15]:(I=h(r),e[14]=r,e[15]=I);let L;e[16]===g?L=e[17]:(L=h(g),e[16]=g,e[17]=L);let R=`${I} / ${L}`,z;e[18]===R?z=e[19]:(z=(0,p.jsx)(`span`,{className:f.Time,children:R}),e[18]=R,e[19]=z);let B;e[20]!==r||e[21]!==g||e[22]!==k?(B=(0,p.jsx)(`input`,{className:f.Seek,type:`range`,min:0,max:g,step:.01,value:r,onChange:k}),e[20]=r,e[21]=g,e[22]=k,e[23]=B):B=e[23];let V;e[24]===Symbol.for(`react.memo_cache_sentinel`)?(V=m.map(_),e[24]=V):V=e[24];let H;e[25]!==j||e[26]!==x?(H=(0,p.jsx)(`select`,{className:f.Speed,value:x,onChange:j,children:V}),e[25]=j,e[26]=x,e[27]=H):H=e[27];let U;return e[28]!==z||e[29]!==B||e[30]!==H||e[31]!==F?(U=(0,p.jsxs)(`div`,{className:f.Root,onKeyDown:b,onPointerDown:y,onClick:v,children:[F,z,B,H]}),e[28]=z,e[29]=B,e[30]=H,e[31]=F,e[32]=U):U=e[32],U}function _(e){return(0,p.jsxs)(`option`,{value:e,children:[e,`x`]},e)}function v(e){return e.stopPropagation()}function y(e){return e.stopPropagation()}function b(e){return e.stopPropagation()}export{g as DemoPlaybackControls};
|
||||
|
|
@ -1 +1 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{i}from"./react-three-fiber.esm-dhSWjERg.js";import{Ut as a}from"./three.module-CwgFV8Kd.js";import{t as o}from"./Html-B3dXZ0LN.js";var s={Label:`_Label_8rn2m_1`},c=t(),l=e(n(),1),u=r(),d=[0,0,0],f=new a;function p(e,t,n,r){let i=e.matrixWorld.elements;return(t-i[12])*-i[8]+(n-i[13])*-i[9]+(r-i[14])*-i[10]<0}var m=(0,l.memo)(function(e){let t=(0,c.c)(11),{children:n,color:r,position:a,opacity:m}=e,h=r===void 0?`white`:r,g=a===void 0?d:a,_=m===void 0?`fadeWithDistance`:m,v=_===`fadeWithDistance`,y=(0,l.useRef)(null),[b,x]=(0,l.useState)(_!==0),S=(0,l.useRef)(null),C;t[0]!==v||t[1]!==b||t[2]!==_?(C=e=>{let{camera:t}=e,n=y.current;if(!n)return;n.getWorldPosition(f);let r=p(t,f.x,f.y,f.z);if(v){let e=r?1/0:t.position.distanceTo(f),n=e<200;if(b!==n&&x(n),S.current&&n){let t=Math.max(0,Math.min(1,1-e/200));S.current.style.opacity=t.toString()}}else{let e=!r&&_!==0;b!==e&&x(e),S.current&&(S.current.style.opacity=_.toString())}},t[0]=v,t[1]=b,t[2]=_,t[3]=C):C=t[3],i(C);let w;t[4]!==n||t[5]!==h||t[6]!==b||t[7]!==g?(w=b?(0,u.jsx)(o,{position:g,center:!0,children:(0,u.jsx)(`div`,{ref:S,className:s.Label,style:{color:h},children:n})}):null,t[4]=n,t[5]=h,t[6]=b,t[7]=g,t[8]=w):w=t[8];let T;return t[9]===w?T=t[10]:(T=(0,u.jsx)(`group`,{ref:y,children:w}),t[9]=w,t[10]=T),T});export{m as t};
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{i}from"./react-three-fiber.esm-CgPHUpXo.js";import{Ut as a}from"./three.module-07hRbor4.js";import{t as o}from"./Html-BLmmc0n9.js";var s={Label:`_Label_8rn2m_1`},c=t(),l=e(n(),1),u=r(),d=[0,0,0],f=new a;function p(e,t,n,r){let i=e.matrixWorld.elements;return(t-i[12])*-i[8]+(n-i[13])*-i[9]+(r-i[14])*-i[10]<0}var m=(0,l.memo)(function(e){let t=(0,c.c)(12),{children:n,color:r,position:a,opacity:m}=e,h=r===void 0?`white`:r,g=a===void 0?d:a,_=m===void 0?`fadeWithDistance`:m,v=_===`fadeWithDistance`,y=(0,l.useRef)(null),[b,x]=(0,l.useState)(_!==0),S=(0,l.useRef)(null),C;t[0]!==v||t[1]!==b||t[2]!==_?(C=e=>{let{camera:t}=e,n=y.current;if(!n)return;n.getWorldPosition(f);let r=p(t,f.x,f.y,f.z);if(v){let e=r?1/0:t.position.distanceTo(f),n=e<200;if(b!==n&&x(n),S.current&&n){let t=Math.max(0,Math.min(1,1-e/200));S.current.style.opacity=t.toString()}}else{let e=!r&&_!==0;b!==e&&x(e),S.current&&(S.current.style.opacity=_.toString())}},t[0]=v,t[1]=b,t[2]=_,t[3]=C):C=t[3],i(C);let w;t[4]!==n||t[5]!==h||t[6]!==v||t[7]!==b||t[8]!==g?(w=b?(0,u.jsx)(o,{position:g,center:!0,children:(0,u.jsx)(`div`,{ref:S,className:s.Label,style:{color:h,opacity:v?0:void 0},children:n})}):null,t[4]=n,t[5]=h,t[6]=v,t[7]=b,t[8]=g,t[9]=w):w=t[9];let T;return t[10]===w?T=t[11]:(T=(0,u.jsx)(`group`,{ref:y,children:w}),t[10]=w,t[11]=T),T});export{m as t};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{i}from"./react-three-fiber.esm-dhSWjERg.js";import{a}from"./SettingsProvider-xrmxG700.js";import"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import{At as o,Dt as s,Ht as c,b as l,p as u}from"./three.module-CwgFV8Kd.js";import"./mission-C2iDKeMo.js";import{t as d}from"./Texture-CUOilM1U.js";import{p as f}from"./loaders-0nekNxyt.js";import{t as p}from"./DebugSuspense-evFO3StN.js";var m=t(),h=e(n(),1),g=`
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{i}from"./react-three-fiber.esm-CgPHUpXo.js";import{a}from"./SettingsProvider-CCHVZuSg.js";import"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import{At as o,Dt as s,Ht as c,b as l,p as u}from"./three.module-07hRbor4.js";import"./mission-D8vr00S1.js";import{p as d}from"./loaders-VxR5Bl13.js";import{t as f}from"./Texture-CIsc25mc.js";import{t as p}from"./DebugSuspense-Dk4MzcWf.js";var m=t(),h=e(n(),1),g=`
|
||||
#include <fog_pars_vertex>
|
||||
|
||||
varying vec2 vUv;
|
||||
|
|
@ -72,4 +72,4 @@ void main() {
|
|||
gl_FragColor.a *= 1.0 - fogFactor;
|
||||
#endif
|
||||
}
|
||||
`;function v({textures:e,scale:t,umapping:n,vmapping:r,color:i,baseTranslucency:a}){let s=[...t].sort((e,t)=>t-e),u=new c(s[0]*n,s[1]*r),d=e[0];return new o({uniforms:{frame0:{value:d},frame1:{value:e[1]??d},frame2:{value:e[2]??d},frame3:{value:e[3]??d},frame4:{value:e[4]??d},currentFrame:{value:0},vScroll:{value:0},uvScale:{value:u},tintColor:{value:new l(...i)},opacity:{value:a},opacityFactor:{value:1},fogColor:{value:new l},fogNear:{value:1},fogFar:{value:2e3}},vertexShader:g,fragmentShader:_,transparent:!0,blending:2,side:2,depthWrite:!1,fog:!0})}var y=r();function b(e){e.wrapS=e.wrapT=s,e.colorSpace=``,e.flipY=!1,e.needsUpdate=!0}function x(e){let t=(0,m.c)(7),[n,r,i]=e,a;t[0]!==n||t[1]!==r||t[2]!==i?(a=new u(n,r,i),a.translate(n/2,r/2,i/2),t[0]=n,t[1]=r,t[2]=i,t[3]=a):a=t[3];let o=a,s,c;return t[4]===o?(s=t[5],c=t[6]):(s=()=>()=>o.dispose(),c=[o],t[4]=o,t[5]=s,t[6]=c),(0,h.useEffect)(s,c),o}function S(e){let t=(0,m.c)(10),{scale:n,color:r,baseTranslucency:i}=e,a=x(n),o;t[0]!==r[0]||t[1]!==r[1]||t[2]!==r[2]?(o=new l(r[0],r[1],r[2]),t[0]=r[0],t[1]=r[1],t[2]=r[2],t[3]=o):o=t[3];let s=o,c=i*1,u;t[4]!==s||t[5]!==c?(u=(0,y.jsx)(`meshBasicMaterial`,{color:s,transparent:!0,opacity:c,blending:2,side:2,depthWrite:!1,fog:!1}),t[4]=s,t[5]=c,t[6]=u):u=t[6];let d;return t[7]!==a||t[8]!==u?(d=(0,y.jsx)(`mesh`,{geometry:a,renderOrder:1,children:u}),t[7]=a,t[8]=u,t[9]=d):d=t[9],d}function C({scale:e,data:t}){let{animationEnabled:n}=a(),r=x(e),o=d((0,h.useMemo)(()=>t.textures.map(e=>f(e)),[t.textures]),e=>{e.forEach(e=>b(e))}),s=(0,h.useMemo)(()=>v({textures:o,scale:e,umapping:t.umapping,vmapping:t.vmapping,color:t.color,baseTranslucency:t.baseTranslucency}),[o,e,t]);(0,h.useEffect)(()=>()=>s.dispose(),[s]);let c=(0,h.useRef)(0);return i((e,r)=>{if(!n){c.current=0,s.uniforms.currentFrame.value=0,s.uniforms.vScroll.value=0;return}c.current+=r,s.uniforms.currentFrame.value=Math.floor(c.current*t.framesPerSec)%t.numFrames,s.uniforms.vScroll.value=c.current*t.scrollSpeed}),(0,y.jsx)(`mesh`,{geometry:r,material:s,renderOrder:1})}function w(e){let t=(0,m.c)(14),{entity:n}=e,r=n.forceFieldData,i=r.dimensions;if(r.textures.map(T).length===0){let e;return t[0]!==r.baseTranslucency||t[1]!==r.color||t[2]!==i?(e=(0,y.jsx)(S,{scale:i,color:r.color,baseTranslucency:r.baseTranslucency}),t[0]=r.baseTranslucency,t[1]=r.color,t[2]=i,t[3]=e):e=t[3],e}let a;t[4]!==r.baseTranslucency||t[5]!==r.color||t[6]!==i?(a=(0,y.jsx)(S,{scale:i,color:r.color,baseTranslucency:r.baseTranslucency}),t[4]=r.baseTranslucency,t[5]=r.color,t[6]=i,t[7]=a):a=t[7];let o;t[8]!==r||t[9]!==i?(o=(0,y.jsx)(C,{scale:i,data:r}),t[8]=r,t[9]=i,t[10]=o):o=t[10];let s;return t[11]!==a||t[12]!==o?(s=(0,y.jsx)(p,{name:`ForceField`,fallback:a,children:o}),t[11]=a,t[12]=o,t[13]=s):s=t[13],s}function T(e){return f(e)}export{w as ForceFieldBare};
|
||||
`;function v({textures:e,scale:t,umapping:n,vmapping:r,color:i,baseTranslucency:a}){let s=[...t].sort((e,t)=>t-e),u=new c(s[0]*n,s[1]*r),d=e[0];return new o({uniforms:{frame0:{value:d},frame1:{value:e[1]??d},frame2:{value:e[2]??d},frame3:{value:e[3]??d},frame4:{value:e[4]??d},currentFrame:{value:0},vScroll:{value:0},uvScale:{value:u},tintColor:{value:new l(...i)},opacity:{value:a},opacityFactor:{value:1},fogColor:{value:new l},fogNear:{value:1},fogFar:{value:2e3}},vertexShader:g,fragmentShader:_,transparent:!0,blending:2,side:2,depthWrite:!1,fog:!0})}var y=r();function b(e){e.wrapS=e.wrapT=s,e.colorSpace=``,e.flipY=!1,e.needsUpdate=!0}function x(e){let t=(0,m.c)(7),[n,r,i]=e,a;t[0]!==n||t[1]!==r||t[2]!==i?(a=new u(n,r,i),a.translate(n/2,r/2,i/2),t[0]=n,t[1]=r,t[2]=i,t[3]=a):a=t[3];let o=a,s,c;return t[4]===o?(s=t[5],c=t[6]):(s=()=>()=>o.dispose(),c=[o],t[4]=o,t[5]=s,t[6]=c),(0,h.useEffect)(s,c),o}function S(e){let t=(0,m.c)(10),{scale:n,color:r,baseTranslucency:i}=e,a=x(n),o;t[0]!==r[0]||t[1]!==r[1]||t[2]!==r[2]?(o=new l(r[0],r[1],r[2]),t[0]=r[0],t[1]=r[1],t[2]=r[2],t[3]=o):o=t[3];let s=o,c=i*1,u;t[4]!==s||t[5]!==c?(u=(0,y.jsx)(`meshBasicMaterial`,{color:s,transparent:!0,opacity:c,blending:2,side:2,depthWrite:!1,fog:!1}),t[4]=s,t[5]=c,t[6]=u):u=t[6];let d;return t[7]!==a||t[8]!==u?(d=(0,y.jsx)(`mesh`,{geometry:a,renderOrder:1,children:u}),t[7]=a,t[8]=u,t[9]=d):d=t[9],d}function C({scale:e,data:t}){let{animationEnabled:n}=a(),r=x(e),o=f((0,h.useMemo)(()=>t.textures.map(e=>d(e)),[t.textures]),e=>{e.forEach(e=>b(e))}),s=(0,h.useMemo)(()=>v({textures:o,scale:e,umapping:t.umapping,vmapping:t.vmapping,color:t.color,baseTranslucency:t.baseTranslucency}),[o,e,t]);(0,h.useEffect)(()=>()=>s.dispose(),[s]);let c=(0,h.useRef)(0);return i((e,r)=>{if(!n){c.current=0,s.uniforms.currentFrame.value=0,s.uniforms.vScroll.value=0;return}c.current+=r,s.uniforms.currentFrame.value=Math.floor(c.current*t.framesPerSec)%t.numFrames,s.uniforms.vScroll.value=c.current*t.scrollSpeed}),(0,y.jsx)(`mesh`,{geometry:r,material:s,renderOrder:1})}function w(e){let t=(0,m.c)(14),{entity:n}=e,r=n.forceFieldData,i=r.dimensions;if(r.textures.map(T).length===0){let e;return t[0]!==r.baseTranslucency||t[1]!==r.color||t[2]!==i?(e=(0,y.jsx)(S,{scale:i,color:r.color,baseTranslucency:r.baseTranslucency}),t[0]=r.baseTranslucency,t[1]=r.color,t[2]=i,t[3]=e):e=t[3],e}let a;t[4]!==r.baseTranslucency||t[5]!==r.color||t[6]!==i?(a=(0,y.jsx)(S,{scale:i,color:r.color,baseTranslucency:r.baseTranslucency}),t[4]=r.baseTranslucency,t[5]=r.color,t[6]=i,t[7]=a):a=t[7];let o;t[8]!==r||t[9]!==i?(o=(0,y.jsx)(C,{scale:i,data:r}),t[8]=r,t[9]=i,t[10]=o):o=t[10];let s;return t[11]!==a||t[12]!==o?(s=(0,y.jsx)(p,{name:`ForceField`,fallback:a,children:o}),t[11]=a,t[12]=o,t[13]=s):s=t[13],s}function T(e){return d(e)}export{w as ForceFieldBare};
|
||||
526
docs/assets/GameView-CQZ6o4hH.js
Normal file
526
docs/assets/GameView-CQZ6o4hH.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
38
docs/assets/Html-BLmmc0n9.js
Normal file
38
docs/assets/Html-BLmmc0n9.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/assets/JoystickContext-YJ6eVLFP.js
Normal file
1
docs/assets/JoystickContext-YJ6eVLFP.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";var i=t(),a=e(n(),1),o=r(),s=(0,a.createContext)(null);function c(){let e=(0,a.useContext)(s);if(!e)throw Error(`No JoystickContext found. Did you forget to add a <JoystickProvider>?`);return e}function l(e){let t=(0,i.c)(7),{children:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r={angle:0,force:0},t[0]=r):r=t[0];let c=(0,a.useRef)(r),l;t[1]===Symbol.for(`react.memo_cache_sentinel`)?(l={angle:0,force:0},t[1]=l):l=t[1];let u=(0,a.useRef)(l),d;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=e=>{let{angle:t,force:n}=e;t!=null&&(c.current.angle=t),n!=null&&(c.current.force=n)},t[2]=d):d=t[2];let f=d,p;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(p=e=>{let{angle:t,force:n}=e;t!=null&&(u.current.angle=t),n!=null&&(u.current.force=n)},t[3]=p):p=t[3];let m=p,h;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(h={moveState:c,lookState:u,setMoveState:f,setLookState:m},t[4]=h):h=t[4];let g=h,_;return t[5]===n?_=t[6]:(_=(0,o.jsx)(s,{value:g,children:n}),t[5]=n,t[6]=_),_}export{c as n,l as t};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{n as e,t}from"./jsx-runtime-BpGWiA-R.js";import"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import"./streamHelpers-Be29sBCp.js";import"./scene-C7IIl-c0.js";import"./mission-C2iDKeMo.js";import"./engineStore-Cio8vU1L.js";import{_ as n,m as r,s as i}from"./index-BMiY1uIn.js";var a=e(),o={Root:`_Root_uktvs_1`,Column:`_Column_uktvs_13`,Row:`_Row_uktvs_20`,Spacer:`_Spacer_uktvs_26`,Key:`_Key_uktvs_30`,Arrow:`_Arrow_uktvs_53`},s=t();function c(){let e=(0,a.c)(51),t=r(),c=n(y),b=i(v),x=i(_),S=i(g),C=i(h),w=i(m),T=i(p),E=i(f),D=i(d),O=i(u),k=i(l);if(t&&t.source!==`live`||t?.source===`live`&&!c)return null;let A;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,s.jsx)(`div`,{className:o.Spacer}),e[0]=A):A=e[0];let j;e[1]===b?j=e[2]:(j=(0,s.jsx)(`div`,{className:o.Key,"data-pressed":b,children:`W`}),e[1]=b,e[2]=j);let M;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(M=(0,s.jsx)(`div`,{className:o.Spacer}),e[3]=M):M=e[3];let N;e[4]===j?N=e[5]:(N=(0,s.jsxs)(`div`,{className:o.Row,children:[A,j,M]}),e[4]=j,e[5]=N);let P;e[6]===S?P=e[7]:(P=(0,s.jsx)(`div`,{className:o.Key,"data-pressed":S,children:`A`}),e[6]=S,e[7]=P);let F;e[8]===x?F=e[9]:(F=(0,s.jsx)(`div`,{className:o.Key,"data-pressed":x,children:`S`}),e[8]=x,e[9]=F);let I;e[10]===C?I=e[11]:(I=(0,s.jsx)(`div`,{className:o.Key,"data-pressed":C,children:`D`}),e[10]=C,e[11]=I);let L;e[12]!==P||e[13]!==F||e[14]!==I?(L=(0,s.jsxs)(`div`,{className:o.Row,children:[P,F,I]}),e[12]=P,e[13]=F,e[14]=I,e[15]=L):L=e[15];let R;e[16]!==N||e[17]!==L?(R=(0,s.jsxs)(`div`,{className:o.Column,children:[N,L]}),e[16]=N,e[17]=L,e[18]=R):R=e[18];let z;e[19]===Symbol.for(`react.memo_cache_sentinel`)?(z=(0,s.jsx)(`span`,{className:o.Arrow,children:`↑`}),e[19]=z):z=e[19];let B;e[20]===w?B=e[21]:(B=(0,s.jsx)(`div`,{className:o.Row,children:(0,s.jsxs)(`div`,{className:o.Key,"data-pressed":w,children:[z,` Space`]})}),e[20]=w,e[21]=B);let V;e[22]===Symbol.for(`react.memo_cache_sentinel`)?(V=(0,s.jsx)(`span`,{className:o.Arrow,children:`↓`}),e[22]=V):V=e[22];let H;e[23]===T?H=e[24]:(H=(0,s.jsx)(`div`,{className:o.Row,children:(0,s.jsxs)(`div`,{className:o.Key,"data-pressed":T,children:[V,` Shift`]})}),e[23]=T,e[24]=H);let U;e[25]!==B||e[26]!==H?(U=(0,s.jsxs)(`div`,{className:o.Column,children:[B,H]}),e[25]=B,e[26]=H,e[27]=U):U=e[27];let W;e[28]===Symbol.for(`react.memo_cache_sentinel`)?(W=(0,s.jsx)(`div`,{className:o.Spacer}),e[28]=W):W=e[28];let G;e[29]===E?G=e[30]:(G=(0,s.jsx)(`div`,{className:o.Key,"data-pressed":E,children:`↑`}),e[29]=E,e[30]=G);let K;e[31]===Symbol.for(`react.memo_cache_sentinel`)?(K=(0,s.jsx)(`div`,{className:o.Spacer}),e[31]=K):K=e[31];let q;e[32]===G?q=e[33]:(q=(0,s.jsxs)(`div`,{className:o.Row,children:[W,G,K]}),e[32]=G,e[33]=q);let J;e[34]===O?J=e[35]:(J=(0,s.jsx)(`div`,{className:o.Key,"data-pressed":O,children:`←`}),e[34]=O,e[35]=J);let Y;e[36]===D?Y=e[37]:(Y=(0,s.jsx)(`div`,{className:o.Key,"data-pressed":D,children:`↓`}),e[36]=D,e[37]=Y);let X;e[38]===k?X=e[39]:(X=(0,s.jsx)(`div`,{className:o.Key,"data-pressed":k,children:`→`}),e[38]=k,e[39]=X);let Z;e[40]!==J||e[41]!==Y||e[42]!==X?(Z=(0,s.jsxs)(`div`,{className:o.Row,children:[J,Y,X]}),e[40]=J,e[41]=Y,e[42]=X,e[43]=Z):Z=e[43];let Q;e[44]!==q||e[45]!==Z?(Q=(0,s.jsxs)(`div`,{className:o.Column,children:[q,Z]}),e[44]=q,e[45]=Z,e[46]=Q):Q=e[46];let $;return e[47]!==U||e[48]!==Q||e[49]!==R?($=(0,s.jsxs)(`div`,{className:o.Root,children:[R,U,Q]}),e[47]=U,e[48]=Q,e[49]=R,e[50]=$):$=e[50],$}function l(e){return e.lookRight}function u(e){return e.lookLeft}function d(e){return e.lookDown}function f(e){return e.lookUp}function p(e){return e.down}function m(e){return e.up}function h(e){return e.right}function g(e){return e.left}function _(e){return e.backward}function v(e){return e.forward}function y(e){return e.liveReady}export{c as KeyboardOverlay};
|
||||
|
|
@ -1 +0,0 @@
|
|||
._Root_uktvs_1{pointer-events:none;z-index:1;align-items:flex-end;gap:10px;display:flex;position:absolute;bottom:16px;left:50%;transform:translate(-50%)}._Column_uktvs_13{flex-direction:column;justify-content:center;gap:4px;display:flex}._Row_uktvs_20{justify-content:stretch;gap:4px;display:flex}._Spacer_uktvs_26{width:32px}._Key_uktvs_30{color:#ffffff80;white-space:nowrap;background:#0006;border:1px solid #fff3;border-radius:4px;flex:1 0 0;justify-content:center;align-items:center;min-width:32px;height:32px;padding:0 8px;font-size:11px;font-weight:600;display:flex}._Key_uktvs_30[data-pressed=true]{color:#fff;background:#34bbab99;border-color:#23fddc80}._Arrow_uktvs_53{margin-right:3px}
|
||||
1
docs/assets/KeyboardOverlay-CpSto9KR.css
Normal file
1
docs/assets/KeyboardOverlay-CpSto9KR.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
._Root_88qkf_1{pointer-events:none;z-index:1;align-items:flex-end;gap:10px;display:flex;position:absolute;bottom:16px;left:50%;transform:translate(-50%)}._Column_88qkf_13{flex-direction:column;justify-content:flex-end;align-items:stretch;gap:4px;display:flex}._Row_88qkf_25{justify-content:stretch;gap:4px;display:flex}._Spacer_88qkf_31{width:32px}._Sep_88qkf_35{opacity:.5}._Key_88qkf_39{color:#ffffff80;white-space:nowrap;background:#00000080;border:1px solid #ffffff4d;border-radius:4px;align-items:stretch;min-width:32px;height:32px;margin:0 auto;font-size:11px;font-weight:600;display:flex;overflow:hidden}._Key_88qkf_39[data-size=auto]{flex:none}._Key_88qkf_39[data-size=fill]{flex:1 0 auto}._Key_88qkf_39[data-disabled=true]{opacity:.6}._Key_88qkf_39[data-pressed=true]:not([data-disabled=true]){background:#23918499;border-color:#18c5ab66}._Label_88qkf_78{background:#c8c8c81a;justify-content:center;align-items:center;min-width:30px;padding:0 6px;display:flex}._Key_88qkf_39[data-pressed=true]:not([data-disabled=true]) ._Label_88qkf_78{color:#ffffffb3}._Label_88qkf_78[data-size=auto]{flex:none}._Label_88qkf_78[data-size=fill]{flex:1 0 auto}._Label_88qkf_78:first-child{border-right:1px solid #fff3}._Label_88qkf_78:last-child{border-left:1px solid #fff3}._Key_88qkf_39[data-pressed=true]:not([data-disabled=true]) ._Label_88qkf_78{color:#a2ffdecc;border-color:#18c5ab66}._MultiInput_88qkf_120{font-family:var(--monospace-font);flex-direction:column;justify-content:center;align-items:center;display:flex}._Input_88qkf_128{justify-content:center;align-items:center;min-width:30px;padding:0 8px;line-height:.8;display:flex}._Input_88qkf_128[data-size=auto],._MultiInput_88qkf_120[data-size=auto]{flex:none}._Input_88qkf_128[data-size=fill],._MultiInput_88qkf_120[data-size=fill]{flex:1 0 auto}._Key_88qkf_39[data-pressed=true]:not([data-disabled=true]) ._Input_88qkf_128{color:#fff}._MultiInput_88qkf_120 ._Input_88qkf_128{background:0 0;border:0;flex:none}._ColumnLabel_88qkf_161{color:#ffffff80;text-shadow:0 0 2px #0009;text-transform:uppercase;text-align:center;padding:2px 0;font-size:9px}._PlayPauseIcon_88qkf_170{font-size:9px}._MouseIcon_88qkf_174{font-size:19px}._Input_88qkf_128:has(._MouseIcon_88qkf_174){padding:0 4px}
|
||||
1
docs/assets/KeyboardOverlay-DLT4q0GY.js
Normal file
1
docs/assets/KeyboardOverlay-DLT4q0GY.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{t as i}from"./useQuery-BgNfxBEt.js";import{c as a}from"./manifest-CirqV3Ls.js";import"./logger-z_EpIdIa.js";import"./mission-C2iDKeMo.js";import{D as o,O as s}from"./index-BMiY1uIn.js";import{i as c,l,n as u}from"./loaders-0nekNxyt.js";var d=t(),f=e(n(),1),p={GuiMarkup:`_GuiMarkup_jg4va_1`,Bullet:`_Bullet_jg4va_12`},m=r(),h=new Set([`spop`,`spush`,`lmargin`,`font`,`color`,`bitmap`,`a`,`/a`]);function g(e){return e.split(/<([^><]+)>/g).map((e,t)=>{if(t%2==0)return e?{type:`text`,value:e}:null;{let[t,...n]=e.split(`:`);return h.has(t.toLowerCase())?{type:`tag`,name:t,args:n}:{type:`text`,value:`<${e}>`}}}).filter(e=>e!=null)}function _(e){let[t,n]=e;return{fontDescription:t,fontSize:n?Math.max(11,Math.min(parseInt(n.trim(),10),16)):void 0}}function v(e){let t=g(e),n={type:`span`,source:`root`,style:{},children:[]},r=n,i=[r],a=e=>e.children!=null&&e.children.some(e=>typeof e==`string`||a(e));for(let e of t)switch(e.type){case`text`:r.children.push(e.value);break;case`tag`:switch(e.name){case`spush`:{let e={type:`span`,source:`spush`,style:{},children:[]};r.children.push(e),r=e,i.push(r);break}case`spop`:if(r.source!==`root`){let e=i.pop();for(;e.source!==`spush`;)e=i.pop();r=i[i.length-1]}break;case`lmargin`:break;case`font`:{let t=_(e.args).fontSize;if(!a(r))r.style.fontSize=t;else{let e={type:`span`,source:`spush`,style:{fontSize:t},children:[]};r.children.push(e),r=e,i.push(r)}break}case`color`:if(!a(r))r.style.color=`#${e.args[0].trim()}`;else{let t={type:`span`,source:`spush`,style:{color:`#${e.args[0].trim()}`},children:[]};r.children.push(t),r=t,i.push(r)}break;case`bitmap`:{let t={type:`bitmap`,value:e.args[0]};r.children.push(t);break}case`a`:{let t=e.args[0].trim().split(` `),n={type:`a`,source:`a`,value:`http://${t.length===2&&t[0]===`wwwlink`?t[1]:t[0]}`,style:{},children:[]};r.children.push(n),r=n,i.push(r);break}case`/a`:{let e=i.pop();for(;e.source!==`a`;)e=i.pop();r=i[i.length-1];break}}}return y(n)}function y(e){switch(e.type){case`span`:return f.createElement(`span`,{style:Object.keys(e.style).length===0?void 0:e.style},...e.children.map(e=>typeof e==`string`?e:y(e)));case`a`:return f.createElement(`a`,{href:e.value,style:Object.keys(e.style).length===0?void 0:e.style,rel:`noopener noreferrer`,target:`_blank`},...e.children.map(e=>typeof e==`string`?e:y(e)));case`bitmap`:return(0,m.jsx)(S,{name:e.value})}}var b=new Map;function x(e){if(b.has(e))return b.get(e);let t;try{t=c(a(`textures/gui/${e}`))}catch{t=null}return b.set(e,t),t}function S(e){let t=(0,d.c)(5),{name:n}=e,r;t[0]===n?r=t[1]:(r=x(n),t[0]=n,t[1]=r);let i=r;if(i){let e;return t[2]===i?e=t[3]:(e=(0,m.jsx)(`img`,{src:i,alt:``,className:p.Bitmap}),t[2]=i,t[3]=e),e}if(/bullet/i.test(n)){let e;return t[4]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,m.jsx)(`span`,{className:p.Bullet,children:`•`}),t[4]=e):e=t[4],e}return null}var C=/<(?:font|color|bitmap|just|lmargin|a):/i;function w(e){return C.test(e)}function T(e,t){let n=t.toUpperCase();return e.split(`
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{t as i}from"./useQuery-B-4HMtqx.js";import{c as a}from"./manifest-CirqV3Ls.js";import"./logger-z_EpIdIa.js";import"./mission-D8vr00S1.js";import{H as o,U as s}from"./index-DBtsNu05.js";import{i as c,l,n as u}from"./loaders-VxR5Bl13.js";var d=t(),f=e(n(),1),p={GuiMarkup:`_GuiMarkup_jg4va_1`,Bullet:`_Bullet_jg4va_12`},m=r(),h=new Set([`spop`,`spush`,`lmargin`,`font`,`color`,`bitmap`,`a`,`/a`]);function g(e){return e.split(/<([^><]+)>/g).map((e,t)=>{if(t%2==0)return e?{type:`text`,value:e}:null;{let[t,...n]=e.split(`:`);return h.has(t.toLowerCase())?{type:`tag`,name:t,args:n}:{type:`text`,value:`<${e}>`}}}).filter(e=>e!=null)}function _(e){let[t,n]=e;return{fontDescription:t,fontSize:n?Math.max(11,Math.min(parseInt(n.trim(),10),16)):void 0}}function v(e){let t=g(e),n={type:`span`,source:`root`,style:{},children:[]},r=n,i=[r],a=e=>e.children!=null&&e.children.some(e=>typeof e==`string`||a(e));for(let e of t)switch(e.type){case`text`:r.children.push(e.value);break;case`tag`:switch(e.name){case`spush`:{let e={type:`span`,source:`spush`,style:{},children:[]};r.children.push(e),r=e,i.push(r);break}case`spop`:if(r.source!==`root`){let e=i.pop();for(;e.source!==`spush`;)e=i.pop();r=i[i.length-1]}break;case`lmargin`:break;case`font`:{let t=_(e.args).fontSize;if(!a(r))r.style.fontSize=t;else{let e={type:`span`,source:`spush`,style:{fontSize:t},children:[]};r.children.push(e),r=e,i.push(r)}break}case`color`:if(!a(r))r.style.color=`#${e.args[0].trim()}`;else{let t={type:`span`,source:`spush`,style:{color:`#${e.args[0].trim()}`},children:[]};r.children.push(t),r=t,i.push(r)}break;case`bitmap`:{let t={type:`bitmap`,value:e.args[0]};r.children.push(t);break}case`a`:{let t=e.args[0].trim().split(` `),n={type:`a`,source:`a`,value:`http://${t.length===2&&t[0]===`wwwlink`?t[1]:t[0]}`,style:{},children:[]};r.children.push(n),r=n,i.push(r);break}case`/a`:{let e=i.pop();for(;e.source!==`a`;)e=i.pop();r=i[i.length-1];break}}}return y(n)}function y(e){switch(e.type){case`span`:return f.createElement(`span`,{style:Object.keys(e.style).length===0?void 0:e.style},...e.children.map(e=>typeof e==`string`?e:y(e)));case`a`:return f.createElement(`a`,{href:e.value,style:Object.keys(e.style).length===0?void 0:e.style,rel:`noopener noreferrer`,target:`_blank`},...e.children.map(e=>typeof e==`string`?e:y(e)));case`bitmap`:return(0,m.jsx)(S,{name:e.value})}}var b=new Map;function x(e){if(b.has(e))return b.get(e);let t;try{t=c(a(`textures/gui/${e}`))}catch{t=null}return b.set(e,t),t}function S(e){let t=(0,d.c)(5),{name:n}=e,r;t[0]===n?r=t[1]:(r=x(n),t[0]=n,t[1]=r);let i=r;if(i){let e;return t[2]===i?e=t[3]:(e=(0,m.jsx)(`img`,{src:i,alt:``,className:p.Bitmap}),t[2]=i,t[3]=e),e}if(/bullet/i.test(n)){let e;return t[4]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,m.jsx)(`span`,{className:p.Bullet,children:`•`}),t[4]=e):e=t[4],e}return null}var C=/<(?:font|color|bitmap|just|lmargin|a):/i;function w(e){return C.test(e)}function T(e,t){let n=t.toUpperCase();return e.split(`
|
||||
`).flatMap(e=>{let t=e.match(/^\[([^\]]+)\]/);return t&&!t[1].toUpperCase().split(/\s+/).includes(n)?[]:[e.replace(/^\[[^\]]+\]/,``)]}).join(`
|
||||
`)}function E(e){let t=(0,d.c)(4),{markup:n}=e,r;t[0]===n?r=t[1]:(r=v(n),t[0]=n,t[1]=r);let i=r,a;return t[2]===i?a=t[3]:(a=(0,m.jsx)(`div`,{className:p.GuiMarkup,children:i}),t[2]=i,t[3]=a),a}var D={Dialog:`_Dialog_tbn5d_1 _Dialog_6c89x_1`,Overlay:`_Overlay_tbn5d_10 _Overlay_6c89x_20`,Body:`_Body_tbn5d_14`,Left:`_Left_tbn5d_22`,PreviewImage:`_PreviewImage_tbn5d_29`,PreviewImageFloating:`_PreviewImageFloating_tbn5d_35`,Title:`_Title_tbn5d_45`,MapMeta:`_MapMeta_tbn5d_53`,MapPlanet:`_MapPlanet_tbn5d_63`,MapQuote:`_MapQuote_tbn5d_67`,MapBlurb:`_MapBlurb_tbn5d_86`,Section:`_Section_tbn5d_91`,SectionTitle:`_SectionTitle_tbn5d_95`,MusicTrack:`_MusicTrack_tbn5d_105`,MusicButton:`_MusicButton_tbn5d_119`,Footer:`_Footer_tbn5d_146`,CloseButton:`_CloseButton_tbn5d_158 _DialogButton_6c89x_31`,Hint:`_Hint_tbn5d_162`,MusicTrackName:`_MusicTrackName_tbn5d_168`};function ee(e){let t=(0,d.c)(2),n;return t[0]===e?n=t[1]:(n={queryKey:[`parsedMission`,e],queryFn:()=>l(e)},t[0]=e,t[1]=n),i(n)}function te(e){for(let t of e.body){if(t.type!==`ObjectDeclaration`)continue;let{instanceName:e,body:n}=t;if(e&&e.type===`Identifier`&&e.name.toLowerCase()===`missiongroup`){let e={};for(let t of n){if(t.type!==`Assignment`)continue;let{target:n,value:r}=t;n.type===`Identifier`&&r.type===`StringLiteral`&&(e[n.name.toLowerCase()]=r.value)}return e}}return{}}function ne(e,t){if(e)try{return c(a(`textures/gui/${e}`))}catch{}try{return c(a(`textures/gui/Load_${t}`))}catch{}return null}function re(e){let t=(0,d.c)(7),{src:n,alt:r,className:i}=e,a=i===void 0?D.PreviewImage:i,[o,s]=(0,f.useState)(null),c,l;if(t[0]===n?(c=t[1],l=t[2]):(c=()=>{let e=!1,t;return fetch(n).then(j).then(A).then(k).then(n=>{e||!n||(t=URL.createObjectURL(n),s(t))}).catch(O),()=>{e=!0,t&&URL.revokeObjectURL(t)}},l=[n],t[0]=n,t[1]=c,t[2]=l),(0,f.useEffect)(c,l),!o)return null;let u;return t[3]!==r||t[4]!==a||t[5]!==o?(u=(0,m.jsx)(`img`,{src:o,alt:r,className:a}),t[3]=r,t[4]=a,t[5]=o,t[6]=u):u=t[6],u}function O(){}function k(e){return new Promise(t=>{let n=document.createElement(`canvas`);n.width=e.width,n.height=e.height,n.getContext(`2d`)?.drawImage(e,0,0),e.close(),n.toBlob(t)})}function A(e){return createImageBitmap(e,{colorSpaceConversion:`none`})}function j(e){return e.blob()}function ie(e){let t=(0,d.c)(22),{track:n}=e,[r,i]=(0,f.useState)(!1),[a,c]=(0,f.useState)(!0),l=(0,f.useRef)(null),p;t[0]===n?p=t[1]:(p=n.toLowerCase(),t[0]=n,t[1]=p);let h=`${u}music/${p}.mp3`,g,_;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(g=()=>{let e=l.current;if(e)return()=>{e.pause()}},_=[],t[2]=g,t[3]=_):(g=t[2],_=t[3]),(0,f.useEffect)(g,_);let v;t[4]===r?v=t[5]:(v=()=>{let e=l.current;e&&(r?e.pause():e.play().catch(()=>c(!1)))},t[4]=r,t[5]=v);let y=v,b,x,S;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(b=()=>i(!0),x=()=>i(!1),S=()=>c(!1),t[6]=b,t[7]=x,t[8]=S):(b=t[6],x=t[7],S=t[8]);let C;t[9]===h?C=t[10]:(C=(0,m.jsx)(`audio`,{ref:l,src:h,loop:!0,onPlay:b,onPause:x,onError:S}),t[9]=h,t[10]=C);let w;t[11]===n?w=t[12]:(w=(0,m.jsx)(`span`,{className:D.MusicTrackName,children:n}),t[11]=n,t[12]=w);let T;t[13]!==a||t[14]!==r||t[15]!==y?(T=a&&(0,m.jsx)(`button`,{className:D.MusicButton,onClick:y,"aria-label":r?`Pause music`:`Play music`,children:r?(0,m.jsx)(s,{}):(0,m.jsx)(o,{})}),t[13]=a,t[14]=r,t[15]=y,t[16]=T):T=t[16];let E;return t[17]!==r||t[18]!==T||t[19]!==C||t[20]!==w?(E=(0,m.jsxs)(`div`,{className:D.MusicTrack,"data-playing":r,children:[C,w,T]}),t[17]=r,t[18]=T,t[19]=C,t[20]=w,t[21]=E):E=t[21],E}function M(e){let t=(0,d.c)(100),{onClose:n,missionName:r,missionType:i}=e,{data:a}=ee(r),o=(0,f.useRef)(null),s,c;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(s=()=>{o.current?.focus();try{document.exitPointerLock()}catch{}},c=[],t[0]=s,t[1]=c):(s=t[0],c=t[1]),(0,f.useEffect)(s,c);let l,u;t[2]===n?(l=t[3],u=t[4]):(l=()=>{let e=e=>{if(e.key===`Escape`)n();else if(e.key===`k`&&(e.metaKey||e.ctrlKey)){n();return}e.stopImmediatePropagation()},t=se;return window.addEventListener(`keydown`,e,{capture:!0}),window.addEventListener(`keyup`,t,{capture:!0}),()=>{window.removeEventListener(`keydown`,e,{capture:!0}),window.removeEventListener(`keyup`,t,{capture:!0})}},u=[n],t[2]=n,t[3]=l,t[4]=u),(0,f.useEffect)(l,u);let p;t[5]===a?p=t[6]:(p=a?te(a.ast):{},t[5]=a,t[6]=p);let h=p,g;t[7]!==r||t[8]!==a?(g=a?ne(a.bitmap,r):null,t[7]=r,t[8]=a,t[9]=g):g=t[9];let _=g,v=a?.displayName??r,y;t[10]===i?y=t[11]:(y=i.toLowerCase(),t[10]=i,t[11]=y);let b=y===`singleplayer`,x=h.musictrack,S,C,O,k,A,j,M,N,P,F,I,L,R,z,B,V,H,U;if(t[12]!==_||t[13]!==v||t[14]!==b||t[15]!==i||t[16]!==n||t[17]!==a){let e=a?.missionString?T(a.missionString,i):null,r,s,c,l;if(t[36]!==a?.missionQuote){if(l=a?.missionQuote?.trim()??``,s=w(l),c=``,r=``,!s)for(let e of l.split(`
|
||||
`)){let t=e.trim();t.match(/^--[^-]/)?r=t.replace(/^-+\s*/,``).trim():t&&(c+=(c?`
|
||||
File diff suppressed because one or more lines are too long
2
docs/assets/PlayerHUD-C6H5M-FO.js
Normal file
2
docs/assets/PlayerHUD-C6H5M-FO.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/PlayerModel-D30dMLih.js
Normal file
1
docs/assets/PlayerModel-D30dMLih.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/Projectiles-BujSW3Gm.js
Normal file
1
docs/assets/Projectiles-BujSW3Gm.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{i}from"./react-three-fiber.esm-CgPHUpXo.js";import"./SettingsProvider-CCHVZuSg.js";import{_ as a,h as o,m as s}from"./GenericShape-PntybIni.js";import"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import{Ot as c,St as l,Ut as u,b as d}from"./three.module-07hRbor4.js";import"./mission-D8vr00S1.js";import"./engineStore-Dkm20jvr.js";import{p as f}from"./loaders-VxR5Bl13.js";import{t as p}from"./Texture-CIsc25mc.js";import"./FloatingLabel-C7nyg5oz.js";import"./globalFogUniforms-CtxQvsRj.js";var m=t(),h=e(n(),1),g=r(),_=new u,v=new u,y=new u,b=new u,x=new u,S=new u,C=new u(0,1,0);function w(e){let t=(0,m.c)(14),{entity:n}=e,{visual:r}=n,i;t[0]===r.texture?i=t[1]:(i=f(r.texture),t[0]=r.texture,t[1]=i);let a=p(i,T),o=Array.isArray(a)?a[0]:a,s;t[2]!==r.color.b||t[3]!==r.color.g||t[4]!==r.color.r?(s=new d().setRGB(r.color.r,r.color.g,r.color.b,c),t[2]=r.color.b,t[3]=r.color.g,t[4]=r.color.r,t[5]=s):s=t[5];let l=s,u;t[6]===r.size?u=t[7]:(u=[r.size,r.size,1],t[6]=r.size,t[7]=u);let h;t[8]!==l||t[9]!==o?(h=(0,g.jsx)(`spriteMaterial`,{map:o,color:l,transparent:!0,blending:2,depthWrite:!1,toneMapped:!1}),t[8]=l,t[9]=o,t[10]=h):h=t[10];let _;return t[11]!==u||t[12]!==h?(_=(0,g.jsx)(`sprite`,{scale:u,children:h}),t[11]=u,t[12]=h,t[13]=_):_=t[13],_}function T(e){o(Array.isArray(e)?e[0]:e)}function E(e){let t=(0,m.c)(29),{entity:n}=e,{visual:r}=n,o=(0,h.useRef)(null),c=(0,h.useRef)(null),u=(0,h.useRef)(null),d;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(d=new l,t[0]=d):d=t[0];let w=(0,h.useRef)(d),T;t[1]===r.texture?T=t[2]:(T=f(r.texture),t[1]=r.texture,t[2]=T);let E=r.crossTexture??r.texture,O;t[3]===E?O=t[4]:(O=f(E),t[3]=E,t[4]=O);let k;t[5]!==T||t[6]!==O?(k=[T,O],t[5]=T,t[6]=O,t[7]=k):k=t[7];let A=p(k,D),j;t[8]===A?j=t[9]:(j=Array.isArray(A)?A:[A,A],t[8]=A,t[9]=j);let[M,N]=j,P;t[10]!==n.direction||t[11]!==n.keyframes?.[0]||t[12]!==r.crossSize||t[13]!==r.crossViewAng||t[14]!==r.renderCross||t[15]!==r.tracerLength||t[16]!==r.tracerWidth?(P=e=>{let{camera:t}=e,i=o.current,l=c.current;if(!i||!l)return;let d=n.keyframes?.[0],f=d?.position,p=n.direction??d?.velocity;if(!f||!p){i.visible=!1,u.current&&(u.current.visible=!1);return}if(a(p,_),_.lengthSq()<1e-8){i.visible=!1,u.current&&(u.current.visible=!1);return}_.normalize(),i.visible=!0,a(f,S),v.copy(S).sub(t.position),y.crossVectors(v,_),y.lengthSq()<1e-8&&(y.crossVectors(C,_),y.lengthSq()<1e-8&&y.set(1,0,0)),y.normalize().multiplyScalar(r.tracerWidth);let m=r.tracerLength*.5;b.copy(_).multiplyScalar(-m),x.copy(_).multiplyScalar(m);let h=l.array;h[0]=b.x+y.x,h[1]=b.y+y.y,h[2]=b.z+y.z,h[3]=b.x-y.x,h[4]=b.y-y.y,h[5]=b.z-y.z,h[6]=x.x-y.x,h[7]=x.y-y.y,h[8]=x.z-y.z,h[9]=x.x+y.x,h[10]=x.y+y.y,h[11]=x.z+y.z,l.needsUpdate=!0;let g=u.current;if(!g)return;if(!r.renderCross){g.visible=!1;return}v.normalize();let T=_.dot(v);if(T>-r.crossViewAng&&T<r.crossViewAng){g.visible=!1;return}g.visible=!0,s(_,w.current),g.quaternion.copy(w.current),g.scale.setScalar(r.crossSize)},t[10]=n.direction,t[11]=n.keyframes?.[0],t[12]=r.crossSize,t[13]=r.crossViewAng,t[14]=r.renderCross,t[15]=r.tracerLength,t[16]=r.tracerWidth,t[17]=P):P=t[17],i(P);let F;t[18]===Symbol.for(`react.memo_cache_sentinel`)?(F=(0,g.jsx)(`bufferAttribute`,{ref:c,attach:`attributes-position`,args:[new Float32Array(12),3]}),t[18]=F):F=t[18];let I;t[19]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,g.jsx)(`bufferAttribute`,{attach:`attributes-uv`,args:[new Float32Array([0,0,0,1,1,1,1,0]),2]}),t[19]=I):I=t[19];let L;t[20]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,g.jsxs)(`bufferGeometry`,{children:[F,I,(0,g.jsx)(`bufferAttribute`,{attach:`index`,args:[new Uint16Array([0,1,2,0,2,3]),1]})]}),t[20]=L):L=t[20];let R;t[21]===M?R=t[22]:(R=(0,g.jsxs)(`mesh`,{ref:o,children:[L,(0,g.jsx)(`meshBasicMaterial`,{map:M,transparent:!0,blending:2,side:2,depthWrite:!1,toneMapped:!1})]}),t[21]=M,t[22]=R);let z;t[23]!==N||t[24]!==r.renderCross?(z=r.renderCross?(0,g.jsxs)(`mesh`,{ref:u,children:[(0,g.jsxs)(`bufferGeometry`,{children:[(0,g.jsx)(`bufferAttribute`,{attach:`attributes-position`,args:[new Float32Array([-.5,0,-.5,.5,0,-.5,.5,0,.5,-.5,0,.5]),3]}),(0,g.jsx)(`bufferAttribute`,{attach:`attributes-uv`,args:[new Float32Array([0,0,0,1,1,1,1,0]),2]}),(0,g.jsx)(`bufferAttribute`,{attach:`index`,args:[new Uint16Array([0,1,2,0,2,3]),1]})]}),(0,g.jsx)(`meshBasicMaterial`,{map:N,transparent:!0,blending:2,side:2,depthWrite:!1,toneMapped:!1})]}):null,t[23]=N,t[24]=r.renderCross,t[25]=z):z=t[25];let B;return t[26]!==R||t[27]!==z?(B=(0,g.jsxs)(g.Fragment,{children:[R,z]}),t[26]=R,t[27]=z,t[28]=B):B=t[28],B}function D(e){let t=Array.isArray(e)?e:[e];for(let e of t)o(e)}export{w as SpriteProjectile,E as TracerProjectile};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{i}from"./react-three-fiber.esm-dhSWjERg.js";import"./SettingsProvider-xrmxG700.js";import{_ as a,h as o,m as s}from"./GenericShape-C-0r5jzJ.js";import"./logger-z_EpIdIa.js";import"./traditional-BTL5qX2E.js";import{Ot as c,St as l,Ut as u,b as d}from"./three.module-CwgFV8Kd.js";import"./mission-C2iDKeMo.js";import"./engineStore-Cio8vU1L.js";import{t as f}from"./Texture-CUOilM1U.js";import{p}from"./loaders-0nekNxyt.js";import"./FloatingLabel-D3hPNzZS.js";import"./globalFogUniforms-CaAkHfiE.js";var m=t(),h=e(n(),1),g=r(),_=new u,v=new u,y=new u,b=new u,x=new u,S=new u,C=new u(0,1,0);function w(e){let t=(0,m.c)(14),{entity:n}=e,{visual:r}=n,i;t[0]===r.texture?i=t[1]:(i=p(r.texture),t[0]=r.texture,t[1]=i);let a=f(i,T),o=Array.isArray(a)?a[0]:a,s;t[2]!==r.color.b||t[3]!==r.color.g||t[4]!==r.color.r?(s=new d().setRGB(r.color.r,r.color.g,r.color.b,c),t[2]=r.color.b,t[3]=r.color.g,t[4]=r.color.r,t[5]=s):s=t[5];let l=s,u;t[6]===r.size?u=t[7]:(u=[r.size,r.size,1],t[6]=r.size,t[7]=u);let h;t[8]!==l||t[9]!==o?(h=(0,g.jsx)(`spriteMaterial`,{map:o,color:l,transparent:!0,blending:2,depthWrite:!1,toneMapped:!1}),t[8]=l,t[9]=o,t[10]=h):h=t[10];let _;return t[11]!==u||t[12]!==h?(_=(0,g.jsx)(`sprite`,{scale:u,children:h}),t[11]=u,t[12]=h,t[13]=_):_=t[13],_}function T(e){o(Array.isArray(e)?e[0]:e)}function E(e){let t=(0,m.c)(29),{entity:n}=e,{visual:r}=n,o=(0,h.useRef)(null),c=(0,h.useRef)(null),u=(0,h.useRef)(null),d;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(d=new l,t[0]=d):d=t[0];let w=(0,h.useRef)(d),T;t[1]===r.texture?T=t[2]:(T=p(r.texture),t[1]=r.texture,t[2]=T);let E=r.crossTexture??r.texture,O;t[3]===E?O=t[4]:(O=p(E),t[3]=E,t[4]=O);let k;t[5]!==T||t[6]!==O?(k=[T,O],t[5]=T,t[6]=O,t[7]=k):k=t[7];let A=f(k,D),j;t[8]===A?j=t[9]:(j=Array.isArray(A)?A:[A,A],t[8]=A,t[9]=j);let[M,N]=j,P;t[10]!==n.direction||t[11]!==n.keyframes?.[0]||t[12]!==r.crossSize||t[13]!==r.crossViewAng||t[14]!==r.renderCross||t[15]!==r.tracerLength||t[16]!==r.tracerWidth?(P=e=>{let{camera:t}=e,i=o.current,l=c.current;if(!i||!l)return;let d=n.keyframes?.[0],f=d?.position,p=n.direction??d?.velocity;if(!f||!p){i.visible=!1,u.current&&(u.current.visible=!1);return}if(a(p,_),_.lengthSq()<1e-8){i.visible=!1,u.current&&(u.current.visible=!1);return}_.normalize(),i.visible=!0,a(f,S),v.copy(S).sub(t.position),y.crossVectors(v,_),y.lengthSq()<1e-8&&(y.crossVectors(C,_),y.lengthSq()<1e-8&&y.set(1,0,0)),y.normalize().multiplyScalar(r.tracerWidth);let m=r.tracerLength*.5;b.copy(_).multiplyScalar(-m),x.copy(_).multiplyScalar(m);let h=l.array;h[0]=b.x+y.x,h[1]=b.y+y.y,h[2]=b.z+y.z,h[3]=b.x-y.x,h[4]=b.y-y.y,h[5]=b.z-y.z,h[6]=x.x-y.x,h[7]=x.y-y.y,h[8]=x.z-y.z,h[9]=x.x+y.x,h[10]=x.y+y.y,h[11]=x.z+y.z,l.needsUpdate=!0;let g=u.current;if(!g)return;if(!r.renderCross){g.visible=!1;return}v.normalize();let T=_.dot(v);if(T>-r.crossViewAng&&T<r.crossViewAng){g.visible=!1;return}g.visible=!0,s(_,w.current),g.quaternion.copy(w.current),g.scale.setScalar(r.crossSize)},t[10]=n.direction,t[11]=n.keyframes?.[0],t[12]=r.crossSize,t[13]=r.crossViewAng,t[14]=r.renderCross,t[15]=r.tracerLength,t[16]=r.tracerWidth,t[17]=P):P=t[17],i(P);let F;t[18]===Symbol.for(`react.memo_cache_sentinel`)?(F=(0,g.jsx)(`bufferAttribute`,{ref:c,attach:`attributes-position`,args:[new Float32Array(12),3]}),t[18]=F):F=t[18];let I;t[19]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,g.jsx)(`bufferAttribute`,{attach:`attributes-uv`,args:[new Float32Array([0,0,0,1,1,1,1,0]),2]}),t[19]=I):I=t[19];let L;t[20]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,g.jsxs)(`bufferGeometry`,{children:[F,I,(0,g.jsx)(`bufferAttribute`,{attach:`index`,args:[new Uint16Array([0,1,2,0,2,3]),1]})]}),t[20]=L):L=t[20];let R;t[21]===M?R=t[22]:(R=(0,g.jsxs)(`mesh`,{ref:o,children:[L,(0,g.jsx)(`meshBasicMaterial`,{map:M,transparent:!0,blending:2,side:2,depthWrite:!1,toneMapped:!1})]}),t[21]=M,t[22]=R);let z;t[23]!==N||t[24]!==r.renderCross?(z=r.renderCross?(0,g.jsxs)(`mesh`,{ref:u,children:[(0,g.jsxs)(`bufferGeometry`,{children:[(0,g.jsx)(`bufferAttribute`,{attach:`attributes-position`,args:[new Float32Array([-.5,0,-.5,.5,0,-.5,.5,0,.5,-.5,0,.5]),3]}),(0,g.jsx)(`bufferAttribute`,{attach:`attributes-uv`,args:[new Float32Array([0,0,0,1,1,1,1,0]),2]}),(0,g.jsx)(`bufferAttribute`,{attach:`index`,args:[new Uint16Array([0,1,2,0,2,3]),1]})]}),(0,g.jsx)(`meshBasicMaterial`,{map:N,transparent:!0,blending:2,side:2,depthWrite:!1,toneMapped:!1})]}):null,t[23]=N,t[24]=r.renderCross,t[25]=z):z=t[25];let B;return t[26]!==R||t[27]!==z?(B=(0,g.jsxs)(g.Fragment,{children:[R,z]}),t[26]=R,t[27]=z,t[28]=B):B=t[28],B}function D(e){let t=Array.isArray(e)?e:[e];for(let e of t)o(e)}export{w as SpriteProjectile,E as TracerProjectile};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
89
docs/assets/StreamingController-DNgr5TmB.js
Normal file
89
docs/assets/StreamingController-DNgr5TmB.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{r as t}from"./jsx-runtime-BpGWiA-R.js";import{a as n,o as r}from"./react-three-fiber.esm-dhSWjERg.js";import{Rt as i,zt as a}from"./three.module-CwgFV8Kd.js";var o=e(t()),s=e=>e===Object(e)&&!Array.isArray(e)&&typeof e!=`function`;function c(e,t){let c=r(e=>e.gl),l=n(a,s(e)?Object.values(e):e);return(0,o.useLayoutEffect)(()=>{t?.(l)},[t]),(0,o.useEffect)(()=>{if(`initTexture`in c){let e=[];Array.isArray(l)?e=l:l instanceof i?e=[l]:s(l)&&(e=Object.values(l)),e.forEach(e=>{e instanceof i&&c.initTexture(e)})}},[c,l]),(0,o.useMemo)(()=>{if(s(e)){let t={},n=0;for(let r in e)t[r]=l[n++];return t}else return l},[e,l])}c.preload=e=>n.preload(a,e),c.clear=e=>n.clear(a,e);export{c as t};
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{r as t}from"./jsx-runtime-BpGWiA-R.js";import{a as n,o as r}from"./react-three-fiber.esm-CgPHUpXo.js";import{Rt as i,zt as a}from"./three.module-07hRbor4.js";var o=e(t()),s=e=>e===Object(e)&&!Array.isArray(e)&&typeof e!=`function`;function c(e,t){let c=r(e=>e.gl),l=n(a,s(e)?Object.values(e):e);return(0,o.useLayoutEffect)(()=>{t?.(l)},[t]),(0,o.useEffect)(()=>{if(`initTexture`in c){let e=[];Array.isArray(l)?e=l:l instanceof i?e=[l]:s(l)&&(e=Object.values(l)),e.forEach(e=>{e instanceof i&&c.initTexture(e)})}},[c,l]),(0,o.useMemo)(()=>{if(s(e)){let t={},n=0;for(let r in e)t[r]=l[n++];return t}else return l},[e,l])}c.preload=e=>n.preload(a,e),c.clear=e=>n.clear(a,e);export{c as t};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n}from"./jsx-runtime-BpGWiA-R.js";import{i as r,o as i}from"./react-three-fiber.esm-dhSWjERg.js";import{r as a}from"./SettingsProvider-xrmxG700.js";import"./traditional-BTL5qX2E.js";import{o,r as s}from"./index-BMiY1uIn.js";var c=t(),l=e(n(),1),u=.004,d=2.5,f=.08,p=.15,m=.15;function h(){let e=(0,c.c)(17),{speedMultiplier:t,touchMode:n,invertDrag:h,invertJoystick:_}=a(),v=i(g),{moveState:y,lookState:b}=o(),x=s(),S=(0,l.useRef)(null),C;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(C={x:0,y:0},e[0]=C):C=e[0];let w=(0,l.useRef)(C),T;e[1]===h?T=e[2]:(T=()=>h,e[1]=h,e[2]=T);let E=(0,l.useEffectEvent)(T),D=(0,l.useRef)(0),O=(0,l.useRef)(0),k;e[3]!==E||e[4]!==v.domElement||e[5]!==n?(k=()=>{if(n!==`moveLookStick`)return;let e=v.domElement,t=e=>{if(S.current===null)for(;0<e.changedTouches.length;){let t=e.changedTouches[0];S.current=t.identifier,w.current={x:t.clientX,y:t.clientY};break}},r=e=>{if(S.current!==null)for(let t=0;t<e.changedTouches.length;t++){let n=e.changedTouches[t];if(n.identifier===S.current){let e=n.clientX-w.current.x,t=n.clientY-w.current.y;w.current={x:n.clientX,y:n.clientY};let r=E()?1:-1;D.current+=r*e*u,O.current+=r*t*u;break}}},i=e=>{for(let t=0;t<e.changedTouches.length;t++)if(e.changedTouches[t].identifier===S.current){S.current=null;break}};return e.addEventListener(`touchstart`,t,{passive:!0}),e.addEventListener(`touchmove`,r,{passive:!0}),e.addEventListener(`touchend`,i,{passive:!0}),e.addEventListener(`touchcancel`,i,{passive:!0}),()=>{e.removeEventListener(`touchstart`,t),e.removeEventListener(`touchmove`,r),e.removeEventListener(`touchend`,i),e.removeEventListener(`touchcancel`,i),S.current=null}},e[3]=E,e[4]=v.domElement,e[5]=n,e[6]=k):k=e[6];let A;e[7]!==v.domElement||e[8]!==n?(A=[v.domElement,n],e[7]=v.domElement,e[8]=n,e[9]=A):A=e[9],(0,l.useEffect)(k,A);let j;return e[10]!==_||e[11]!==b.current||e[12]!==y.current||e[13]!==x||e[14]!==t||e[15]!==n?(j=(e,r)=>{let{force:i,angle:a}=y.current,{force:o,angle:s}=b.current,c=D.current,l=O.current;D.current=0,O.current=0;let u=0,h=0;if(n===`dualStick`){if(o>p){let e=(o-p)/(1-p),t=Math.cos(s),n=Math.sin(s),i=_?1:-1;c-=i*t*e*d*r,l+=i*n*e*d*r}if(i>f){let e=(i-f)/(1-f),n=Math.cos(a),r=Math.sin(a);u=Math.max(-1,Math.min(1,n*e*t)),h=Math.max(-1,Math.min(1,r*e*t))}}else if(n===`moveLookStick`&&i>0&&(h=Math.max(-1,Math.min(1,.5*t)),i>=m)){let e=Math.cos(a),t=Math.sin(a),n=(i-m)/(1-m),o=_?1:-1;c-=o*e*n*d*.5*r,l+=o*t*n*d*.5*r}!(c!==0||l!==0)&&!(u!==0||h!==0)||x({deltaYaw:c,deltaPitch:l,x:u,y:h,z:0,triggers:[],delta:r})},e[10]=_,e[11]=b.current,e[12]=y.current,e[13]=x,e[14]=t,e[15]=n,e[16]=j):j=e[16],r(j),null}function g(e){return e.gl}export{h as TouchHandler};
|
||||
1
docs/assets/TouchHandler-DEueXaCg.js
Normal file
1
docs/assets/TouchHandler-DEueXaCg.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{n as e}from"./jsx-runtime-BpGWiA-R.js";import{i as t}from"./react-three-fiber.esm-CgPHUpXo.js";import{r as n}from"./SettingsProvider-CCHVZuSg.js";import"./traditional-BTL5qX2E.js";import{n as r}from"./JoystickContext-YJ6eVLFP.js";import{f as i,h as a}from"./index-DBtsNu05.js";var o=e(),s=.004,c=2.5,l=.08,u=.15,d=.15;function f(){let e=(0,o.c)(9),{speedMultiplier:f,touchMode:p,invertDrag:m,invertJoystick:h}=n(),{moveState:g,lookState:_}=r(),v=a(),[,y]=i(),b;return e[0]!==y||e[1]!==m||e[2]!==h||e[3]!==_.current||e[4]!==g.current||e[5]!==v||e[6]!==f||e[7]!==p?(b=(e,t)=>{let{force:n,angle:r}=g.current,{force:i,angle:a}=_.current,o=y().touchLook,b=m?1:-1,x=0,S=0;p===`moveLookStick`&&o&&o.dragging&&(x=b*o.deltaX*s,S=b*o.deltaY*s);let C=0,w=0;if(p===`dualStick`){if(i>u){let e=(i-u)/(1-u),n=Math.cos(a),r=Math.sin(a),o=h?1:-1;x-=o*n*e*c*t,S+=o*r*e*c*t}if(n>l){let e=(n-l)/(1-l),t=Math.cos(r),i=Math.sin(r);C=Math.max(-1,Math.min(1,t*e*f)),w=Math.max(-1,Math.min(1,i*e*f))}}else if(p===`moveLookStick`&&n>0&&(w=Math.max(-1,Math.min(1,.5*f)),n>=d)){let e=Math.cos(r),i=Math.sin(r),a=(n-d)/(1-d),o=h?1:-1;x-=o*e*a*c*.5*t,S+=o*i*a*c*.5*t}!(x!==0||S!==0)&&!(C!==0||w!==0)||v({deltaYaw:x,deltaPitch:S,x:C,y:w,z:0,triggers:[],delta:t})},e[0]=y,e[1]=m,e[2]=h,e[3]=_.current,e[4]=g.current,e[5]=v,e[6]=f,e[7]=p,e[8]=b):b=e[8],t(b),null}export{f as TouchHandler};
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/nipplejs-TUj5E3G4.js","assets/chunk-DECur_0Z.js"])))=>i.map(i=>d[i]);
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{r as t,t as n}from"./jsx-runtime-BpGWiA-R.js";import{r}from"./SettingsProvider-xrmxG700.js";import{l as i,o as a}from"./index-BMiY1uIn.js";var o=e(t(),1),s={Joystick:`_Joystick_155b9_1`,Left:`_Left_155b9_11 _Joystick_155b9_1`,Right:`_Right_155b9_17 _Joystick_155b9_1`},c=n();function l(e){let t=e.querySelector(`.back`);t&&(t.style.background=`rgba(3, 79, 76, 0.6)`,t.style.border=`1px solid rgba(0, 219, 223, 0.5)`,t.style.boxShadow=`inset 0 0 10px rgba(0, 0, 0, 0.7)`);let n=e.querySelector(`.front`);n&&(n.style.background=`radial-gradient(circle at 50% 50%, rgba(23, 247, 198, 0.9) 0%, rgba(9, 184, 170, 0.95) 100%)`,n.style.border=`2px solid rgba(255, 255, 255, 0.4)`,n.style.boxShadow=`0 2px 4px rgba(0, 0, 0, 0.5), 0 1px 1px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15), inset 0 -1px 2px rgba(0, 0, 0, 0.3)`)}function u(){let{touchMode:t}=r(),[n,u]=(0,o.useState)(null),[d,f]=(0,o.useState)(null),{moveState:p,lookState:m,setMoveState:h,setLookState:g}=a();(0,o.useEffect)(()=>{if(!n)return;let t=null,r=!1;return i(()=>import(`./nipplejs-TUj5E3G4.js`).then(t=>e(t.default,1)).then(e=>{r||(t=e.default.create({zone:n,mode:`static`,position:{left:`70px`,bottom:`70px`},size:120,restOpacity:.9,dynamicPage:!0}),l(n),t.on(`move`,(e,t)=>{h({angle:t.angle.radian,force:Math.min(1,t.force)})}),t.on(`end`,()=>{h({force:0})}))}),__vite__mapDeps([0,1])),()=>{r=!0,t?.destroy()}},[p,n,h]),(0,o.useEffect)(()=>{if(!d)return;let t=null,n=!1;return i(()=>import(`./nipplejs-TUj5E3G4.js`).then(t=>e(t.default,1)).then(e=>{n||(t=e.default.create({zone:d,mode:`static`,position:{right:`70px`,bottom:`70px`},size:120,restOpacity:.9,dynamicPage:!0}),l(d),t.on(`move`,(e,t)=>{g({angle:t.angle.radian,force:Math.min(1,t.force)})}),t.on(`end`,()=>{g({force:0})}))}),__vite__mapDeps([0,1])),()=>{n=!0,t?.destroy()}},[m,d,g]);let _=()=>{document.activeElement instanceof HTMLElement&&document.activeElement.blur()};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(`div`,{ref:u,className:t===`dualStick`?s.Left:s.Joystick,onContextMenu:e=>e.preventDefault(),onTouchStart:_},t),t===`dualStick`?(0,c.jsx)(`div`,{ref:f,className:s.Right,onContextMenu:e=>e.preventDefault(),onTouchStart:_}):null]})}export{u as TouchJoystick};
|
||||
2
docs/assets/TouchJoystick-j0axiDg9.js
Normal file
2
docs/assets/TouchJoystick-j0axiDg9.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/nipplejs-l2bgT5Z7.js","assets/chunk-DECur_0Z.js"])))=>i.map(i=>d[i]);
|
||||
import{r as e}from"./chunk-DECur_0Z.js";import{r as t,t as n}from"./jsx-runtime-BpGWiA-R.js";import{r}from"./SettingsProvider-CCHVZuSg.js";import{t as i}from"./preload-helper-CwUjIIrH.js";import{n as a}from"./JoystickContext-YJ6eVLFP.js";var o=e(t(),1),s={Joystick:`_Joystick_155b9_1`,Left:`_Left_155b9_11 _Joystick_155b9_1`,Right:`_Right_155b9_17 _Joystick_155b9_1`},c=n();function l(e){let t=e.querySelector(`.back`);t&&(t.style.background=`rgba(3, 79, 76, 0.6)`,t.style.border=`1px solid rgba(0, 219, 223, 0.5)`,t.style.boxShadow=`inset 0 0 10px rgba(0, 0, 0, 0.7)`);let n=e.querySelector(`.front`);n&&(n.style.background=`radial-gradient(circle at 50% 50%, rgba(23, 247, 198, 0.9) 0%, rgba(9, 184, 170, 0.95) 100%)`,n.style.border=`2px solid rgba(255, 255, 255, 0.4)`,n.style.boxShadow=`0 2px 4px rgba(0, 0, 0, 0.5), 0 1px 1px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15), inset 0 -1px 2px rgba(0, 0, 0, 0.3)`)}function u(){let{touchMode:t}=r(),[n,u]=(0,o.useState)(null),[d,f]=(0,o.useState)(null),{moveState:p,lookState:m,setMoveState:h,setLookState:g}=a();(0,o.useEffect)(()=>{if(!n)return;let t=null,r=!1;return i(()=>import(`./nipplejs-l2bgT5Z7.js`).then(t=>e(t.default,1)).then(e=>{r||(t=e.default.create({zone:n,mode:`static`,position:{left:`70px`,bottom:`70px`},size:120,restOpacity:.9,dynamicPage:!0}),l(n),t.on(`move`,(e,t)=>{h({angle:t.angle.radian,force:Math.min(1,t.force)})}),t.on(`end`,()=>{h({force:0})}))}),__vite__mapDeps([0,1])),()=>{r=!0,t?.destroy()}},[p,n,h]),(0,o.useEffect)(()=>{if(!d)return;let t=null,n=!1;return i(()=>import(`./nipplejs-l2bgT5Z7.js`).then(t=>e(t.default,1)).then(e=>{n||(t=e.default.create({zone:d,mode:`static`,position:{right:`70px`,bottom:`70px`},size:120,restOpacity:.9,dynamicPage:!0}),l(d),t.on(`move`,(e,t)=>{g({angle:t.angle.radian,force:Math.min(1,t.force)})}),t.on(`end`,()=>{g({force:0})}))}),__vite__mapDeps([0,1])),()=>{n=!0,t?.destroy()}},[m,d,g]);let _=()=>{document.activeElement instanceof HTMLElement&&document.activeElement.blur()};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(`div`,{ref:u,className:t===`dualStick`?s.Left:s.Joystick,onContextMenu:e=>e.preventDefault(),onTouchStart:_},t),t===`dualStick`?(0,c.jsx)(`div`,{ref:f,className:s.Right,onContextMenu:e=>e.preventDefault(),onTouchStart:_}):null]})}export{u as TouchJoystick};
|
||||
File diff suppressed because one or more lines are too long
1
docs/assets/cameraTourStore-PEzPVGnX.js
Normal file
1
docs/assets/cameraTourStore-PEzPVGnX.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{i as e,n as t}from"./traditional-BTL5qX2E.js";function n(e,t=null){return{targets:e,categoryName:t,currentIndex:0,phase:`traveling`,elapsed:0,phaseDuration:0,curve:null,startPos:null,startQuat:null,orbitCenter:null,orbitRadius:null,orbitStartAngle:0}}var r=e(e=>({animation:null,flyTo(t){e({animation:n([t])})},startTour(t,r){t.length!==0&&e({animation:n(t,r)})},advanceTarget(){e(e=>e.animation?{animation:{...e.animation,currentIndex:e.animation.currentIndex+1,phase:`traveling`,elapsed:0,curve:null,startPos:null,startQuat:null,orbitCenter:null,orbitRadius:null,orbitStartAngle:0}}:e)},cancel(){e({animation:null})}}));function i(e,n){return t(r,e,n)}export{i as n,r as t};
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{t as e}from"./logger-z_EpIdIa.js";import{A as t,_ as n}from"./streamHelpers-Be29sBCp.js";var r=e(`demoTimelineScanner`),i=500,a=new Set([`msglegitkill`,`msgheadshotkill`,`msgteamkill`,`msgselfkill`,`msgexplosionkill`,`msgvehiclekill`,`msgvehiclecrash`,`msgvehiclespawnkill`,`msgturretkill`,`msgcturretkill`,`msgturretselfkill`,`msgoobkill`,`msgcampkill`,`msgrogueminekill`,`msglavakill`,`msglightningkill`]),o=new Set([`msgselfkill`,`msgturretselfkill`,`msgoobkill`,`msglavakill`,`msglightningkill`,`msgcampkill`]);function s(e,t){if(e.length>=2&&e.charCodeAt(0)===1){let n=parseInt(e.slice(1),10);if(Number.isFinite(n))return t.get(n)??e}return e}function c(e,t,r){let i=s(e,r);for(let e=0;e<t.length;e++){let a=`%${e+1}`;i.includes(a)&&(i=i.replaceAll(a,n(s(t[e],r))))}return i=i.replace(/%\d+/g,``),n(i)}async function l(e,l,u,d){let f=new t(new Uint8Array(e)),{initialBlock:p}=await f.load(),m=new Map;for(let[e,t]of p.taggedStrings)m.set(e,t);let h=f.getRegistry(),g=l?n(l).trim().toLowerCase():null,_=null,v=null;for(let e=0;e<p.demoValues.length;e++){if(p.demoValues[e]!==`readplayerinfo`)continue;let t=p.demoValues[e+1];if(t?.startsWith(`1 `)){let e=t.split(` `),n=parseInt(e[1],10);Number.isFinite(n)&&(_=n);break}}if(_!=null){let e=p.demoValues,t=parseInt(e[1]===`<BLANK>`?`0`:e[1]??`0`,10)||0;for(let n=0;n<t;n++){let t=(e[2+n]??``).split(` `);if(parseInt(t[2],10)===_){let e=parseInt(t[4],10);isNaN(e)||(v=e);break}}}let y=[],b=0,x=!1,S=0,C=f.blockCount;for(;!d?.aborted;){let e;try{e=f.nextBlock()}catch(e){r.warn(`Stopping scan at block %d due to read error: %o`,S,e);break}if(!e)break;if(S++,e.type===2){b++;continue}if(e.type!==0||!e.parsed)continue;let t=e.parsed;if(!t.events)continue;let l=32/1e3*b;for(let e of t.events)try{if(!e.parsedData)continue;let t=e.parsedData.type;if(t===`NetStringEvent`){let t=e.parsedData.id,n=e.parsedData.value;n!=null&&m.set(t,n);continue}let r=h.getEventParser(e.classId)?.name;if(t!==`RemoteCommandEvent`&&r!==`RemoteCommandEvent`||s(e.parsedData.funcName,m)!==`ServerMessage`)continue;let i=e.parsedData.args;if(!i||i.length<2)continue;let u=s(i[0],m).toLowerCase();if(u===`msgclientjointeam`&&_!=null&&i.length>=6&&parseInt(s(i[4],m),10)===_){let e=parseInt(s(i[5],m),10);isNaN(e)||(v=e)}if(u===`msgmissionstart`&&!x){x=!0,y.push({timeSec:l,type:`match-start`,description:`Match started`});continue}if(u===`msggameover`){y.push({timeSec:l,type:`match-end`,description:`Match ended`});continue}if(u===`msgctfflagcapped`&&i.length>=2){let e=c(i[1],i.slice(2),m),t=i.length>=3?n(s(i[2],m)).trim():void 0,r=i.length>=4?n(s(i[3],m)).trim():void 0,a=`neutral`;if(v!=null&&v>0&&i.length>=6){let e=parseInt(s(i[5],m),10);e===v?a=`friendly`:isNaN(e)||(a=`enemy`)}y.push({timeSec:l,type:`flag-cap`,description:e||`Flag captured`,teamAffinity:a,capturer:t,flagTeamName:r||void 0});continue}if(a.has(u)&&i.length>=6){if(o.has(u))continue;let e=n(s(i[5],m)).trim(),t=n(s(i[2],m)).trim(),r=i.length>=10?n(s(i[9],m)).trim():void 0;if(g&&e.toLowerCase()===g){let n=c(i[1],i.slice(2),m);y.push({timeSec:l,type:`kill`,description:n||`${e} got a kill`,killer:e,victim:t,weapon:r||void 0})}}}catch(e){r.warn(`Skipping malformed event in block %d: %o`,S,e)}S%i===0&&(C&&u&&u(Math.min(S/C,1)),await new Promise(e=>setTimeout(e,0)))}return r.info(`Scanned %d blocks, found %d events`,S,y.length),y}export{l as scanDemoTimeline};
|
||||
import{t as e}from"./logger-z_EpIdIa.js";import{A as t,_ as n}from"./streamHelpers-AIec78DP.js";var r=e(`demoTimelineScanner`),i=500,a=new Set([`msglegitkill`,`msgheadshotkill`,`msgteamkill`,`msgselfkill`,`msgexplosionkill`,`msgvehiclekill`,`msgvehiclecrash`,`msgvehiclespawnkill`,`msgturretkill`,`msgcturretkill`,`msgturretselfkill`,`msgoobkill`,`msgcampkill`,`msgrogueminekill`,`msglavakill`,`msglightningkill`]),o=new Set([`msgselfkill`,`msgturretselfkill`,`msgoobkill`,`msglavakill`,`msglightningkill`,`msgcampkill`]);function s(e,t){if(e.length>=2&&e.charCodeAt(0)===1){let n=parseInt(e.slice(1),10);if(Number.isFinite(n))return t.get(n)??e}return e}function c(e,t,r){let i=s(e,r);for(let e=0;e<t.length;e++){let a=`%${e+1}`;i.includes(a)&&(i=i.replaceAll(a,n(s(t[e],r))))}return i=i.replace(/%\d+/g,``),n(i)}async function l(e,l,u,d){let f=new t(new Uint8Array(e)),{initialBlock:p}=await f.load(),m=new Map;for(let[e,t]of p.taggedStrings)m.set(e,t);let h=f.getRegistry(),g=l?n(l).trim().toLowerCase():null,_=null,v=null;for(let e=0;e<p.demoValues.length;e++){if(p.demoValues[e]!==`readplayerinfo`)continue;let t=p.demoValues[e+1];if(t?.startsWith(`1 `)){let e=t.split(` `),n=parseInt(e[1],10);Number.isFinite(n)&&(_=n);break}}if(_!=null){let e=p.demoValues,t=parseInt(e[1]===`<BLANK>`?`0`:e[1]??`0`,10)||0;for(let n=0;n<t;n++){let t=(e[2+n]??``).split(` `);if(parseInt(t[2],10)===_){let e=parseInt(t[4],10);isNaN(e)||(v=e);break}}}let y=[],b=0,x=!1,S=0,C=f.blockCount;for(;!d?.aborted;){let e;try{e=f.nextBlock()}catch(e){r.warn(`Stopping scan at block %d due to read error: %o`,S,e);break}if(!e)break;if(S++,e.type===2){b++;continue}if(e.type!==0||!e.parsed)continue;let t=e.parsed;if(!t.events)continue;let l=32/1e3*b;for(let e of t.events)try{if(!e.parsedData)continue;let t=e.parsedData.type;if(t===`NetStringEvent`){let t=e.parsedData.id,n=e.parsedData.value;n!=null&&m.set(t,n);continue}let r=h.getEventParser(e.classId)?.name;if(t!==`RemoteCommandEvent`&&r!==`RemoteCommandEvent`||s(e.parsedData.funcName,m)!==`ServerMessage`)continue;let i=e.parsedData.args;if(!i||i.length<2)continue;let u=s(i[0],m).toLowerCase();if(u===`msgclientjointeam`&&_!=null&&i.length>=6&&parseInt(s(i[4],m),10)===_){let e=parseInt(s(i[5],m),10);isNaN(e)||(v=e)}if(u===`msgmissionstart`&&!x){x=!0,y.push({timeSec:l,type:`match-start`,description:`Match started`});continue}if(u===`msggameover`){y.push({timeSec:l,type:`match-end`,description:`Match ended`});continue}if(u===`msgctfflagcapped`&&i.length>=2){let e=c(i[1],i.slice(2),m),t=i.length>=3?n(s(i[2],m)).trim():void 0,r=i.length>=4?n(s(i[3],m)).trim():void 0,a=`neutral`;if(v!=null&&v>0&&i.length>=6){let e=parseInt(s(i[5],m),10);e===v?a=`friendly`:isNaN(e)||(a=`enemy`)}y.push({timeSec:l,type:`flag-cap`,description:e||`Flag captured`,teamAffinity:a,capturer:t,flagTeamName:r||void 0});continue}if(a.has(u)&&i.length>=6){if(o.has(u))continue;let e=n(s(i[5],m)).trim(),t=n(s(i[2],m)).trim(),r=i.length>=10?n(s(i[9],m)).trim():void 0;if(g&&e.toLowerCase()===g){let n=c(i[1],i.slice(2),m);y.push({timeSec:l,type:`kill`,description:n||`${e} got a kill`,killer:e,victim:t,weapon:r||void 0})}}}catch(e){r.warn(`Skipping malformed event in block %d: %o`,S,e)}S%i===0&&(C&&u&&u(Math.min(S/C,1)),await new Promise(e=>setTimeout(e,0)))}return r.info(`Scanned %d blocks, found %d events`,S,y.length),y}export{l as scanDemoTimeline};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{n as e}from"./jsx-runtime-BpGWiA-R.js";import{i as t,n}from"./traditional-BTL5qX2E.js";var r=e=>(t,n,r)=>{let i=r.subscribe;return r.subscribe=((e,t,n)=>{let a=e;if(t){let i=n?.equalityFn||Object.is,o=e(r.getState());a=n=>{let r=e(n);if(!i(o,r)){let e=o;t(o=r,e)}},n?.fireImmediately&&t(o,o)}return i(a)}),e(t,n,r)};function i(e){let t=new Map;for(let n of e.state.datablocks.values()){if(n._class!==`tsshapeconstructor`)continue;let e=n.baseshape;if(typeof e!=`string`)continue;let r=e.toLowerCase(),i=r.replace(/\.dts$/i,``)+`_`,a=new Map;for(let e=0;e<=127;e++){let t=n[`sequence${e}`];if(typeof t!=`string`)continue;let r=t.indexOf(` `);if(r===-1)continue;let o=t.slice(0,r).toLowerCase(),s=t.slice(r+1).trim().toLowerCase();if(!s||!o.startsWith(i)||!o.endsWith(`.dsq`))continue;let c=o.slice(i.length,-4);c&&a.set(s,c)}a.size>0&&t.set(r,a)}return t}function a(e,t,n){let r=new Map;for(let n of e){let e=t.clipAction(n);r.set(n.name.toLowerCase(),e)}if(n)for(let[e,t]of n){let n=r.get(t);n&&!r.has(e)&&r.set(e,n)}return r}e();function o(e){return e.toLowerCase()}function s(e){let t=o(e.trim());return t.startsWith(`$`)?t.slice(1):t}function c(e,t,n){return e<t?t:e>n?n:e}function l(e){let t={},n={},r={},i={};for(let n of e.state.objectsById.values())t[n._id]=0,n._name&&(r[o(n._name)]=n._id,n._isDatablock&&(i[o(n._name)]=n._id));for(let t of e.state.globals.keys())n[s(t)]=0;return{objectVersionById:t,globalVersionByName:n,objectIdsByName:r,datablockIdsByName:i}}var u={runtime:{runtime:null,sequenceAliases:new Map,objectVersionById:{},globalVersionByName:{},objectIdsByName:{},datablockIdsByName:{},lastRuntimeTick:0},playback:{recording:null,status:`stopped`,timeMs:0,rate:1,durationMs:0,streamSnapshot:null}},d=t()(r(e=>({...u,setRuntime(t){let n=l(t),r=i(t);e(e=>({...e,runtime:{runtime:t,sequenceAliases:r,objectVersionById:n.objectVersionById,globalVersionByName:n.globalVersionByName,objectIdsByName:n.objectIdsByName,datablockIdsByName:n.datablockIdsByName,lastRuntimeTick:0}}))},clearRuntime(){e(e=>({...e,runtime:{runtime:null,sequenceAliases:new Map,objectVersionById:{},globalVersionByName:{},objectIdsByName:{},datablockIdsByName:{},lastRuntimeTick:0}}))},applyRuntimeBatch(t,n){t.length!==0&&e(e=>{let r={...e.runtime.objectVersionById},i={...e.runtime.globalVersionByName},a={...e.runtime.objectIdsByName},c={...e.runtime.datablockIdsByName},l=e=>{e!=null&&(r[e]=(r[e]??0)+1)};for(let e of t){if(e.type===`object.created`){let t=e.object;if(l(e.objectId),t._name){let n=o(t._name);a[n]=e.objectId,t._isDatablock&&(c[n]=e.objectId)}l(t._parent?._id);continue}if(e.type===`object.deleted`){let t=e.object;if(delete r[e.objectId],t?._name){let e=o(t._name);delete a[e],t._isDatablock&&delete c[e]}l(t?._parent?._id);continue}if(e.type===`field.changed`){l(e.objectId);continue}if(e.type===`global.changed`){let t=s(e.name);i[t]=(i[t]??0)+1;continue}}let u=n?.tick??(e.runtime.lastRuntimeTick>0?e.runtime.lastRuntimeTick+1:1);return{...e,runtime:{...e.runtime,objectVersionById:r,globalVersionByName:i,objectIdsByName:a,datablockIdsByName:c,lastRuntimeTick:u}}})},setRecording(t){let n=Math.max(0,(t?.duration??0)*1e3);e(e=>({...e,playback:{recording:t,status:t?`stopped`:e.playback.status,timeMs:t?0:e.playback.timeMs,rate:t?1:e.playback.rate,durationMs:n,streamSnapshot:t?null:e.playback.streamSnapshot}}))},setPlaybackTime(t){e(e=>{let n=c(t,0,e.playback.durationMs);return{...e,playback:{...e.playback,timeMs:n}}})},setPlaybackStatus(t){e(e=>({...e,playback:{...e.playback,status:t}}))},setPlaybackRate(t){let n=Number.isFinite(t)?c(t,.01,16):1;e(e=>({...e,playback:{...e.playback,rate:n}}))},setPlaybackStreamSnapshot(t){e(e=>({...e,playback:{...e.playback,streamSnapshot:t}}))}}))),f=0;function p(){return f}function m(e,t){f+=e*t*1e3}function h(){f=0}d.subscribe(e=>e.playback.status,e=>{e===`stopped`&&h()});function g(){return d}function _(e,t){return n(d,e,t)}export{g as a,_ as i,p as n,a as o,d as r,r as s,m as t};
|
||||
1
docs/assets/engineStore-Dkm20jvr.js
Normal file
1
docs/assets/engineStore-Dkm20jvr.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{n as e}from"./jsx-runtime-BpGWiA-R.js";import{i as t,n}from"./traditional-BTL5qX2E.js";import{t as r}from"./middleware-DPacZrFu.js";function i(e){let t=new Map;for(let n of e.state.datablocks.values()){if(n._class!==`tsshapeconstructor`)continue;let e=n.baseshape;if(typeof e!=`string`)continue;let r=e.toLowerCase(),i=r.replace(/\.dts$/i,``)+`_`,a=new Map;for(let e=0;e<=127;e++){let t=n[`sequence${e}`];if(typeof t!=`string`)continue;let r=t.indexOf(` `);if(r===-1)continue;let o=t.slice(0,r).toLowerCase(),s=t.slice(r+1).trim().toLowerCase();if(!s||!o.startsWith(i)||!o.endsWith(`.dsq`))continue;let c=o.slice(i.length,-4);c&&a.set(s,c)}a.size>0&&t.set(r,a)}return t}function a(e,t,n){let r=new Map;for(let n of e){let e=t.clipAction(n);r.set(n.name.toLowerCase(),e)}if(n)for(let[e,t]of n){let n=r.get(t);n&&!r.has(e)&&r.set(e,n)}return r}e();function o(e){return e.toLowerCase()}function s(e){let t=o(e.trim());return t.startsWith(`$`)?t.slice(1):t}function c(e,t,n){return e<t?t:e>n?n:e}function l(e){let t={},n={},r={},i={};for(let n of e.state.objectsById.values())t[n._id]=0,n._name&&(r[o(n._name)]=n._id,n._isDatablock&&(i[o(n._name)]=n._id));for(let t of e.state.globals.keys())n[s(t)]=0;return{objectVersionById:t,globalVersionByName:n,objectIdsByName:r,datablockIdsByName:i}}var u={runtime:{runtime:null,sequenceAliases:new Map,objectVersionById:{},globalVersionByName:{},objectIdsByName:{},datablockIdsByName:{},lastRuntimeTick:0},playback:{recording:null,status:`stopped`,timeMs:0,rate:1,durationMs:0,streamSnapshot:null}},d=t()(r(e=>({...u,setRuntime(t){let n=l(t),r=i(t);e(e=>({...e,runtime:{runtime:t,sequenceAliases:r,objectVersionById:n.objectVersionById,globalVersionByName:n.globalVersionByName,objectIdsByName:n.objectIdsByName,datablockIdsByName:n.datablockIdsByName,lastRuntimeTick:0}}))},clearRuntime(){e(e=>({...e,runtime:{runtime:null,sequenceAliases:new Map,objectVersionById:{},globalVersionByName:{},objectIdsByName:{},datablockIdsByName:{},lastRuntimeTick:0}}))},applyRuntimeBatch(t,n){t.length!==0&&e(e=>{let r={...e.runtime.objectVersionById},i={...e.runtime.globalVersionByName},a={...e.runtime.objectIdsByName},c={...e.runtime.datablockIdsByName},l=e=>{e!=null&&(r[e]=(r[e]??0)+1)};for(let e of t){if(e.type===`object.created`){let t=e.object;if(l(e.objectId),t._name){let n=o(t._name);a[n]=e.objectId,t._isDatablock&&(c[n]=e.objectId)}l(t._parent?._id);continue}if(e.type===`object.deleted`){let t=e.object;if(delete r[e.objectId],t?._name){let e=o(t._name);delete a[e],t._isDatablock&&delete c[e]}l(t?._parent?._id);continue}if(e.type===`field.changed`){l(e.objectId);continue}if(e.type===`global.changed`){let t=s(e.name);i[t]=(i[t]??0)+1;continue}}let u=n?.tick??(e.runtime.lastRuntimeTick>0?e.runtime.lastRuntimeTick+1:1);return{...e,runtime:{...e.runtime,objectVersionById:r,globalVersionByName:i,objectIdsByName:a,datablockIdsByName:c,lastRuntimeTick:u}}})},setRecording(t){let n=Math.max(0,(t?.duration??0)*1e3);e(e=>({...e,playback:{recording:t,status:t?`stopped`:e.playback.status,timeMs:t?0:e.playback.timeMs,rate:t?1:e.playback.rate,durationMs:n,streamSnapshot:t?null:e.playback.streamSnapshot}}))},setPlaybackTime(t){e(e=>{let n=c(t,0,e.playback.durationMs);return{...e,playback:{...e.playback,timeMs:n}}})},setPlaybackStatus(t){e(e=>({...e,playback:{...e.playback,status:t}}))},setPlaybackRate(t){let n=Number.isFinite(t)?c(t,.01,16):1;e(e=>({...e,playback:{...e.playback,rate:n}}))},setPlaybackStreamSnapshot(t){e(e=>({...e,playback:{...e.playback,streamSnapshot:t}}))}}))),f=0;function p(){return f}function m(e,t){f+=e*t*1e3}function h(){f=0}d.subscribe(e=>e.playback.status,e=>{e===`stopped`&&h()});function g(){return d}function _(e,t){return n(d,e,t)}export{g as a,_ as i,p as n,a as o,d as r,m as t};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import{o as e}from"./react-three-fiber.esm-dhSWjERg.js";import{C as t,Dt as n,Et as r,K as i,N as a,Ot as o,Rt as s,Vt as c,q as l,t as u}from"./three.module-CwgFV8Kd.js";var d=new a,f=new Map;function p(e,t){let n=f.get(e);if(n)return t&&n.image&&t(n),n;let r=new s;return r.flipY=!1,f.set(e,r),d.load(e,e=>{r.image=e,r.needsUpdate=!0,t?.(r)}),r}function m(e){let t=f.get(e);return t?t.image?Promise.resolve(t):new Promise(e=>{let n=()=>{t.image?e(t):setTimeout(n,16)};n()}):new Promise((t,n)=>{let r=new s;r.flipY=!1,f.set(e,r),d.load(e,e=>{r.image=e,r.needsUpdate=!0,t(r)},void 0,n)})}function h(e,t={}){let{repeat:r=[1,1],disableMipmaps:a=!1,anisotropy:s}=t;return e.wrapS=e.wrapT=n,e.colorSpace=o,e.repeat.set(...r),e.flipY=!1,e.anisotropy=s??1,a?(e.generateMipmaps=!1,e.minFilter=i):(e.generateMipmaps=!0,e.minFilter=l),e.magFilter=i,e.needsUpdate=!0,e}function g(e){let a=new t(e,256,256,r,c);return a.colorSpace=``,a.wrapS=a.wrapT=n,a.generateMipmaps=!1,a.minFilter=i,a.magFilter=i,a.needsUpdate=!0,a}function _(){return e(v)}function v(e){return e.gl.capabilities.getMaxAnisotropy()}var y=`
|
||||
import{o as e}from"./react-three-fiber.esm-CgPHUpXo.js";import{C as t,Dt as n,Et as r,K as i,N as a,Ot as o,Rt as s,Vt as c,q as l,t as u}from"./three.module-07hRbor4.js";var d=new a,f=new Map;function p(e,t){let n=f.get(e);if(n)return t&&n.image&&t(n),n;let r=new s;return r.flipY=!1,f.set(e,r),d.load(e,e=>{r.image=e,r.needsUpdate=!0,t?.(r)}),r}function m(e){let t=f.get(e);return t?t.image?Promise.resolve(t):new Promise(e=>{let n=()=>{t.image?e(t):setTimeout(n,16)};n()}):new Promise((t,n)=>{let r=new s;r.flipY=!1,f.set(e,r),d.load(e,e=>{r.image=e,r.needsUpdate=!0,t(r)},void 0,n)})}function h(e,t={}){let{repeat:r=[1,1],disableMipmaps:a=!1,anisotropy:s}=t;return e.wrapS=e.wrapT=n,e.colorSpace=o,e.repeat.set(...r),e.flipY=!1,e.anisotropy=s??1,a?(e.generateMipmaps=!1,e.minFilter=i):(e.generateMipmaps=!0,e.minFilter=l),e.magFilter=i,e.needsUpdate=!0,e}function g(e){let a=new t(e,256,256,r,c);return a.colorSpace=``,a.wrapS=a.wrapT=n,a.generateMipmaps=!1,a.minFilter=i,a.magFilter=i,a.needsUpdate=!0,a}function _(){return e(v)}function v(e){return e.gl.capabilities.getMaxAnisotropy()}var y=`
|
||||
#ifdef USE_FOG
|
||||
// Check fog enabled uniform - allows toggling without shader recompilation
|
||||
#ifdef USE_VOLUMETRIC_FOG
|
||||
1
docs/assets/gr-1PcjgMj0.js
Normal file
1
docs/assets/gr-1PcjgMj0.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{t as e}from"./iconBase-BCRUFbxq.js";function t(t){return e({tag:`svg`,attr:{viewBox:`0 0 24 24`},child:[{tag:`path`,attr:{fill:`none`,strokeWidth:`2`,d:`M3,22.0000002 L21,12 L3,2 L3,22.0000002 Z M5,19 L17.5999998,11.9999999 L5,5 L5,19 Z M7,16 L14.1999999,12 L7,8 L7,16 Z M9,13 L10.8,12 L9,11 L9,13 Z`},child:[]}]})(t)}function n(t){return e({tag:`svg`,attr:{viewBox:`0 0 24 24`},child:[{tag:`path`,attr:{fill:`none`,strokeWidth:`2`,d:`M3,21 L9,21 L9,3 L3,3 L3,21 Z M4,19 L8,19 L8,5 L4,5 L4,19 Z M5,17 L7,17 L7,7 L5,7 L5,17 Z M15,21 L21,21 L21,3 L15,3 L15,21 Z M16,19 L20,19 L20,5 L16,5 L16,19 Z M17,17 L19,17 L19,7 L17,7 L17,17 Z`},child:[]}]})(t)}export{t as n,n as t};
|
||||
File diff suppressed because one or more lines are too long
12
docs/assets/index-DBtsNu05.js
Normal file
12
docs/assets/index-DBtsNu05.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/assets/index-DgQPeusO.css
Normal file
1
docs/assets/index-DgQPeusO.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{c as e,n as t,s as n,t as r}from"./manifest-CirqV3Ls.js";import{i}from"./stringUtils-EmGsjr9D.js";import{t as a}from"./logger-z_EpIdIa.js";import{a as o}from"./mission-C2iDKeMo.js";function s(e){return e.split(/(?:\r\n|\r|\n)/g).map(e=>e.trim()).filter(Boolean).filter(e=>!e.startsWith(`;`)).map(e=>{let t=e.match(/^(.+)\s(\d+)$/);if(t){let e=parseInt(t[2],10);return{name:t[1],frameCount:e}}else return{name:e,frameCount:1}})}var c=256;function l(e){let t=new DataView(e),n=0,r=t.getUint8(n++),i=new Uint16Array(c*c),a=[],o=e=>{let r=``;for(let i=0;i<e;i++){let e=t.getUint8(n+i);if(e===0)break;r+=String.fromCharCode(e)}return n+=e,r};for(let e=0;e<c*c;e++){let r=t.getUint16(n,!0);n+=2,i[e]=r}n+=256*256;let s=i;for(let e=0;e<8;e++){let r=t.getUint8(n++),i=o(r);e<6&&r>0&&a.push(i)}let l=[];for(let e of a){let e=new Uint8Array(c*c);for(let r=0;r<c*c;r++)e[r]=t.getUint8(n++);l.push(e)}return{version:r,textureNames:a,heightMap:s,alphaMaps:l}}var u=a(`loaders`),d=`/t2-mapper`,f=`${d}/base/`,p=`${d}/magenta.png`;function m(e,t){let i;try{i=r(e)}catch(n){if(t)return u.warn(`Resource "%s" not found — rendering fallback`,e),t;throw n}let[a,o]=n(i);return a?`${f}@vl2/${a}/${o}`:`${f}${o}`}function h(e){return m(`interiors/${e}`).replace(/\.dif$/i,`.glb`)}function g(e){return m(`shapes/${e}`).replace(/\.dts$/i,`.glb`)}function _(t){return t=t.replace(/^terrain\./,``),m(e(`textures/terrain/${t}`),p)}function v(t,n){let r=i(n).split(`/`);return m(e(`${r.length>1?r.slice(0,-1).join(`/`)+`/`:``}${t}`),p)}function y(t){return m(e(`textures/${t}`),p)}function b(e){return m(`audio/${e}`).replace(/\.wav$/i,`.m4a`)}async function x(e){let t=m(`textures/${e}`);return(await(await fetch(t)).text()).split(/(?:\r\n|\r|\n)/).map(e=>{if(e=e.trim(),!e.startsWith(`;`))return e}).filter(Boolean)}async function S(e){let n=t(e),r=await(await fetch(m(n.resourcePath))).arrayBuffer(),i;try{i=new TextDecoder(`utf-8`,{fatal:!0}).decode(r)}catch{i=new TextDecoder(`windows-1252`).decode(r)}return i=i.replaceAll(`<EFBFBD>`,`'`),o(i)}async function C(e){let t=m(`terrains/${e}`);u.debug(`Fetching terrain: %s`,t);let n=await fetch(t);if(!n.ok)throw Error(`[loadTerrain] Failed to fetch ${t}: ${n.status} ${n.statusText}`);let r=await n.arrayBuffer();return u.debug(`Loaded terrain %s: %d bytes`,e,r.byteLength),l(r)}async function w(e){let t=m(e);return s(await(await fetch(t)).text())}export{v as a,w as c,g as d,_ as f,m as i,S as l,f as n,h as o,y as p,b as r,x as s,p as t,C as u};
|
||||
import{c as e,n as t,s as n,t as r}from"./manifest-CirqV3Ls.js";import{i}from"./stringUtils-EmGsjr9D.js";import{t as a}from"./logger-z_EpIdIa.js";import{a as o}from"./mission-D8vr00S1.js";function s(e){return e.split(/(?:\r\n|\r|\n)/g).map(e=>e.trim()).filter(Boolean).filter(e=>!e.startsWith(`;`)).map(e=>{let t=e.match(/^(.+)\s(\d+)$/);if(t){let e=parseInt(t[2],10);return{name:t[1],frameCount:e}}else return{name:e,frameCount:1}})}var c=256;function l(e){let t=new DataView(e),n=0,r=t.getUint8(n++),i=new Uint16Array(c*c),a=[],o=e=>{let r=``;for(let i=0;i<e;i++){let e=t.getUint8(n+i);if(e===0)break;r+=String.fromCharCode(e)}return n+=e,r};for(let e=0;e<c*c;e++){let r=t.getUint16(n,!0);n+=2,i[e]=r}n+=256*256;let s=i;for(let e=0;e<8;e++){let r=t.getUint8(n++),i=o(r);e<6&&r>0&&a.push(i)}let l=[];for(let e of a){let e=new Uint8Array(c*c);for(let r=0;r<c*c;r++)e[r]=t.getUint8(n++);l.push(e)}return{version:r,textureNames:a,heightMap:s,alphaMaps:l}}var u=a(`loaders`),d=`/t2-mapper`,f=`${d}/base/`,p=`${d}/magenta.png`;function m(e,t){let i;try{i=r(e)}catch(n){if(t)return u.warn(`Resource "%s" not found — rendering fallback`,e),t;throw n}let[a,o]=n(i);return a?`${f}@vl2/${a}/${o}`:`${f}${o}`}function h(e){return m(`interiors/${e}`).replace(/\.dif$/i,`.glb`)}function g(e){return m(`shapes/${e}`).replace(/\.dts$/i,`.glb`)}function _(t){return t=t.replace(/^terrain\./,``),m(e(`textures/terrain/${t}`),p)}function v(t,n){let r=i(n).split(`/`);return m(e(`${r.length>1?r.slice(0,-1).join(`/`)+`/`:``}${t}`),p)}function y(t){return m(e(`textures/${t}`),p)}function b(e){return m(`audio/${e}`).replace(/\.wav$/i,`.m4a`)}async function x(e){let t=m(`textures/${e}`);return(await(await fetch(t)).text()).split(/(?:\r\n|\r|\n)/).map(e=>{if(e=e.trim(),!e.startsWith(`;`))return e}).filter(Boolean)}async function S(e){let n=t(e),r=await(await fetch(m(n.resourcePath))).arrayBuffer(),i;try{i=new TextDecoder(`utf-8`,{fatal:!0}).decode(r)}catch{i=new TextDecoder(`windows-1252`).decode(r)}return i=i.replaceAll(`<EFBFBD>`,`'`),o(i)}async function C(e){let t=m(`terrains/${e}`);u.debug(`Fetching terrain: %s`,t);let n=await fetch(t);if(!n.ok)throw Error(`[loadTerrain] Failed to fetch ${t}: ${n.status} ${n.statusText}`);let r=await n.arrayBuffer();return u.debug(`Loaded terrain %s: %d bytes`,e,r.byteLength),l(r)}async function w(e){let t=m(e);return s(await(await fetch(t)).text())}export{v as a,w as c,g as d,_ as f,m as i,S as l,f as n,h as o,y as p,b as r,x as s,p as t,C as u};
|
||||
1
docs/assets/middleware-DPacZrFu.js
Normal file
1
docs/assets/middleware-DPacZrFu.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
var e=e=>(t,n,r)=>{let i=r.subscribe;return r.subscribe=((e,t,n)=>{let a=e;if(t){let i=n?.equalityFn||Object.is,o=e(r.getState());a=n=>{let r=e(n);if(!i(o,r)){let e=o;t(o=r,e)}},n?.fireImmediately&&t(o,o)}return i(a)}),e(t,n,r)};export{e as t};
|
||||
1
docs/assets/preload-helper-CwUjIIrH.js
Normal file
1
docs/assets/preload-helper-CwUjIIrH.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
var e=`modulepreload`,t=function(e){return`/t2-mapper/`+e},n={},r=function(r,i,a){let o=Promise.resolve();if(i&&i.length>0){let r=document.getElementsByTagName(`link`),s=document.querySelector(`meta[property=csp-nonce]`),c=s?.nonce||s?.getAttribute(`nonce`);function l(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}o=l(i.map(i=>{if(i=t(i,a),i in n)return;n[i]=!0;let o=i.endsWith(`.css`),s=o?`[rel="stylesheet"]`:``;if(a)for(let e=r.length-1;e>=0;e--){let t=r[e];if(t.href===i&&(!o||t.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${i}"]${s}`))return;let l=document.createElement(`link`);if(l.rel=o?`stylesheet`:e,o||(l.as=`script`),l.crossOrigin=``,l.href=i,c&&l.setAttribute(`nonce`,c),document.head.appendChild(l),o)return new Promise((e,t)=>{l.addEventListener(`load`,e),l.addEventListener(`error`,()=>t(Error(`Unable to preload CSS for ${i}`)))})}))}function s(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return o.then(e=>{for(let t of e||[])t.status===`rejected`&&s(t.reason);return r().catch(s)})};export{r as t};
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{t as e}from"./logger-z_EpIdIa.js";import{St as t,nt as n}from"./three.module-CwgFV8Kd.js";var r=e(`ghostToScene`);function i(e,t={x:0,y:0,z:0}){return e&&typeof e==`object`&&`x`in e?e:t}function a(e,t={r:0,g:0,b:0}){return e&&typeof e==`object`&&`r`in e?e:t}function o(e,t={r:.5,g:.5,b:.5,a:1}){return e&&typeof e==`object`&&`r`in e?e:t}function s(e){if(e&&typeof e==`object`&&`elements`in e&&Array.isArray(e.elements))return e;if(e&&typeof e==`object`&&`position`in e&&`rotation`in e){let{position:t,rotation:n}=e,r=n.x*n.x,i=n.y*n.y,a=n.z*n.z,o=n.x*n.y,s=n.x*n.z,c=n.y*n.z,l=n.w*n.x,u=n.w*n.y,d=n.w*n.z;return{elements:[1-2*(i+a),2*(o+d),2*(s-u),0,2*(o-d),1-2*(r+a),2*(c+l),0,2*(s+u),2*(c-l),1-2*(r+i),0,t.x,t.y,t.z,1],position:{x:t.x,y:t.y,z:t.z}}}return{elements:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],position:{x:0,y:0,z:0}}}function c(e,t){return{className:`TerrainBlock`,ghostIndex:e,terrFileName:t.terrFileName??``,detailTextureName:t.detailTextureName??``,squareSize:t.squareSize??8,emptySquareRuns:t.emptySquareRuns}}function l(e,t){return{className:`InteriorInstance`,ghostIndex:e,interiorFile:t.interiorFile??``,transform:s(t.transform),scale:i(t.scale,{x:1,y:1,z:1}),showTerrainInside:t.showTerrainInside??!1,skinBase:t.skinBase??``,alarmState:t.alarmState??!1}}function u(e,t){return{className:`TSStatic`,ghostIndex:e,shapeName:t.shapeName??``,transform:s(t.transform),scale:i(t.scale,{x:1,y:1,z:1})}}function d(e,t){let n=Array.isArray(t.fogVolumes)?t.fogVolumes.map(e=>({visibleDistance:e.visibleDistance??0,minHeight:e.minHeight??0,maxHeight:e.maxHeight??0,color:a(e.color)})):[],r=Array.isArray(t.cloudLayers)?t.cloudLayers.map(e=>({texture:e.texture??``,heightPercent:e.heightPercent??0,speed:e.speed??0})):[];return{className:`Sky`,ghostIndex:e,materialList:t.materialList??``,fogColor:a(t.fogColor),visibleDistance:t.visibleDistance??1e3,fogDistance:t.fogDistance??0,skySolidColor:a(t.skySolidColor),useSkyTextures:t.useSkyTextures??!0,fogVolumes:n,cloudLayers:r,windVelocity:i(t.windVelocity)}}function f(e,t){return{className:`Sun`,ghostIndex:e,direction:i(t.direction,{x:.57735,y:.57735,z:-.57735}),color:o(t.color,{r:.7,g:.7,b:.7,a:1}),ambient:o(t.ambient,{r:.5,g:.5,b:.5,a:1}),textures:Array.isArray(t.textures)?t.textures:void 0}}function p(e,t){return{className:`MissionArea`,ghostIndex:e,area:t.area??{x:-512,y:-512,w:1024,h:1024},flightCeiling:t.flightCeiling??2e3,flightCeilingRange:t.flightCeilingRange??50}}function m(e,t){return{className:`WaterBlock`,ghostIndex:e,transform:s(t.transform),scale:i(t.scale,{x:1,y:1,z:1}),surfaceName:t.surfaceName??``,envMapName:t.envMapName??``,surfaceOpacity:t.surfaceOpacity??.75,waveMagnitude:t.waveMagnitude??1,envMapIntensity:t.envMapIntensity??1}}function h(e,t,n){let i;switch(e){case`TerrainBlock`:return i=c(t,n),r.debug(`TerrainBlock #%d: terrFileName=%s`,t,i.terrFileName),i;case`InteriorInstance`:return i=l(t,n),r.debug(`InteriorInstance #%d: interiorFile=%s`,t,i.interiorFile),i;case`TSStatic`:return u(t,n);case`Sky`:{i=d(t,n);let e=i;return r.debug(`Sky #%d: materialList=%s fogColor=(%s, %s, %s) visibleDist=%d fogDist=%d useSkyTextures=%s`,t,e.materialList,e.fogColor.r.toFixed(3),e.fogColor.g.toFixed(3),e.fogColor.b.toFixed(3),e.visibleDistance,e.fogDistance,e.useSkyTextures),i}case`Sun`:{i=f(t,n);let e=i;return r.debug(`Sun #%d: dir=(%s, %s, %s) color=(%s, %s, %s) ambient=(%s, %s, %s)`,t,e.direction.x.toFixed(3),e.direction.y.toFixed(3),e.direction.z.toFixed(3),e.color.r.toFixed(3),e.color.g.toFixed(3),e.color.b.toFixed(3),e.ambient.r.toFixed(3),e.ambient.g.toFixed(3),e.ambient.b.toFixed(3)),i}case`MissionArea`:return p(t,n);case`WaterBlock`:return m(t,n);default:return null}}function g(e){return[e.y,e.z,e.x]}function _(e){return[e.y,e.z,e.x]}function v(e){let r=e.elements,i=new n,a=i.elements;a[0]=r[5],a[1]=r[6],a[2]=r[4],a[3]=0,a[4]=r[9],a[5]=r[10],a[6]=r[8],a[7]=0,a[8]=r[1],a[9]=r[2],a[10]=r[0],a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1;let o=new t;return o.setFromRotationMatrix(i),o.conjugate(),o}export{h as i,_ as n,g as r,v as t};
|
||||
import{t as e}from"./logger-z_EpIdIa.js";import{St as t,nt as n}from"./three.module-07hRbor4.js";var r=e(`ghostToScene`);function i(e,t={x:0,y:0,z:0}){return e&&typeof e==`object`&&`x`in e?e:t}function a(e,t={r:0,g:0,b:0}){return e&&typeof e==`object`&&`r`in e?e:t}function o(e,t={r:.5,g:.5,b:.5,a:1}){return e&&typeof e==`object`&&`r`in e?e:t}function s(e){if(e&&typeof e==`object`&&`elements`in e&&Array.isArray(e.elements))return e;if(e&&typeof e==`object`&&`position`in e&&`rotation`in e){let{position:t,rotation:n}=e,r=n.x*n.x,i=n.y*n.y,a=n.z*n.z,o=n.x*n.y,s=n.x*n.z,c=n.y*n.z,l=n.w*n.x,u=n.w*n.y,d=n.w*n.z;return{elements:[1-2*(i+a),2*(o+d),2*(s-u),0,2*(o-d),1-2*(r+a),2*(c+l),0,2*(s+u),2*(c-l),1-2*(r+i),0,t.x,t.y,t.z,1],position:{x:t.x,y:t.y,z:t.z}}}return{elements:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],position:{x:0,y:0,z:0}}}function c(e,t){return{className:`TerrainBlock`,ghostIndex:e,terrFileName:t.terrFileName??``,detailTextureName:t.detailTextureName??``,squareSize:t.squareSize??8,emptySquareRuns:t.emptySquareRuns}}function l(e,t){return{className:`InteriorInstance`,ghostIndex:e,interiorFile:t.interiorFile??``,transform:s(t.transform),scale:i(t.scale,{x:1,y:1,z:1}),showTerrainInside:t.showTerrainInside??!1,skinBase:t.skinBase??``,alarmState:t.alarmState??!1}}function u(e,t){return{className:`TSStatic`,ghostIndex:e,shapeName:t.shapeName??``,transform:s(t.transform),scale:i(t.scale,{x:1,y:1,z:1})}}function d(e,t){let n=Array.isArray(t.fogVolumes)?t.fogVolumes.map(e=>({visibleDistance:e.visibleDistance??0,minHeight:e.minHeight??0,maxHeight:e.maxHeight??0,color:a(e.color)})):[],r=Array.isArray(t.cloudLayers)?t.cloudLayers.map(e=>({texture:e.texture??``,heightPercent:e.heightPercent??0,speed:e.speed??0})):[];return{className:`Sky`,ghostIndex:e,materialList:t.materialList??``,fogColor:a(t.fogColor),visibleDistance:t.visibleDistance??1e3,fogDistance:t.fogDistance??0,skySolidColor:a(t.skySolidColor),useSkyTextures:t.useSkyTextures??!0,fogVolumes:n,cloudLayers:r,windVelocity:i(t.windVelocity)}}function f(e,t){return{className:`Sun`,ghostIndex:e,direction:i(t.direction,{x:.57735,y:.57735,z:-.57735}),color:o(t.color,{r:.7,g:.7,b:.7,a:1}),ambient:o(t.ambient,{r:.5,g:.5,b:.5,a:1}),textures:Array.isArray(t.textures)?t.textures:void 0}}function p(e,t){return{className:`MissionArea`,ghostIndex:e,area:t.area??{x:-512,y:-512,w:1024,h:1024},flightCeiling:t.flightCeiling??2e3,flightCeilingRange:t.flightCeilingRange??50}}function m(e,t){return{className:`WaterBlock`,ghostIndex:e,transform:s(t.transform),scale:i(t.scale,{x:1,y:1,z:1}),surfaceName:t.surfaceName??``,envMapName:t.envMapName??``,surfaceOpacity:t.surfaceOpacity??.75,waveMagnitude:t.waveMagnitude??1,envMapIntensity:t.envMapIntensity??1}}function h(e,t,n){let i;switch(e){case`TerrainBlock`:return i=c(t,n),r.debug(`TerrainBlock #%d: terrFileName=%s`,t,i.terrFileName),i;case`InteriorInstance`:return i=l(t,n),r.debug(`InteriorInstance #%d: interiorFile=%s`,t,i.interiorFile),i;case`TSStatic`:return u(t,n);case`Sky`:{i=d(t,n);let e=i;return r.debug(`Sky #%d: materialList=%s fogColor=(%s, %s, %s) visibleDist=%d fogDist=%d useSkyTextures=%s`,t,e.materialList,e.fogColor.r.toFixed(3),e.fogColor.g.toFixed(3),e.fogColor.b.toFixed(3),e.visibleDistance,e.fogDistance,e.useSkyTextures),i}case`Sun`:{i=f(t,n);let e=i;return r.debug(`Sun #%d: dir=(%s, %s, %s) color=(%s, %s, %s) ambient=(%s, %s, %s)`,t,e.direction.x.toFixed(3),e.direction.y.toFixed(3),e.direction.z.toFixed(3),e.color.r.toFixed(3),e.color.g.toFixed(3),e.color.b.toFixed(3),e.ambient.r.toFixed(3),e.ambient.g.toFixed(3),e.ambient.b.toFixed(3)),i}case`MissionArea`:return p(t,n);case`WaterBlock`:return m(t,n);default:return null}}function g(e){return[e.y,e.z,e.x]}function _(e){return[e.y,e.z,e.x]}function v(e){let r=e.elements,i=new n,a=i.elements;a[0]=r[5],a[1]=r[6],a[2]=r[4],a[3]=0,a[4]=r[9],a[5]=r[10],a[6]=r[8],a[7]=0,a[8]=r[1],a[9]=r[2],a[10]=r[0],a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1;let o=new t;return o.setFromRotationMatrix(i),o.conjugate(),o}export{h as i,_ as n,g as r,v as t};
|
||||
File diff suppressed because one or more lines are too long
1
docs/assets/streamPlaybackStore-DhJekvnx.js
Normal file
1
docs/assets/streamPlaybackStore-DhJekvnx.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{i as e}from"./traditional-BTL5qX2E.js";var t=e()(()=>({time:0,playback:null,root:null,cameraMode:`original`,orbitOverrideYaw:0,orbitOverridePitch:0,entities:new Map}));function n(){t.setState({time:0,playback:null,cameraMode:`original`,orbitOverrideYaw:0,orbitOverridePitch:0})}export{t as n,n as t};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{i as e}from"./traditional-BTL5qX2E.js";var t=e()(()=>({time:0,playback:null,root:null,freeFlyCamera:!1,entities:new Map}));function n(){t.setState({time:0,playback:null,freeFlyCamera:!1})}export{t as n,n as t};
|
||||
File diff suppressed because one or more lines are too long
1
docs/assets/useBaseQuery-BcnVzmUC.js
Normal file
1
docs/assets/useBaseQuery-BcnVzmUC.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/assets/useQuery-B-4HMtqx.js
Normal file
1
docs/assets/useQuery-B-4HMtqx.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as e,t}from"./useBaseQuery-BcnVzmUC.js";function n(n,r){return t(n,e,r)}export{n as t};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{r as e,t}from"./useBaseQuery-BIpPgzSS.js";function n(n,r){return t(n,e,r)}export{n as t};
|
||||
|
|
@ -19,22 +19,26 @@
|
|||
<link rel="apple-touch-icon" href="/t2-mapper/icon-512.png" />
|
||||
<meta name="theme-color" content="#067272" />
|
||||
<link rel="preconnect" href="https://www.gstatic.com" crossorigin />
|
||||
<script type="module" crossorigin src="/t2-mapper/assets/index-BMiY1uIn.js"></script>
|
||||
<script type="module" crossorigin src="/t2-mapper/assets/index-DBtsNu05.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/chunk-DECur_0Z.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/three.module-CwgFV8Kd.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/streamHelpers-Be29sBCp.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/preload-helper-CwUjIIrH.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/three.module-07hRbor4.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/streamHelpers-AIec78DP.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/logger-z_EpIdIa.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/mission-C2iDKeMo.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/mission-D8vr00S1.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/jsx-runtime-BpGWiA-R.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/traditional-BTL5qX2E.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/react-three-fiber.esm-dhSWjERg.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/react-three-fiber.esm-CgPHUpXo.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/stringUtils-EmGsjr9D.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/manifest-CirqV3Ls.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/SettingsProvider-xrmxG700.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/SettingsProvider-CCHVZuSg.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/iconBase-BCRUFbxq.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/engineStore-Cio8vU1L.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/scene-C7IIl-c0.js">
|
||||
<link rel="stylesheet" crossorigin href="/t2-mapper/assets/index-UOHJQQT_.css">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/middleware-DPacZrFu.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/JoystickContext-YJ6eVLFP.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/scene-BdOVRsxo.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/cameraTourStore-PEzPVGnX.js">
|
||||
<link rel="modulepreload" crossorigin href="/t2-mapper/assets/engineStore-Dkm20jvr.js">
|
||||
<link rel="stylesheet" crossorigin href="/t2-mapper/assets/index-DgQPeusO.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export default defineConfig([
|
|||
"react-hooks/set-state-in-effect": "off",
|
||||
"react-refresh/only-export-components": "warn",
|
||||
"react-hooks/immutability": "warn",
|
||||
"no-control-regex": "off",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
|
|||
53
src/components/ActiveInputBindings.tsx
Normal file
53
src/components/ActiveInputBindings.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { useRecording } from "./RecordingProvider";
|
||||
import { useInputMode } from "./InputContext";
|
||||
import { useCameraTour } from "../state/cameraTourStore";
|
||||
import { InputBindings } from "./InputBindings";
|
||||
import {
|
||||
FREE_FLY_INPUT,
|
||||
MOVABLE_CAMERA_INPUT,
|
||||
POINTER_LOCKABLE_INPUT,
|
||||
MAP_MODE_INPUT,
|
||||
DEMO_MODE_INPUT,
|
||||
LIVE_OBSERVER_INPUT,
|
||||
LIVE_FOLLOW_INPUT,
|
||||
TOUR_MODE_INPUT,
|
||||
} from "./inputMap";
|
||||
|
||||
/**
|
||||
* Mounts the appropriate `InputBindings` components based on the current
|
||||
* app mode. Each binding group has its own lifecycle — when a group
|
||||
* unmounts, its actions are automatically cleaned up from the store.
|
||||
*/
|
||||
export function ActiveInputBindings() {
|
||||
const recording = useRecording();
|
||||
const inputMode = useInputMode();
|
||||
const isTourActive = useCameraTour((s) => s.animation !== null);
|
||||
const isDemo = recording?.source === "demo";
|
||||
const isLive = recording?.source === "live";
|
||||
const isMap = !recording;
|
||||
|
||||
// Free-fly movement: map mode (no tour) or live free-fly.
|
||||
const showFreeFly =
|
||||
(isMap && !isTourActive) || (isLive && inputMode === "fly");
|
||||
|
||||
// Camera can be moved by drag/touch in most modes.
|
||||
const showMovableCamera = !isTourActive;
|
||||
|
||||
// Pointer lock: available when not touring.
|
||||
const showPointerLockable = !isTourActive;
|
||||
|
||||
return (
|
||||
<>
|
||||
{showFreeFly && <InputBindings map={FREE_FLY_INPUT} />}
|
||||
{showMovableCamera && <InputBindings map={MOVABLE_CAMERA_INPUT} />}
|
||||
{showPointerLockable && <InputBindings map={POINTER_LOCKABLE_INPUT} />}
|
||||
{isMap && !isTourActive && <InputBindings map={MAP_MODE_INPUT} />}
|
||||
{isDemo && <InputBindings map={DEMO_MODE_INPUT} />}
|
||||
{isLive && <InputBindings map={LIVE_OBSERVER_INPUT} />}
|
||||
{isLive && inputMode === "follow" && (
|
||||
<InputBindings map={LIVE_FOLLOW_INPUT} />
|
||||
)}
|
||||
{isTourActive && <InputBindings map={TOUR_MODE_INPUT} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|||
import { FeaturesProvider } from "@/src/components/FeaturesProvider";
|
||||
import { MapInspector } from "@/src/components/MapInspector";
|
||||
import { SettingsProvider } from "@/src/components/SettingsProvider";
|
||||
|
||||
// Three.js has its own loaders for textures and models, but we need to load other
|
||||
// stuff too, e.g. missions, terrains, and more. This client is used for those.
|
||||
const queryClient = new QueryClient();
|
||||
|
|
|
|||
|
|
@ -425,6 +425,7 @@ export const AudioEmitter = memo(function AudioEmitter({
|
|||
}, [audioEnabled]);
|
||||
|
||||
return debugMode ? (
|
||||
// eslint-disable-next-line react-hooks/refs
|
||||
<mesh position={emitterPosRef.current}>
|
||||
<sphereGeometry args={[minDistance, 12, 12]} />
|
||||
<meshBasicMaterial
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useRef } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useInputAction } from "./InputControls";
|
||||
import {
|
||||
Box3,
|
||||
Camera,
|
||||
|
|
@ -23,9 +24,9 @@ function easeInOutCubic(t: number): number {
|
|||
|
||||
const FALLBACK_ORBIT_RADIUS = 3;
|
||||
const DEFAULT_ORBIT_HEIGHT = 2;
|
||||
const MIN_ORBIT_RADIUS = 1.5;
|
||||
const MIN_ORBIT_RADIUS = 1.8;
|
||||
/** Extra orbit radius added beyond half the object's height. */
|
||||
const ORBIT_PAD_VERTICAL = 1.6;
|
||||
const ORBIT_PAD_VERTICAL = 1.8;
|
||||
/** Extra orbit radius added beyond half the object's spread (width/length). */
|
||||
const ORBIT_PAD_HORIZONTAL = 1.2;
|
||||
const ORBIT_ANGULAR_SPEED = 0.6; // rad/s
|
||||
|
|
@ -337,6 +338,24 @@ export function CameraTourConsumer() {
|
|||
const scene = useThree((s) => s.scene);
|
||||
const prevAnimationRef = useRef<TourAnimation | null>(null);
|
||||
|
||||
// Click to advance to next stop, or exit if on the last target.
|
||||
useInputAction("nextStop", () => {
|
||||
const animation = cameraTourStore.getState().animation;
|
||||
if (!animation) return;
|
||||
const isLastTarget =
|
||||
animation.currentIndex >= animation.targets.length - 1;
|
||||
if (isLastTarget) {
|
||||
cameraTourStore.getState().cancel();
|
||||
} else {
|
||||
cameraTourStore.getState().advanceTarget();
|
||||
}
|
||||
});
|
||||
|
||||
// Escape to exit tour.
|
||||
useInputAction("exitTour", () => {
|
||||
cameraTourStore.getState().cancel();
|
||||
});
|
||||
|
||||
useFrame((_state, delta) => {
|
||||
const animation = cameraTourStore.getState().animation;
|
||||
if (!animation) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
border-radius: 4px;
|
||||
background: rgba(3, 82, 147, 0.6);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
|
@ -41,12 +41,26 @@
|
|||
max-width: none;
|
||||
}
|
||||
|
||||
.Speed {
|
||||
.Speed,
|
||||
.CameraMode {
|
||||
flex-shrink: 0;
|
||||
padding: 2px 4px;
|
||||
padding: 3px 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.Field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.Field label {
|
||||
text-transform: uppercase;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { useCallback, useEffect, type ChangeEvent } from "react";
|
||||
import { useInputAction } from "./InputControls";
|
||||
// import { useStore } from "zustand";
|
||||
import {
|
||||
usePlaybackActions,
|
||||
useCurrentTime,
|
||||
|
|
@ -6,10 +8,21 @@ import {
|
|||
useIsPlaying,
|
||||
useRecording,
|
||||
useSpeed,
|
||||
SPEED_OPTIONS,
|
||||
} from "./RecordingProvider";
|
||||
// import {
|
||||
// streamPlaybackStore,
|
||||
// type DemoCameraMode,
|
||||
// } from "../state/streamPlaybackStore";
|
||||
// import { useEngineStoreApi } from "../state/engineStore";
|
||||
import { GrPauseFill, GrPlayFill } from "react-icons/gr";
|
||||
import styles from "./DemoPlaybackControls.module.css";
|
||||
|
||||
const SPEED_OPTIONS = [0.25, 0.5, 1, 2, 4];
|
||||
// const CAMERA_MODE_OPTIONS: { value: DemoCameraMode; label: string }[] = [
|
||||
// { value: "original", label: "Original" },
|
||||
// { value: "freeFly", label: "Free Fly" },
|
||||
// { value: "orbitOverride", label: "Orbit Target" },
|
||||
// ];
|
||||
|
||||
function formatTime(seconds: number): string {
|
||||
const m = Math.floor(seconds / 60);
|
||||
|
|
@ -24,6 +37,8 @@ export function DemoPlaybackControls() {
|
|||
const duration = useDuration();
|
||||
const speed = useSpeed();
|
||||
const { play, pause, seek, setSpeed } = usePlaybackActions();
|
||||
// const cameraMode = useStore(streamPlaybackStore, (s) => s.cameraMode);
|
||||
// const engineStore = useEngineStoreApi();
|
||||
|
||||
// Spacebar toggles play/pause during demo playback.
|
||||
useEffect(() => {
|
||||
|
|
@ -51,6 +66,16 @@ export function DemoPlaybackControls() {
|
|||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [recording, isPlaying, play, pause]);
|
||||
|
||||
useInputAction("decreasePlaybackSpeed", () => {
|
||||
const idx = SPEED_OPTIONS.indexOf(speed);
|
||||
if (idx > 0) setSpeed(SPEED_OPTIONS[idx - 1]);
|
||||
});
|
||||
|
||||
useInputAction("increasePlaybackSpeed", () => {
|
||||
const idx = SPEED_OPTIONS.indexOf(speed);
|
||||
if (idx < SPEED_OPTIONS.length - 1) setSpeed(SPEED_OPTIONS[idx + 1]);
|
||||
});
|
||||
|
||||
const handleSeek = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
seek(parseFloat(e.target.value));
|
||||
|
|
@ -65,22 +90,36 @@ export function DemoPlaybackControls() {
|
|||
[setSpeed],
|
||||
);
|
||||
|
||||
// const handleCameraModeChange = useCallback(
|
||||
// (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
// const newMode = e.target.value as DemoCameraMode;
|
||||
// if (newMode === "orbitOverride") {
|
||||
// // Seed yaw/pitch from current stream camera to avoid a jump.
|
||||
// const cam =
|
||||
// engineStore.getState().playback.streamSnapshot?.camera ?? null;
|
||||
// streamPlaybackStore.setState({
|
||||
// cameraMode: newMode,
|
||||
// orbitOverrideYaw: cam?.yaw ?? 0,
|
||||
// orbitOverridePitch: cam?.pitch ?? 0,
|
||||
// });
|
||||
// } else {
|
||||
// streamPlaybackStore.setState({ cameraMode: newMode });
|
||||
// }
|
||||
// },
|
||||
// [engineStore],
|
||||
// );
|
||||
|
||||
if (!recording || !Number.isFinite(recording.duration)) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.Root}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className={styles.Root}>
|
||||
<button
|
||||
className={styles.PlayPause}
|
||||
onClick={isPlaying ? pause : play}
|
||||
aria-label={isPlaying ? "Pause" : "Play"}
|
||||
autoFocus
|
||||
>
|
||||
{isPlaying ? "\u275A\u275A" : "\u25B6"}
|
||||
{isPlaying ? <GrPauseFill /> : <GrPlayFill />}
|
||||
</button>
|
||||
<span className={styles.Time}>
|
||||
{`${formatTime(currentTime)} / ${formatTime(duration)}`}
|
||||
|
|
@ -94,17 +133,32 @@ export function DemoPlaybackControls() {
|
|||
value={currentTime}
|
||||
onChange={handleSeek}
|
||||
/>
|
||||
<select
|
||||
className={styles.Speed}
|
||||
value={speed}
|
||||
onChange={handleSpeedChange}
|
||||
<div className={styles.Field}>
|
||||
<label htmlFor="playbackSpeed">Speed</label>
|
||||
<select
|
||||
id="playbackSpeed"
|
||||
className={styles.Speed}
|
||||
value={speed}
|
||||
onChange={handleSpeedChange}
|
||||
>
|
||||
{SPEED_OPTIONS.map((s) => (
|
||||
<option key={s} value={s}>
|
||||
{s}x
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{/* <select
|
||||
className={styles.CameraMode}
|
||||
value={cameraMode}
|
||||
onChange={handleCameraModeChange}
|
||||
>
|
||||
{SPEED_OPTIONS.map((s) => (
|
||||
<option key={s} value={s}>
|
||||
{s}x
|
||||
{CAMERA_MODE_OPTIONS.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</select> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,20 +51,24 @@ const EntityLayer = memo(function EntityLayer() {
|
|||
const currentIds = new Set<string>();
|
||||
for (const entity of entities) {
|
||||
currentIds.add(entity.id);
|
||||
cache.set(entity.id, entity);
|
||||
cache.set(entity.id, entity); // eslint-disable-line react-hooks/refs
|
||||
}
|
||||
// Remove entities no longer in the set
|
||||
// eslint-disable-next-line react-hooks/refs
|
||||
for (const id of cache.keys()) {
|
||||
if (!currentIds.has(id)) {
|
||||
cache.delete(id);
|
||||
cache.delete(id); // eslint-disable-line react-hooks/refs
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{[...cache.values()].map((entity) => (
|
||||
<EntityWrapper key={entity.id} entity={entity} />
|
||||
))}
|
||||
{
|
||||
// eslint-disable-next-line react-hooks/refs
|
||||
[...cache.values()].map((entity) => (
|
||||
<EntityWrapper key={entity.id} entity={entity} />
|
||||
))
|
||||
}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -115,7 +119,7 @@ function FlagMarkerSlot({ entity }: { entity: GameEntity }) {
|
|||
: undefined;
|
||||
return ((flags ?? 0) & 0x2) !== 0;
|
||||
});
|
||||
flagRef.current = isFlag;
|
||||
flagRef.current = isFlag; // eslint-disable-line react-hooks/refs
|
||||
|
||||
useFrame(() => {
|
||||
const flags =
|
||||
|
|
|
|||
|
|
@ -79,7 +79,11 @@ export const FloatingLabel = memo(function FloatingLabel({
|
|||
<group ref={groupRef}>
|
||||
{isVisible ? (
|
||||
<Html position={position} center>
|
||||
<div ref={labelRef} className={styles.Label} style={{ color }}>
|
||||
<div
|
||||
ref={labelRef}
|
||||
className={styles.Label}
|
||||
style={{ color, opacity: fadeWithDistance ? 0 : undefined }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Html>
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ export function FogProvider({
|
|||
|
||||
// Initial update
|
||||
useMemo(() => {
|
||||
updateFogUniforms(uniformsRef.current, fogState, 0);
|
||||
updateFogUniforms(uniformsRef.current, fogState, 0); // eslint-disable-line react-hooks/refs
|
||||
}, [fogState]);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useDataSource } from "../state/gameEntityStore";
|
|||
import { useRecording } from "./RecordingProvider";
|
||||
import { AudioProvider } from "./AudioContext";
|
||||
import { CamerasProvider } from "./CamerasProvider";
|
||||
import { InputProducers } from "./InputHandlers";
|
||||
import { InputProducer } from "./InputProducer";
|
||||
import { SceneLighting } from "./SceneLighting";
|
||||
import { ThreeCanvas } from "./ThreeCanvas";
|
||||
import { TickProvider } from "./TickProvider";
|
||||
|
|
@ -14,6 +14,7 @@ import { AudioEnabled } from "./AudioEnabled";
|
|||
import { DebugEnabled } from "./DebugEnabled";
|
||||
import { InputConsumer } from "./InputConsumer";
|
||||
import { CameraTourConsumer } from "./CameraTourConsumer";
|
||||
import { ActiveInputBindings } from "./ActiveInputBindings";
|
||||
|
||||
function createLazy(
|
||||
name: string,
|
||||
|
|
@ -59,7 +60,8 @@ export const GameView = memo(function GameView({
|
|||
<ThreeCanvas dpr={dpr} onCreated={onCreated}>
|
||||
<TickProvider>
|
||||
<CamerasProvider>
|
||||
<InputProducers />
|
||||
<ActiveInputBindings />
|
||||
<InputProducer />
|
||||
<AudioProvider>
|
||||
<SceneLighting />
|
||||
<Suspense>
|
||||
|
|
|
|||
466
src/components/InputBindings.tsx
Normal file
466
src/components/InputBindings.tsx
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
import { useEffect, useMemo } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import {
|
||||
inputControlsStore,
|
||||
notifySubscribers,
|
||||
keySetModifiersMatch,
|
||||
eventModifiersMatch,
|
||||
parseBinding,
|
||||
defaultStateForBinding,
|
||||
defaultDragState,
|
||||
defaultTouchState,
|
||||
type InputMapEntry,
|
||||
type ActionState,
|
||||
type DragState,
|
||||
type KeyState,
|
||||
type ScrollState,
|
||||
type TouchState,
|
||||
type ParsedAction,
|
||||
type Modifier,
|
||||
} from "./InputControls";
|
||||
|
||||
const DRAG_THRESHOLD = 3;
|
||||
|
||||
/**
|
||||
* Parses the input map and attaches event listeners that write to the
|
||||
* InputControls store. Place inside the r3f Canvas.
|
||||
* Multiple InputBindings instances can coexist.
|
||||
*
|
||||
* Keyboard state is tracked centrally in InputControls (module-level
|
||||
* keydown/keyup listeners). This component subscribes to key changes
|
||||
* and derives its action state from them.
|
||||
*/
|
||||
export function InputBindings<T extends string = string>({
|
||||
map,
|
||||
}: {
|
||||
map: readonly InputMapEntry<T>[] | InputMapEntry<T>[];
|
||||
}) {
|
||||
const store = inputControlsStore;
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
|
||||
const bindings = useMemo(() => {
|
||||
// Parse the map.
|
||||
const actions: ParsedAction[] = map.map((entry) => {
|
||||
const keys = Array.isArray(entry.keys) ? entry.keys : [entry.keys];
|
||||
return { name: entry.name, bindings: keys.map(parseBinding) };
|
||||
});
|
||||
|
||||
// Build default action state (applied in useEffect, not here).
|
||||
const initialActions: Record<string, ActionState> = {};
|
||||
for (const action of actions) {
|
||||
initialActions[action.name] = defaultStateForBinding(action.bindings[0]);
|
||||
}
|
||||
|
||||
// Build lookup indices.
|
||||
const keyBindings = new Map<
|
||||
string,
|
||||
{
|
||||
action: ParsedAction;
|
||||
binding: { type: "key"; code: string; modifiers?: Modifier[] };
|
||||
}[]
|
||||
>();
|
||||
const clickBindings: {
|
||||
action: ParsedAction;
|
||||
binding: {
|
||||
type: "click";
|
||||
button?: number;
|
||||
modifiers?: Modifier[];
|
||||
whenPointerLocked?: boolean;
|
||||
};
|
||||
}[] = [];
|
||||
const dragBindings: {
|
||||
action: ParsedAction;
|
||||
binding: { type: "drag"; button?: number; whenPointerLocked?: boolean };
|
||||
}[] = [];
|
||||
const pointerLockMoveBindings: { action: ParsedAction }[] = [];
|
||||
const scrollBindings: { action: ParsedAction }[] = [];
|
||||
const touchBindings: { action: ParsedAction }[] = [];
|
||||
|
||||
for (const action of actions) {
|
||||
for (const binding of action.bindings) {
|
||||
switch (binding.type) {
|
||||
case "key": {
|
||||
let list = keyBindings.get(binding.code);
|
||||
if (!list) {
|
||||
list = [];
|
||||
keyBindings.set(binding.code, list);
|
||||
}
|
||||
list.push({ action, binding });
|
||||
break;
|
||||
}
|
||||
case "click":
|
||||
clickBindings.push({ action, binding });
|
||||
break;
|
||||
case "drag":
|
||||
dragBindings.push({ action, binding });
|
||||
break;
|
||||
case "pointerLockMove":
|
||||
pointerLockMoveBindings.push({ action });
|
||||
break;
|
||||
case "scroll":
|
||||
scrollBindings.push({ action });
|
||||
break;
|
||||
case "touch":
|
||||
touchBindings.push({ action });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a binding's whenPointerLocked constraint is satisfied. */
|
||||
function pointerLockMatches(whenPointerLocked?: boolean): boolean {
|
||||
if (whenPointerLocked == null) return true;
|
||||
return whenPointerLocked === !!document.pointerLockElement;
|
||||
}
|
||||
|
||||
// ── Key action derivation ──
|
||||
|
||||
/**
|
||||
* Derive key action state from the raw pressed-key set.
|
||||
* Called whenever the global key set changes.
|
||||
*/
|
||||
function deriveKeyActions(keys: Set<string>) {
|
||||
const { actions } = store.getState();
|
||||
const updates: Record<string, ActionState> = {};
|
||||
|
||||
for (const [, entries] of keyBindings) {
|
||||
for (const { action, binding } of entries) {
|
||||
const shouldBePressed =
|
||||
keys.has(binding.code) &&
|
||||
keySetModifiersMatch(keys, binding.modifiers);
|
||||
const prev = actions[action.name] as KeyState | undefined;
|
||||
const wasPressed = prev?.pressed ?? false;
|
||||
if (shouldBePressed && !wasPressed) {
|
||||
updates[action.name] = { pressed: true };
|
||||
notifySubscribers(action.name);
|
||||
} else if (!shouldBePressed && wasPressed) {
|
||||
updates[action.name] = { pressed: false };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
store.setState((prev) => ({
|
||||
...prev,
|
||||
actions: { ...prev.actions, ...updates },
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mouse handlers ──
|
||||
|
||||
let mouseDownButton = -1;
|
||||
let mouseDownX = 0;
|
||||
let mouseDownY = 0;
|
||||
let isDragging = false;
|
||||
|
||||
function setAction(name: string, state: ActionState) {
|
||||
store.setState((prev) => ({
|
||||
...prev,
|
||||
actions: { ...prev.actions, [name]: state },
|
||||
}));
|
||||
}
|
||||
|
||||
function handleMouseDown(e: MouseEvent) {
|
||||
const isLocked = !!document.pointerLockElement;
|
||||
|
||||
// Click bindings: set pressed on mousedown.
|
||||
for (const { action, binding } of clickBindings) {
|
||||
if (!pointerLockMatches(binding.whenPointerLocked)) continue;
|
||||
const button = binding.button ?? 0;
|
||||
if (e.button !== button) continue;
|
||||
if (!eventModifiersMatch(e, binding.modifiers)) continue;
|
||||
setAction(action.name, { pressed: true } satisfies KeyState);
|
||||
}
|
||||
|
||||
// Drag tracking only when not pointer-locked (locked movement
|
||||
// is handled by pointerLockMove bindings in handleMouseMove).
|
||||
if (!isLocked) {
|
||||
mouseDownButton = e.button;
|
||||
mouseDownX = e.clientX;
|
||||
mouseDownY = e.clientY;
|
||||
isDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseMove(e: MouseEvent) {
|
||||
// Pointer lock: accumulate deltas for pointerLockMove bindings.
|
||||
// Mutate in place and batch into a single setState.
|
||||
if (document.pointerLockElement) {
|
||||
if (pointerLockMoveBindings.length > 0) {
|
||||
const { actions } = store.getState();
|
||||
const updates: Record<string, ActionState> = {};
|
||||
for (const { action } of pointerLockMoveBindings) {
|
||||
const prev = actions[action.name] as DragState;
|
||||
updates[action.name] = {
|
||||
...prev,
|
||||
deltaX: prev.deltaX + e.movementX,
|
||||
deltaY: prev.deltaY + e.movementY,
|
||||
};
|
||||
}
|
||||
store.setState((prev) => ({
|
||||
...prev,
|
||||
actions: { ...prev.actions, ...updates },
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-locked: check drag threshold.
|
||||
if (mouseDownButton < 0) return;
|
||||
|
||||
if (!isDragging) {
|
||||
const dx = e.clientX - mouseDownX;
|
||||
const dy = e.clientY - mouseDownY;
|
||||
if (Math.abs(dx) < DRAG_THRESHOLD && Math.abs(dy) < DRAG_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
isDragging = true;
|
||||
|
||||
// Crossed drag threshold — cancel matching click bindings.
|
||||
for (const { action, binding } of clickBindings) {
|
||||
if (!pointerLockMatches(binding.whenPointerLocked)) continue;
|
||||
if ((binding.button ?? 0) !== mouseDownButton) continue;
|
||||
const prev = store.getState().actions[action.name] as KeyState;
|
||||
if (prev.pressed) {
|
||||
setAction(action.name, { pressed: false } satisfies KeyState);
|
||||
}
|
||||
}
|
||||
|
||||
for (const { action, binding } of dragBindings) {
|
||||
if (!pointerLockMatches(binding.whenPointerLocked)) continue;
|
||||
if ((binding.button ?? 0) !== mouseDownButton) continue;
|
||||
setAction(action.name, {
|
||||
dragging: true,
|
||||
deltaX: 0,
|
||||
deltaY: 0,
|
||||
startX: mouseDownX,
|
||||
startY: mouseDownY,
|
||||
} satisfies DragState);
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate drag deltas — batch into a single setState.
|
||||
const { actions } = store.getState();
|
||||
const dragUpdates: Record<string, ActionState> = {};
|
||||
for (const { action, binding } of dragBindings) {
|
||||
if (!pointerLockMatches(binding.whenPointerLocked)) continue;
|
||||
if ((binding.button ?? 0) !== mouseDownButton) continue;
|
||||
const prev = actions[action.name] as DragState;
|
||||
dragUpdates[action.name] = {
|
||||
...prev,
|
||||
deltaX: prev.deltaX + e.movementX,
|
||||
deltaY: prev.deltaY + e.movementY,
|
||||
};
|
||||
}
|
||||
if (Object.keys(dragUpdates).length > 0) {
|
||||
store.setState((prev) => ({
|
||||
...prev,
|
||||
actions: { ...prev.actions, ...dragUpdates },
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseUp(e: MouseEvent) {
|
||||
const isLocked = !!document.pointerLockElement;
|
||||
|
||||
// Click bindings: if still pressed (not cancelled by drag),
|
||||
// this is a confirmed click — notify subscribers then release.
|
||||
for (const { action, binding } of clickBindings) {
|
||||
if (!pointerLockMatches(binding.whenPointerLocked)) continue;
|
||||
const button = binding.button ?? 0;
|
||||
if (e.button !== button) continue;
|
||||
const prev = store.getState().actions[action.name] as KeyState;
|
||||
if (prev.pressed) {
|
||||
notifySubscribers(action.name);
|
||||
setAction(action.name, { pressed: false } satisfies KeyState);
|
||||
}
|
||||
}
|
||||
|
||||
// Drag bindings: end dragging (only relevant when not locked).
|
||||
if (!isLocked && e.button === mouseDownButton) {
|
||||
for (const { action, binding } of dragBindings) {
|
||||
if (!pointerLockMatches(binding.whenPointerLocked)) continue;
|
||||
if ((binding.button ?? 0) !== mouseDownButton) continue;
|
||||
const prev = store.getState().actions[action.name] as DragState;
|
||||
if (prev.dragging) {
|
||||
setAction(action.name, defaultDragState());
|
||||
}
|
||||
}
|
||||
mouseDownButton = -1;
|
||||
isDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleWheel(e: WheelEvent) {
|
||||
for (const { action } of scrollBindings) {
|
||||
setAction(action.name, {
|
||||
deltaX: e.deltaX,
|
||||
deltaY: e.deltaY,
|
||||
} satisfies ScrollState);
|
||||
notifySubscribers(action.name);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Touch handlers ──
|
||||
|
||||
let touchId: number | null = null;
|
||||
let lastTouchX = 0;
|
||||
let lastTouchY = 0;
|
||||
|
||||
function handleTouchStart(e: TouchEvent) {
|
||||
if (touchId !== null) return;
|
||||
if (touchBindings.length === 0) return;
|
||||
const touch = e.changedTouches[0];
|
||||
if (!touch) return;
|
||||
touchId = touch.identifier;
|
||||
lastTouchX = touch.clientX;
|
||||
lastTouchY = touch.clientY;
|
||||
for (const { action } of touchBindings) {
|
||||
setAction(action.name, {
|
||||
touching: true,
|
||||
dragging: false,
|
||||
deltaX: 0,
|
||||
deltaY: 0,
|
||||
} satisfies TouchState);
|
||||
}
|
||||
}
|
||||
|
||||
function handleTouchMove(e: TouchEvent) {
|
||||
if (touchId === null) return;
|
||||
for (let i = 0; i < e.changedTouches.length; i++) {
|
||||
const touch = e.changedTouches[i];
|
||||
if (touch.identifier !== touchId) continue;
|
||||
const dx = touch.clientX - lastTouchX;
|
||||
const dy = touch.clientY - lastTouchY;
|
||||
lastTouchX = touch.clientX;
|
||||
lastTouchY = touch.clientY;
|
||||
for (const { action } of touchBindings) {
|
||||
const prev = store.getState().actions[action.name] as TouchState;
|
||||
setAction(action.name, {
|
||||
touching: true,
|
||||
dragging: true,
|
||||
deltaX: prev.deltaX + dx,
|
||||
deltaY: prev.deltaY + dy,
|
||||
} satisfies TouchState);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handleTouchEnd(e: TouchEvent) {
|
||||
if (touchId === null) return;
|
||||
for (let i = 0; i < e.changedTouches.length; i++) {
|
||||
if (e.changedTouches[i].identifier !== touchId) continue;
|
||||
touchId = null;
|
||||
for (const { action } of touchBindings) {
|
||||
setAction(action.name, defaultTouchState());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const actionNames = actions.map((a) => a.name);
|
||||
const hasKeyBindings = keyBindings.size > 0;
|
||||
|
||||
return {
|
||||
actionNames,
|
||||
initialActions,
|
||||
deriveKeyActions,
|
||||
hasKeyBindings,
|
||||
handleMouseDown,
|
||||
handleMouseMove,
|
||||
handleMouseUp,
|
||||
handleWheel,
|
||||
handleTouchStart,
|
||||
handleTouchMove,
|
||||
handleTouchEnd,
|
||||
hasMouseBindings:
|
||||
clickBindings.length > 0 ||
|
||||
dragBindings.length > 0 ||
|
||||
pointerLockMoveBindings.length > 0,
|
||||
hasScrollBindings: scrollBindings.length > 0,
|
||||
hasTouchBindings: touchBindings.length > 0,
|
||||
};
|
||||
}, [map, store]);
|
||||
|
||||
// Initialize action state, subscribe to key changes, and attach
|
||||
// mouse/touch/scroll listeners.
|
||||
useEffect(() => {
|
||||
store.setState((prev) => ({
|
||||
...prev,
|
||||
actions: { ...prev.actions, ...bindings.initialActions },
|
||||
}));
|
||||
|
||||
// Subscribe to global key set changes to derive key actions.
|
||||
let unsubKeys: (() => void) | undefined;
|
||||
if (bindings.hasKeyBindings) {
|
||||
// Derive immediately from current key state.
|
||||
bindings.deriveKeyActions(store.getState().keys);
|
||||
|
||||
unsubKeys = store.subscribe(
|
||||
(state) => state.keys,
|
||||
(keys) => bindings.deriveKeyActions(keys),
|
||||
);
|
||||
}
|
||||
|
||||
if (bindings.hasMouseBindings) {
|
||||
canvas.addEventListener("mousedown", bindings.handleMouseDown);
|
||||
document.addEventListener("mousemove", bindings.handleMouseMove);
|
||||
document.addEventListener("mouseup", bindings.handleMouseUp);
|
||||
}
|
||||
|
||||
if (bindings.hasScrollBindings) {
|
||||
canvas.addEventListener("wheel", bindings.handleWheel, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (bindings.hasTouchBindings) {
|
||||
canvas.addEventListener("touchstart", bindings.handleTouchStart, {
|
||||
passive: true,
|
||||
});
|
||||
document.addEventListener("touchmove", bindings.handleTouchMove, {
|
||||
passive: true,
|
||||
});
|
||||
document.addEventListener("touchend", bindings.handleTouchEnd, {
|
||||
passive: true,
|
||||
});
|
||||
document.addEventListener("touchcancel", bindings.handleTouchEnd, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
unsubKeys?.();
|
||||
|
||||
if (bindings.hasMouseBindings) {
|
||||
canvas.removeEventListener("mousedown", bindings.handleMouseDown);
|
||||
document.removeEventListener("mousemove", bindings.handleMouseMove);
|
||||
document.removeEventListener("mouseup", bindings.handleMouseUp);
|
||||
}
|
||||
|
||||
if (bindings.hasScrollBindings) {
|
||||
canvas.removeEventListener("wheel", bindings.handleWheel);
|
||||
}
|
||||
|
||||
if (bindings.hasTouchBindings) {
|
||||
canvas.removeEventListener("touchstart", bindings.handleTouchStart);
|
||||
document.removeEventListener("touchmove", bindings.handleTouchMove);
|
||||
document.removeEventListener("touchend", bindings.handleTouchEnd);
|
||||
document.removeEventListener("touchcancel", bindings.handleTouchEnd);
|
||||
}
|
||||
|
||||
// Remove this instance's actions from the store.
|
||||
store.setState((prev) => {
|
||||
const nextActions = { ...prev.actions };
|
||||
for (const name of bindings.actionNames) {
|
||||
delete nextActions[name];
|
||||
}
|
||||
return { ...prev, actions: nextActions };
|
||||
});
|
||||
};
|
||||
}, [bindings, store, canvas]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ import type { ClientMove } from "../../relay/types";
|
|||
|
||||
const log = createLogger("InputConsumer");
|
||||
|
||||
const MAX_SPEED = 300;
|
||||
const MAX_SPEED = 270;
|
||||
const LOCAL_MAX_PITCH = Math.PI / 2 - 0.01; // ~89°
|
||||
|
||||
/**
|
||||
|
|
@ -261,6 +261,15 @@ export function InputConsumer() {
|
|||
}
|
||||
}, [liveReady, setMode]);
|
||||
|
||||
// Set input mode to "follow" during orbit override so
|
||||
// MouseAndKeyboardHandler flips drag direction correctly.
|
||||
useEffect(() => {
|
||||
if (isLive) return;
|
||||
return streamPlaybackStore.subscribe((state) => {
|
||||
setMode(state.cameraMode === "orbitOverride" ? "follow" : "local");
|
||||
});
|
||||
}, [isLive, setMode]);
|
||||
|
||||
// ── processTick: send moves at the Torque tick rate (32Hz). ──
|
||||
useTick(() => {
|
||||
if (!activeAdapterRef.current || gameStatus !== "connected" || !liveReady)
|
||||
|
|
@ -453,7 +462,19 @@ export function InputConsumer() {
|
|||
} else {
|
||||
// Local mode: apply input directly to camera.
|
||||
const spState = streamPlaybackStore.getState();
|
||||
if (spState.playback && !spState.freeFlyCamera) return;
|
||||
if (spState.playback) {
|
||||
if (spState.cameraMode === "freeFly") {
|
||||
applyLocalCamera(camera, dYaw, dPitch, x, y, z, frameDelta);
|
||||
} else if (spState.cameraMode === "orbitOverride") {
|
||||
// Accumulate orbit yaw/pitch for StreamingController to read.
|
||||
spState.orbitOverrideYaw += dYaw;
|
||||
spState.orbitOverridePitch = Math.max(
|
||||
-MAX_PITCH,
|
||||
Math.min(MAX_PITCH, spState.orbitOverridePitch + dPitch),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
applyLocalCamera(camera, dYaw, dPitch, x, y, z, frameDelta);
|
||||
return;
|
||||
|
|
|
|||
368
src/components/InputControls.tsx
Normal file
368
src/components/InputControls.tsx
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
import { useEffect, useEffectEvent, useMemo } from "react";
|
||||
import { createStore } from "zustand";
|
||||
import { subscribeWithSelector } from "zustand/middleware";
|
||||
import { useStoreWithEqualityFn } from "zustand/traditional";
|
||||
|
||||
// ── Types ──
|
||||
|
||||
export type Modifier = "Ctrl" | "Shift" | "Alt";
|
||||
|
||||
export type InputBinding =
|
||||
| { type: "key"; code: string; modifiers?: Modifier[] }
|
||||
| {
|
||||
type: "click";
|
||||
button?: number;
|
||||
modifiers?: Modifier[];
|
||||
whenPointerLocked?: boolean;
|
||||
}
|
||||
| { type: "drag"; button?: number; whenPointerLocked?: boolean }
|
||||
| { type: "pointerLockMove" }
|
||||
| { type: "scroll" }
|
||||
| { type: "touch" };
|
||||
|
||||
/** String shorthand: `"KeyW"`, `"Shift-KeyA"`, or an InputBinding object. */
|
||||
export type BindingShorthand = string | InputBinding;
|
||||
|
||||
export type InputMapEntry<T extends string = string> = {
|
||||
name: T;
|
||||
keys: BindingShorthand | BindingShorthand[];
|
||||
};
|
||||
|
||||
export interface KeyState {
|
||||
pressed: boolean;
|
||||
}
|
||||
|
||||
export interface DragState {
|
||||
dragging: boolean;
|
||||
deltaX: number;
|
||||
deltaY: number;
|
||||
startX: number;
|
||||
startY: number;
|
||||
}
|
||||
|
||||
export interface ScrollState {
|
||||
deltaX: number;
|
||||
deltaY: number;
|
||||
}
|
||||
|
||||
export interface TouchState {
|
||||
touching: boolean;
|
||||
dragging: boolean;
|
||||
deltaX: number;
|
||||
deltaY: number;
|
||||
}
|
||||
|
||||
export type ActionState = KeyState | DragState | ScrollState | TouchState;
|
||||
|
||||
/** The full store state: raw keys + derived action state. */
|
||||
export interface InputStoreState {
|
||||
/** Physical key codes currently held down. */
|
||||
keys: Set<string>;
|
||||
/** Derived action state, keyed by action name. */
|
||||
actions: Record<string, ActionState>;
|
||||
}
|
||||
|
||||
// ── Modifier parsing ──
|
||||
|
||||
const MODIFIER_NAMES = new Set<string>(["Ctrl", "Shift", "Alt"]);
|
||||
|
||||
/** All physical key codes that are modifier keys (including Meta for
|
||||
* key-tracking purposes, even though Meta bindings are not supported). */
|
||||
const MODIFIER_CODE_SET = new Set([
|
||||
"MetaLeft",
|
||||
"MetaRight",
|
||||
"ControlLeft",
|
||||
"ControlRight",
|
||||
"ShiftLeft",
|
||||
"ShiftRight",
|
||||
"AltLeft",
|
||||
"AltRight",
|
||||
]);
|
||||
|
||||
export function parseBinding(shorthand: BindingShorthand): InputBinding {
|
||||
if (typeof shorthand !== "string") return shorthand;
|
||||
const parts = shorthand.split("-");
|
||||
const code = parts.pop()!;
|
||||
const modifiers: Modifier[] = [];
|
||||
for (const part of parts) {
|
||||
if (MODIFIER_NAMES.has(part)) {
|
||||
modifiers.push(part as Modifier);
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: "key",
|
||||
code,
|
||||
modifiers: modifiers.length > 0 ? modifiers : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/** Check if the required modifiers match the pressed key set. */
|
||||
export function keySetModifiersMatch(
|
||||
keys: Set<string>,
|
||||
required?: Modifier[],
|
||||
): boolean {
|
||||
const hasCtrl = keys.has("ControlLeft") || keys.has("ControlRight");
|
||||
const hasShift = keys.has("ShiftLeft") || keys.has("ShiftRight");
|
||||
const hasAlt = keys.has("AltLeft") || keys.has("AltRight");
|
||||
return (
|
||||
hasCtrl === (required?.includes("Ctrl") ?? false) &&
|
||||
hasShift === (required?.includes("Shift") ?? false) &&
|
||||
hasAlt === (required?.includes("Alt") ?? false)
|
||||
);
|
||||
}
|
||||
|
||||
/** Check if the required modifiers match a DOM event's modifier flags. */
|
||||
export function eventModifiersMatch(
|
||||
e: KeyboardEvent | MouseEvent,
|
||||
required?: Modifier[],
|
||||
): boolean {
|
||||
const wantCtrl = required?.includes("Ctrl") ?? false;
|
||||
const wantShift = required?.includes("Shift") ?? false;
|
||||
const wantAlt = required?.includes("Alt") ?? false;
|
||||
return (
|
||||
e.ctrlKey === wantCtrl &&
|
||||
e.shiftKey === wantShift &&
|
||||
e.altKey === wantAlt
|
||||
);
|
||||
}
|
||||
|
||||
// ── Default state factories ──
|
||||
|
||||
export function defaultKeyState(): KeyState {
|
||||
return { pressed: false };
|
||||
}
|
||||
|
||||
export function defaultDragState(): DragState {
|
||||
return { dragging: false, deltaX: 0, deltaY: 0, startX: 0, startY: 0 };
|
||||
}
|
||||
|
||||
export function defaultScrollState(): ScrollState {
|
||||
return { deltaX: 0, deltaY: 0 };
|
||||
}
|
||||
|
||||
export function defaultTouchState(): TouchState {
|
||||
return { touching: false, dragging: false, deltaX: 0, deltaY: 0 };
|
||||
}
|
||||
|
||||
export function defaultStateForBinding(binding: InputBinding): ActionState {
|
||||
switch (binding.type) {
|
||||
case "key":
|
||||
case "click":
|
||||
return defaultKeyState();
|
||||
case "drag":
|
||||
case "pointerLockMove":
|
||||
return defaultDragState();
|
||||
case "scroll":
|
||||
return defaultScrollState();
|
||||
case "touch":
|
||||
return defaultTouchState();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Internal types ──
|
||||
|
||||
type ActionCallback = () => void;
|
||||
|
||||
export interface ParsedAction {
|
||||
name: string;
|
||||
bindings: InputBinding[];
|
||||
}
|
||||
|
||||
// ── Module-level store and subscriber registry ──
|
||||
|
||||
export const inputControlsStore = createStore<InputStoreState>()(
|
||||
subscribeWithSelector(() => ({
|
||||
keys: new Set<string>(),
|
||||
actions: {} as Record<string, ActionState>,
|
||||
})),
|
||||
);
|
||||
|
||||
const actionSubscribers = new Map<string, Set<ActionCallback>>();
|
||||
|
||||
export function subscribeAction(
|
||||
action: string,
|
||||
callback: ActionCallback,
|
||||
): () => void {
|
||||
let set = actionSubscribers.get(action);
|
||||
if (!set) {
|
||||
set = new Set();
|
||||
actionSubscribers.set(action, set);
|
||||
}
|
||||
set.add(callback);
|
||||
return () => {
|
||||
set!.delete(callback);
|
||||
if (set!.size === 0) actionSubscribers.delete(action);
|
||||
};
|
||||
}
|
||||
|
||||
export function notifySubscribers(action: string) {
|
||||
const set = actionSubscribers.get(action);
|
||||
if (set) {
|
||||
for (const cb of set) cb();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Centralized key tracking ──
|
||||
// A single pair of keydown/keyup listeners manages the `keys` Set.
|
||||
// InputBindings instances subscribe to key changes to derive actions,
|
||||
// instead of each attaching their own keyboard listeners.
|
||||
|
||||
// Input types that accept text entry — all keys should be ignored.
|
||||
const TEXT_INPUT_TYPES = new Set([
|
||||
"text",
|
||||
"search",
|
||||
"url",
|
||||
"tel",
|
||||
"email",
|
||||
"password",
|
||||
"number",
|
||||
"date",
|
||||
"datetime-local",
|
||||
"month",
|
||||
"week",
|
||||
"time",
|
||||
]);
|
||||
|
||||
// Keys that interactive (non-text) elements use natively.
|
||||
const INTERACTIVE_KEYS = new Set([
|
||||
"Space",
|
||||
"Enter",
|
||||
"NumpadEnter",
|
||||
"ArrowUp",
|
||||
"ArrowDown",
|
||||
"ArrowLeft",
|
||||
"ArrowRight",
|
||||
]);
|
||||
|
||||
function shouldIgnoreForFocus(e: KeyboardEvent): boolean {
|
||||
// Tab: capture when pointer is locked (prevent focus shift), otherwise
|
||||
// let the browser handle focus navigation.
|
||||
if (e.code === "Tab") {
|
||||
if (document.pointerLockElement) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const el = document.activeElement;
|
||||
if (!el || el === document.body) return false;
|
||||
|
||||
const tag = el.tagName;
|
||||
if ((el as HTMLElement).isContentEditable) return true;
|
||||
if (tag === "TEXTAREA") return true;
|
||||
if (tag === "INPUT") {
|
||||
const type = (el as HTMLInputElement).type.toLowerCase();
|
||||
if (TEXT_INPUT_TYPES.has(type)) return true;
|
||||
return INTERACTIVE_KEYS.has(e.code);
|
||||
}
|
||||
if (
|
||||
tag === "BUTTON" ||
|
||||
tag === "SELECT" ||
|
||||
tag === "A" ||
|
||||
tag === "SUMMARY"
|
||||
) {
|
||||
return INTERACTIVE_KEYS.has(e.code);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleGlobalKeyDown(e: KeyboardEvent) {
|
||||
// Meta (Cmd) is not supported as a modifier — macOS doesn't fire
|
||||
// reliable keyup events while it's held, causing stale input state.
|
||||
if (e.metaKey) return;
|
||||
if (shouldIgnoreForFocus(e)) return;
|
||||
|
||||
const { keys: prevKeys } = inputControlsStore.getState();
|
||||
if (prevKeys.has(e.code)) return; // Already tracked (repeat).
|
||||
|
||||
const nextKeys = new Set(prevKeys);
|
||||
nextKeys.add(e.code);
|
||||
inputControlsStore.setState((prev) => ({ ...prev, keys: nextKeys }));
|
||||
}
|
||||
|
||||
function handleGlobalKeyUp(e: KeyboardEvent) {
|
||||
const { keys: prevKeys } = inputControlsStore.getState();
|
||||
if (!prevKeys.has(e.code)) return;
|
||||
|
||||
const nextKeys = new Set(prevKeys);
|
||||
nextKeys.delete(e.code);
|
||||
|
||||
// macOS Cocoa quirk: keyup not fired for keys released while Cmd held.
|
||||
// Clear non-modifier keys since we can't trust their state.
|
||||
if (e.code === "MetaLeft" || e.code === "MetaRight") {
|
||||
for (const code of nextKeys) {
|
||||
if (!MODIFIER_CODE_SET.has(code)) {
|
||||
nextKeys.delete(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inputControlsStore.setState((prev) => ({ ...prev, keys: nextKeys }));
|
||||
}
|
||||
|
||||
function handleGlobalBlur() {
|
||||
const { keys } = inputControlsStore.getState();
|
||||
if (keys.size === 0) return;
|
||||
inputControlsStore.setState((prev) => ({
|
||||
...prev,
|
||||
keys: new Set<string>(),
|
||||
}));
|
||||
}
|
||||
|
||||
// Attach once at module load.
|
||||
window.addEventListener("keydown", handleGlobalKeyDown);
|
||||
window.addEventListener("keyup", handleGlobalKeyUp);
|
||||
window.addEventListener("blur", handleGlobalBlur);
|
||||
|
||||
// ── Public hooks ──
|
||||
|
||||
/** Reactive selector for input action state. */
|
||||
export function useInputControls<T>(
|
||||
selector: (state: Record<string, ActionState>) => T,
|
||||
): T {
|
||||
return useStoreWithEqualityFn(inputControlsStore, (s) => selector(s.actions));
|
||||
}
|
||||
|
||||
/** Imperative access to input state for useFrame callbacks. */
|
||||
export function useInputState() {
|
||||
return useMemo(
|
||||
() =>
|
||||
[
|
||||
inputControlsStore.subscribe,
|
||||
() => inputControlsStore.getState().actions,
|
||||
] as const,
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/** Zero accumulated deltas for drag, scroll, and touch actions.
|
||||
* Does NOT reset `dragging` — that is managed by binding lifecycle.
|
||||
* Call after reading deltas in your useFrame. */
|
||||
export function clearInputDeltas() {
|
||||
const { actions } = inputControlsStore.getState();
|
||||
const updates: Record<string, ActionState> = {};
|
||||
for (const [name, s] of Object.entries(actions)) {
|
||||
if ("deltaX" in s && (s.deltaX !== 0 || s.deltaY !== 0)) {
|
||||
updates[name] = { ...s, deltaX: 0, deltaY: 0 };
|
||||
}
|
||||
}
|
||||
if (Object.keys(updates).length > 0) {
|
||||
inputControlsStore.setState((prev) => ({
|
||||
...prev,
|
||||
actions: { ...prev.actions, ...updates },
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback for a one-shot action (key press, click, etc.).
|
||||
* The callback always sees current closure state via useEffectEvent.
|
||||
*/
|
||||
export function useInputAction(action: string, callback: () => void) {
|
||||
const stableCallback = useEffectEvent(callback);
|
||||
|
||||
useEffect(() => {
|
||||
return subscribeAction(action, stableCallback);
|
||||
}, [action]);
|
||||
}
|
||||
|
|
@ -6,13 +6,9 @@ import {
|
|||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { KeyboardControls } from "@react-three/drei";
|
||||
import { JoystickProvider } from "./JoystickContext";
|
||||
import { useTouchDevice } from "./useTouchDevice";
|
||||
import {
|
||||
MouseAndKeyboardHandler,
|
||||
KEYBOARD_CONTROLS,
|
||||
} from "./MouseAndKeyboardHandler";
|
||||
import { MouseAndKeyboardHandler } from "./MouseAndKeyboardHandler";
|
||||
import {
|
||||
InputContext,
|
||||
type InputFrame,
|
||||
|
|
@ -36,14 +32,12 @@ export function InputProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
return (
|
||||
<InputContext.Provider value={{ moveQueue, onInput, mode, setMode }}>
|
||||
<KeyboardControls map={KEYBOARD_CONTROLS}>
|
||||
<JoystickProvider>{children}</JoystickProvider>
|
||||
</KeyboardControls>
|
||||
<JoystickProvider>{children}</JoystickProvider>
|
||||
</InputContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function InputProducers() {
|
||||
export function InputProducer() {
|
||||
const isTouch = useTouchDevice();
|
||||
|
||||
return (
|
||||
|
|
@ -57,6 +51,3 @@ export function InputProducers() {
|
|||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/** @deprecated Use `InputProducers` instead. */
|
||||
export const InputHandlers = InputProducers;
|
||||
|
|
@ -74,6 +74,8 @@ export const InspectorControls = memo(function InspectorControls({
|
|||
setAnimationEnabled,
|
||||
fpsLimit,
|
||||
setFpsLimit,
|
||||
showInputOverlay,
|
||||
setShowInputOverlay,
|
||||
} = useSettings();
|
||||
const {
|
||||
speedMultiplier,
|
||||
|
|
@ -329,6 +331,22 @@ export const InspectorControls = memo(function InspectorControls({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.CheckboxField}>
|
||||
<input
|
||||
id="showInputOverlayInput"
|
||||
type="checkbox"
|
||||
checked={showInputOverlay}
|
||||
onChange={(event) => {
|
||||
setShowInputOverlay(event.target.checked);
|
||||
}}
|
||||
/>
|
||||
<label
|
||||
className={styles.Label}
|
||||
htmlFor="showInputOverlayInput"
|
||||
>
|
||||
Show input overlay
|
||||
</label>
|
||||
</div>
|
||||
</Accordion>
|
||||
<Accordion value="audio" label="Audio">
|
||||
<div className={styles.CheckboxField}>
|
||||
|
|
|
|||
|
|
@ -14,9 +14,14 @@
|
|||
display: flex;
|
||||
gap: 4px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* .Column[data-height="compact"] {
|
||||
gap: 2px;
|
||||
} */
|
||||
|
||||
.Row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
|
|
@ -27,29 +32,153 @@
|
|||
width: 32px;
|
||||
}
|
||||
|
||||
.Sep {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.Key {
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
flex: 1 0 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 8px;
|
||||
align-items: stretch;
|
||||
margin: 0 auto;
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.Key[data-pressed="true"] {
|
||||
background: rgba(52, 187, 171, 0.6);
|
||||
border-color: rgba(35, 253, 220, 0.5);
|
||||
/* .Column[data-height="compact"] .Key {
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
height: 26px;
|
||||
} */
|
||||
|
||||
.Key[data-size="auto"] {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.Key[data-size="fill"] {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.Key[data-disabled="true"] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.Key[data-pressed="true"]:not([data-disabled="true"]) {
|
||||
background: rgba(35, 145, 132, 0.6);
|
||||
border-color: rgba(24, 197, 171, 0.4);
|
||||
}
|
||||
|
||||
.Label {
|
||||
min-width: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 6px;
|
||||
background: rgba(200, 200, 200, 0.1);
|
||||
}
|
||||
|
||||
.Key[data-pressed="true"]:not([data-disabled="true"]) .Label {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.Label[data-size="auto"] {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.Label[data-size="fill"] {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.Label:first-child {
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.Label:last-child {
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* .Column[data-height="compact"] .Label:first-child {
|
||||
padding-right: 5px;
|
||||
} */
|
||||
|
||||
/* .Column[data-height="compact"] .Label:last-child {
|
||||
padding-left: 5px;
|
||||
} */
|
||||
|
||||
.Key[data-pressed="true"]:not([data-disabled="true"]) .Label {
|
||||
border-color: rgba(24, 197, 171, 0.4);
|
||||
color: rgba(162, 255, 222, 0.8);
|
||||
}
|
||||
|
||||
.MultiInput {
|
||||
font-family: var(--monospace-font);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.Input {
|
||||
min-width: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 0.8;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
/* .Column[data-height="compact"] .Key .Input {
|
||||
min-width: 26px;
|
||||
} */
|
||||
|
||||
.Input[data-size="auto"],
|
||||
.MultiInput[data-size="auto"] {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.Input[data-size="fill"],
|
||||
.MultiInput[data-size="fill"] {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.Key[data-pressed="true"]:not([data-disabled="true"]) .Input {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.Arrow {
|
||||
margin-right: 3px;
|
||||
.MultiInput .Input {
|
||||
flex: 0 0 auto;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ColumnLabel {
|
||||
font-size: 9px;
|
||||
color: rgb(255, 255, 255, 0.5);
|
||||
text-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
|
||||
text-transform: uppercase;
|
||||
padding: 2px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.PlayPauseIcon {
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.MouseIcon {
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
/* .Column[data-height="compact"] .Key .MouseIcon {
|
||||
font-size: 16px;
|
||||
} */
|
||||
|
||||
.Input:has(.MouseIcon) {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,82 +1,463 @@
|
|||
import { useKeyboardControls } from "@react-three/drei";
|
||||
import { type ControlName } from "./MouseAndKeyboardHandler";
|
||||
import { useRecording } from "./RecordingProvider";
|
||||
import { useLiveSelector } from "../state/liveConnectionStore";
|
||||
import {
|
||||
ScrollState,
|
||||
useInputControls,
|
||||
type ActionState,
|
||||
type DragState,
|
||||
type KeyState,
|
||||
} from "./InputControls";
|
||||
import {
|
||||
SPEED_OPTIONS,
|
||||
useIsPlaying,
|
||||
useRecording,
|
||||
useSpeed,
|
||||
} from "./RecordingProvider";
|
||||
import { useInputMode } from "./InputContext";
|
||||
import { useCameraTour } from "../state/cameraTourStore";
|
||||
import {
|
||||
useDataSource,
|
||||
useGameEntitiesByRenderType,
|
||||
} from "../state/gameEntityStore";
|
||||
import { FaAngleDoubleDown, FaAngleDoubleUp } from "react-icons/fa";
|
||||
import { PiMouseLeftClickFill, PiMouseScroll } from "react-icons/pi";
|
||||
import { ReactNode, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
FaArrowDown,
|
||||
FaArrowLeft,
|
||||
FaArrowRight,
|
||||
FaArrowUp,
|
||||
} from "react-icons/fa6";
|
||||
import { GrPauseFill, GrPlayFill } from "react-icons/gr";
|
||||
import { usePointerLocked } from "./usePointerLocked";
|
||||
import styles from "./KeyboardOverlay.module.css";
|
||||
import { useControls } from "./SettingsProvider";
|
||||
import { MdSwipe } from "react-icons/md";
|
||||
|
||||
export function KeyboardOverlay() {
|
||||
const recording = useRecording();
|
||||
const liveReady = useLiveSelector((s) => s.liveReady);
|
||||
const forward = useKeyboardControls<ControlName>((s) => s.forward);
|
||||
const backward = useKeyboardControls<ControlName>((s) => s.backward);
|
||||
const left = useKeyboardControls<ControlName>((s) => s.left);
|
||||
const right = useKeyboardControls<ControlName>((s) => s.right);
|
||||
const up = useKeyboardControls<ControlName>((s) => s.up);
|
||||
const down = useKeyboardControls<ControlName>((s) => s.down);
|
||||
const lookUp = useKeyboardControls<ControlName>((s) => s.lookUp);
|
||||
const lookDown = useKeyboardControls<ControlName>((s) => s.lookDown);
|
||||
const lookLeft = useKeyboardControls<ControlName>((s) => s.lookLeft);
|
||||
const lookRight = useKeyboardControls<ControlName>((s) => s.lookRight);
|
||||
type InputState = Record<string, ActionState>;
|
||||
type ActionSelector = (state: InputState) => boolean;
|
||||
|
||||
// Show when no recording (map browsing) or during live mode once ready.
|
||||
// Hidden during demo playback and during live map transitions.
|
||||
if (recording && recording.source !== "live") return null;
|
||||
if (recording?.source === "live" && !liveReady) return null;
|
||||
function actionPressed(state: InputState, name: string): boolean {
|
||||
const s = state[name];
|
||||
return s != null && "pressed" in s && (s as KeyState).pressed;
|
||||
}
|
||||
|
||||
function Key({
|
||||
action,
|
||||
input,
|
||||
label,
|
||||
labelPosition = "hidden",
|
||||
labelSize = "fill",
|
||||
inputSize = "fill",
|
||||
size = "fill",
|
||||
disabled = false,
|
||||
debounce,
|
||||
}: {
|
||||
action: string | ActionSelector;
|
||||
input: ReactNode;
|
||||
label: ReactNode;
|
||||
labelPosition?: "left" | "right" | "hidden";
|
||||
labelSize?: "auto" | "fill";
|
||||
inputSize?: "auto" | "fill";
|
||||
size?: "auto" | "fill";
|
||||
debounce?: number;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
// Debounce state: when the raw value goes false within the debounce
|
||||
// window, the selector keeps returning true (no re-render). A timer
|
||||
// triggers one final re-render after the window expires.
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||
const [held, setHeld] = useState(false);
|
||||
|
||||
const baseSelector =
|
||||
typeof action === "function"
|
||||
? action
|
||||
: (s: InputState) => actionPressed(s, action);
|
||||
|
||||
const rawIsPressed = useInputControls(baseSelector);
|
||||
|
||||
useEffect(() => {
|
||||
if (!debounce) return;
|
||||
if (rawIsPressed) {
|
||||
clearTimeout(timerRef.current);
|
||||
timerRef.current = undefined;
|
||||
setHeld(true);
|
||||
} else {
|
||||
timerRef.current = setTimeout(() => {
|
||||
timerRef.current = undefined;
|
||||
setHeld(false);
|
||||
}, debounce);
|
||||
return () => clearTimeout(timerRef.current);
|
||||
}
|
||||
}, [rawIsPressed, debounce]);
|
||||
|
||||
const isPressed = debounce ? held : rawIsPressed;
|
||||
|
||||
return (
|
||||
<div className={styles.Root}>
|
||||
<div
|
||||
className={styles.Key}
|
||||
data-pressed={isPressed}
|
||||
data-size={size}
|
||||
data-disabled={disabled}
|
||||
>
|
||||
{labelPosition === "left" ? (
|
||||
<span className={styles.Label} data-size={labelSize}>
|
||||
{label}
|
||||
</span>
|
||||
) : null}
|
||||
{Array.isArray(input) ? (
|
||||
<div className={styles.MultiInput} data-size={inputSize}>
|
||||
{input.map((input, i) => (
|
||||
<span className={styles.Input} key={i}>
|
||||
{input}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className={styles.Input} data-size={inputSize}>
|
||||
{input}
|
||||
</span>
|
||||
)}
|
||||
{labelPosition === "right" ? (
|
||||
<span className={styles.Label} data-size={labelSize}>
|
||||
{label}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PointerLockKey() {
|
||||
const isPointerLocked = usePointerLocked();
|
||||
// When pointer lock exits, briefly keep showing the "Unlock mouse" UI
|
||||
// so the Esc key appears highlighted (the browser consumes the keydown
|
||||
// so we can't detect it directly).
|
||||
const [justUnlocked, setJustUnlocked] = useState(false);
|
||||
const wasLockedRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (wasLockedRef.current && !isPointerLocked) {
|
||||
setJustUnlocked(true);
|
||||
const id = setTimeout(() => setJustUnlocked(false), 150);
|
||||
return () => clearTimeout(id);
|
||||
}
|
||||
wasLockedRef.current = isPointerLocked;
|
||||
}, [isPointerLocked]);
|
||||
|
||||
const showLockedUI = isPointerLocked || justUnlocked;
|
||||
|
||||
return (
|
||||
<Key
|
||||
action={showLockedUI ? () => justUnlocked : "canvasClick"}
|
||||
label={showLockedUI ? "Unlock mouse" : "Capture mouse"}
|
||||
input={
|
||||
showLockedUI ? (
|
||||
"Esc"
|
||||
) : (
|
||||
<PiMouseLeftClickFill className={styles.MouseIcon} />
|
||||
)
|
||||
}
|
||||
labelPosition="right"
|
||||
inputSize="auto"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function MoveKeys() {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Row}>
|
||||
<div className={styles.Spacer} />
|
||||
<div className={styles.Key} data-pressed={forward}>
|
||||
W
|
||||
</div>
|
||||
<Key action="moveForward" input="W" label="Forward" />
|
||||
<div className={styles.Spacer} />
|
||||
</div>
|
||||
<div className={styles.Row}>
|
||||
<div className={styles.Key} data-pressed={left}>
|
||||
A
|
||||
</div>
|
||||
<div className={styles.Key} data-pressed={backward}>
|
||||
S
|
||||
</div>
|
||||
<div className={styles.Key} data-pressed={right}>
|
||||
D
|
||||
</div>
|
||||
<Key action="moveLeft" input="A" label="Strafe left" />
|
||||
<Key action="moveBackward" input="S" label="Backward" />
|
||||
<Key action="moveRight" input="D" label="Strafe right" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Row}>
|
||||
<div className={styles.Key} data-pressed={up}>
|
||||
<span className={styles.Arrow}>↑</span> Space
|
||||
</div>
|
||||
<Key
|
||||
action="moveUp"
|
||||
input="E"
|
||||
label={<FaAngleDoubleUp />}
|
||||
labelPosition="left"
|
||||
labelSize="auto"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.Row}>
|
||||
<div className={styles.Key} data-pressed={down}>
|
||||
<span className={styles.Arrow}>↓</span> Shift
|
||||
</div>
|
||||
<Key
|
||||
action="moveDown"
|
||||
input="Q"
|
||||
label={<FaAngleDoubleDown />}
|
||||
labelPosition="left"
|
||||
labelSize="auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Row}>
|
||||
<div className={styles.Spacer} />
|
||||
<div className={styles.Key} data-pressed={lookUp}>
|
||||
↑
|
||||
</div>
|
||||
<div className={styles.Spacer} />
|
||||
</div>
|
||||
<div className={styles.Row}>
|
||||
<div className={styles.Key} data-pressed={lookLeft}>
|
||||
←
|
||||
</div>
|
||||
<div className={styles.Key} data-pressed={lookDown}>
|
||||
↓
|
||||
</div>
|
||||
<div className={styles.Key} data-pressed={lookRight}>
|
||||
→
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function LookKeys() {
|
||||
return (
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Row}>
|
||||
<div className={styles.Spacer} />
|
||||
<Key action="lookUp" input={<FaArrowUp />} label="Look up" />
|
||||
<div className={styles.Spacer} />
|
||||
</div>
|
||||
<div className={styles.Row}>
|
||||
<Key action="lookLeft" input={<FaArrowLeft />} label="Look left" />
|
||||
<Key action="lookDown" input={<FaArrowDown />} label="Look down" />
|
||||
<Key action="lookRight" input={<FaArrowRight />} label="Look right" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FlySpeedKey() {
|
||||
const { speedMultiplier } = useControls();
|
||||
const [speedMultiplierChanged, setSpeedMultiplierChanged] = useState<
|
||||
boolean | null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setSpeedMultiplierChanged((value) => (value == null ? false : true));
|
||||
const timeoutId = setTimeout(() => {
|
||||
setSpeedMultiplierChanged(false);
|
||||
}, 100);
|
||||
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [speedMultiplier]);
|
||||
|
||||
return (
|
||||
<Key
|
||||
action={(s) =>
|
||||
((s.adjustSpeed as ScrollState)?.deltaY ?? 0) !== 0 &&
|
||||
(speedMultiplierChanged ?? false)
|
||||
}
|
||||
debounce={50}
|
||||
label="Adjust speed"
|
||||
input={<PiMouseScroll className={styles.MouseIcon} />}
|
||||
labelPosition="right"
|
||||
inputSize="auto"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function RotateCameraKey() {
|
||||
return (
|
||||
<Key
|
||||
action={(s) => (s.dragLook as DragState | undefined)?.dragging ?? false}
|
||||
input={<MdSwipe className={styles.MouseIcon} />}
|
||||
label="Rotate camera"
|
||||
labelPosition="right"
|
||||
inputSize="auto"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SelectCameraKey() {
|
||||
const dataSource = useDataSource();
|
||||
const isMapMode = dataSource === "map";
|
||||
const cameraEntities = useGameEntitiesByRenderType("Camera");
|
||||
const cameraCount = isMapMode ? cameraEntities.length : 0;
|
||||
|
||||
return (
|
||||
<Key
|
||||
action={(s) =>
|
||||
Array.from({ length: cameraCount }, (_, i) =>
|
||||
actionPressed(s, `camera${i + 1}`),
|
||||
).some((pressed) => pressed)
|
||||
}
|
||||
input={
|
||||
cameraCount === 1 ? "1" : <>1 – {cameraCount}</>
|
||||
}
|
||||
label="Select camera"
|
||||
labelPosition="right"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function FreeFlyOverlay() {
|
||||
const isPointerLocked = usePointerLocked();
|
||||
const dataSource = useDataSource();
|
||||
const isMapMode = dataSource === "map";
|
||||
const cameraEntities = useGameEntitiesByRenderType("Camera");
|
||||
const cameraCount = isMapMode ? cameraEntities.length : 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<MoveKeys />
|
||||
<LookKeys />
|
||||
<div className={styles.Column} data-height="compact">
|
||||
<div className={styles.Row}>
|
||||
<FlySpeedKey />
|
||||
</div>
|
||||
<div className={styles.Row}>
|
||||
<PointerLockKey />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.Column} data-height="compact">
|
||||
{!isPointerLocked ? (
|
||||
<div className={styles.Row}>
|
||||
<RotateCameraKey />
|
||||
</div>
|
||||
) : null}
|
||||
{cameraCount > 0 && (
|
||||
<div className={styles.Row}>
|
||||
<SelectCameraKey />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function DemoOverlay() {
|
||||
const isPlaying = useIsPlaying();
|
||||
const speed = useSpeed();
|
||||
|
||||
const nextSpeedIndex = SPEED_OPTIONS.indexOf(speed) + 1;
|
||||
const prevSpeedIndex = SPEED_OPTIONS.indexOf(speed) - 1;
|
||||
const atMaxSpeed = nextSpeedIndex >= SPEED_OPTIONS.length;
|
||||
const atMinSpeed = prevSpeedIndex < 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Row}>
|
||||
<Key
|
||||
action="decreasePlaybackSpeed"
|
||||
label="Slow down"
|
||||
input={["<", ","]}
|
||||
labelPosition="right"
|
||||
disabled={atMinSpeed}
|
||||
/>
|
||||
<Key
|
||||
action="playPause"
|
||||
label={
|
||||
isPlaying ? (
|
||||
<GrPauseFill className={styles.PlayPauseIcon} />
|
||||
) : (
|
||||
<GrPlayFill className={styles.PlayPauseIcon} />
|
||||
)
|
||||
}
|
||||
input="Space"
|
||||
labelPosition="left"
|
||||
size="auto"
|
||||
/>
|
||||
<Key
|
||||
action="increasePlaybackSpeed"
|
||||
input={[">", "."]}
|
||||
label="Speed up"
|
||||
labelPosition="left"
|
||||
disabled={atMaxSpeed}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Row}>
|
||||
<PointerLockKey />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TourOverlay() {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Row}>
|
||||
<Key
|
||||
action="nextStop"
|
||||
label="Skip to next stop"
|
||||
input={<PiMouseLeftClickFill className={styles.MouseIcon} />}
|
||||
labelPosition="right"
|
||||
/>
|
||||
<Key
|
||||
action="exitTour"
|
||||
label="Exit tour"
|
||||
input="Esc"
|
||||
labelPosition="right"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ObserverOverlay() {
|
||||
const inputMode = useInputMode();
|
||||
const isPointerLocked = usePointerLocked();
|
||||
return (
|
||||
<>
|
||||
{inputMode === "fly" ? <MoveKeys /> : null}
|
||||
<LookKeys />
|
||||
<div className={styles.Column} data-height="compact">
|
||||
{inputMode === "fly" ? (
|
||||
<div className={styles.Row}>
|
||||
<FlySpeedKey />
|
||||
</div>
|
||||
) : null}
|
||||
<div className={styles.Row}>
|
||||
<PointerLockKey />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.Column} data-height="compact">
|
||||
{!isPointerLocked ? (
|
||||
<div className={styles.Row}>
|
||||
<RotateCameraKey />
|
||||
</div>
|
||||
) : null}
|
||||
{inputMode === "follow" && isPointerLocked ? (
|
||||
<div className={styles.Row}>
|
||||
<Key
|
||||
action="nextPlayer"
|
||||
label="Next player"
|
||||
input={<PiMouseLeftClickFill className={styles.MouseIcon} />}
|
||||
labelPosition="right"
|
||||
inputSize="auto"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div className={styles.Row}>
|
||||
<Key
|
||||
action="toggleObserverMode"
|
||||
label={inputMode === "follow" ? "Fly mode" : "Follow mode"}
|
||||
input="Space"
|
||||
labelPosition="right"
|
||||
inputSize="auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function KeyboardOverlay() {
|
||||
const recording = useRecording();
|
||||
const inputMode = useInputMode();
|
||||
|
||||
const isTourActive = useCameraTour((s) => s.animation !== null);
|
||||
|
||||
const isDemo = recording?.source === "demo";
|
||||
const isLive = recording?.source === "live";
|
||||
const isMap = !recording;
|
||||
|
||||
const isLiveObserver =
|
||||
isLive && (inputMode === "fly" || inputMode === "follow");
|
||||
|
||||
const showFreeFly = isMap && !isTourActive;
|
||||
|
||||
return (
|
||||
<div className={styles.Root}>
|
||||
{showFreeFly && <FreeFlyOverlay />}
|
||||
{isLiveObserver && <ObserverOverlay />}
|
||||
{isDemo && <DemoOverlay />}
|
||||
{isTourActive && <TourOverlay />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,8 +104,10 @@
|
|||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.ToggleSidebarButton:active {
|
||||
.ToggleSidebarButton:active,
|
||||
.ToggleSidebarButton[data-active="true"] {
|
||||
opacity: 1;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.ToggleSidebarButton[data-orientation="top"] {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import {
|
|||
CurrentMission,
|
||||
useMissionQueryState,
|
||||
} from "@/src/components/useQueryParams";
|
||||
import { InputProvider } from "./InputHandlers";
|
||||
import { InputProvider } from "./InputProducer";
|
||||
import { VisualInput } from "./VisualInput";
|
||||
import { LoadingIndicator } from "./LoadingIndicator";
|
||||
import { engineStore } from "../state/engineStore";
|
||||
|
|
@ -180,6 +180,18 @@ export function MapInspector() {
|
|||
}
|
||||
}, [isTouch, isTourActive, setSidebarOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.code === "Backslash" && (e.metaKey || e.ctrlKey)) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setSidebarOpen((open) => !open);
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, [setSidebarOpen]);
|
||||
|
||||
const loadingProgress = missionLoadingProgress;
|
||||
const isLoading = loadingProgress < 1;
|
||||
|
||||
|
|
@ -220,12 +232,7 @@ export function MapInspector() {
|
|||
return (
|
||||
<main className={styles.Frame}>
|
||||
<RecordingProvider>
|
||||
<header
|
||||
className={styles.Toolbar}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<header className={styles.Toolbar}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.ToggleSidebarButton}
|
||||
|
|
@ -286,13 +293,7 @@ export function MapInspector() {
|
|||
</header>
|
||||
{sidebarOpen ? <div className={styles.Backdrop} /> : null}
|
||||
<Activity mode={sidebarOpen ? "visible" : "hidden"}>
|
||||
<div
|
||||
className={styles.Sidebar}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
data-open={sidebarOpen}
|
||||
>
|
||||
<div className={styles.Sidebar} data-open={sidebarOpen}>
|
||||
<InspectorControls
|
||||
missionName={missionName}
|
||||
missionType={missionType}
|
||||
|
|
@ -342,12 +343,7 @@ export function MapInspector() {
|
|||
)}
|
||||
</div>
|
||||
</InputProvider>
|
||||
<footer
|
||||
className={styles.PlayerBar}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<footer className={styles.PlayerBar}>
|
||||
{recording?.source === "demo" ? (
|
||||
<Suspense>
|
||||
<DemoPlaybackControls />
|
||||
|
|
|
|||
|
|
@ -10,6 +10,33 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.TourAllButton {
|
||||
align-self: flex-start;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 6px;
|
||||
padding: 10px 14px;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.TourAllButton:hover {
|
||||
/* background: rgba(2, 93, 153, 0.4); */
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.TourAllButton:active {
|
||||
/* background: rgba(0, 85, 177, 0.5); */
|
||||
}
|
||||
|
||||
.CategoryHeader {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import styles from "./MapTourPanel.module.css";
|
|||
import { BsPlayFill } from "react-icons/bs";
|
||||
import { HiMiniArrowLeftEndOnRectangle } from "react-icons/hi2";
|
||||
|
||||
const ALL_FEATURES_TOUR = "__all__";
|
||||
|
||||
function selectTourState(state: {
|
||||
animation: {
|
||||
targets: TourTarget[];
|
||||
|
|
@ -51,6 +53,28 @@ export function MapTourPanel() {
|
|||
);
|
||||
const tourState = useCameraTour(selectTourState, tourStateEqual);
|
||||
|
||||
const allTargets = useMemo(() => {
|
||||
// Build a lookup from target → category index for sorting by type.
|
||||
const categoryIndex = new Map<TourTarget, number>();
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
for (const target of categories[i].targets) {
|
||||
categoryIndex.set(target, i);
|
||||
}
|
||||
}
|
||||
// Sort by [team, type, name] with "no team" (undefined/0) last.
|
||||
return categories
|
||||
.flatMap((c) => c.targets)
|
||||
.sort((a, b) => {
|
||||
const aTeam = a.teamId != null && a.teamId > 0 ? a.teamId : Infinity;
|
||||
const bTeam = b.teamId != null && b.teamId > 0 ? b.teamId : Infinity;
|
||||
if (aTeam !== bTeam) return aTeam - bTeam;
|
||||
const aCat = categoryIndex.get(a) ?? 0;
|
||||
const bCat = categoryIndex.get(b) ?? 0;
|
||||
if (aCat !== bCat) return aCat - bCat;
|
||||
return a.label.localeCompare(b.label);
|
||||
});
|
||||
}, [categories]);
|
||||
|
||||
if (categories.length === 0) {
|
||||
return (
|
||||
<div className={styles.Root}>
|
||||
|
|
@ -59,8 +83,37 @@ export function MapTourPanel() {
|
|||
);
|
||||
}
|
||||
|
||||
const isTouringAll =
|
||||
tourState !== null && tourState.categoryName === ALL_FEATURES_TOUR;
|
||||
|
||||
const handleTourAllClick = () => {
|
||||
if (isTouringAll) {
|
||||
cameraTourStore.getState().cancel();
|
||||
} else {
|
||||
cameraTourStore.getState().startTour(allTargets, ALL_FEATURES_TOUR);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.Root}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.TourAllButton}
|
||||
data-active={isTouringAll}
|
||||
onClick={handleTourAllClick}
|
||||
>
|
||||
{isTouringAll ? (
|
||||
<>
|
||||
<HiMiniArrowLeftEndOnRectangle className={styles.ExitIcon} /> Exit
|
||||
tour
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<BsPlayFill className={styles.PlayIcon} />{" "}
|
||||
<span className={styles.ButtonLabel}>Tour all features</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
{categories.map((category) => (
|
||||
<CategoryGroup
|
||||
key={category.name}
|
||||
|
|
@ -120,8 +173,8 @@ function CategoryGroup({
|
|||
const isActive =
|
||||
(isTouringCategory && tourState!.currentIndex === index) ||
|
||||
(tourState !== null &&
|
||||
tourState.targets.length === 1 &&
|
||||
tourState.targets[0].entityId === target.entityId);
|
||||
tourState.targets[tourState.currentIndex]?.entityId ===
|
||||
target.entityId);
|
||||
return (
|
||||
<button
|
||||
key={target.entityId}
|
||||
|
|
|
|||
|
|
@ -213,7 +213,8 @@ export function MissionSelect({
|
|||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
||||
if (e.code === "KeyK" && (e.metaKey || e.ctrlKey)) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
inputRef.current?.focus();
|
||||
combobox.show();
|
||||
|
|
@ -283,7 +284,14 @@ export function MissionSelect({
|
|||
<Activity mode={isOpen ? "visible" : "hidden"}>
|
||||
<div className={styles.Backdrop} />
|
||||
</Activity>
|
||||
<div className={styles.InputWrapper}>
|
||||
<div
|
||||
className={styles.InputWrapper}
|
||||
onKeyDown={(event) => {
|
||||
if (!event.metaKey) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Combobox
|
||||
ref={inputRef}
|
||||
autoSelect
|
||||
|
|
@ -322,6 +330,11 @@ export function MissionSelect({
|
|||
fitViewport
|
||||
autoFocusOnHide={false}
|
||||
className={styles.Popover}
|
||||
onKeyDown={(event) => {
|
||||
if (!event.metaKey) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ComboboxList className={styles.List}>
|
||||
{filteredResults.type === "flat"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { useEffect, useEffectEvent, useRef } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useKeyboardControls } from "@react-three/drei";
|
||||
import { PointerLockControls } from "three-stdlib";
|
||||
import {
|
||||
MAX_SPEED_MULTIPLIER,
|
||||
MIN_SPEED_MULTIPLIER,
|
||||
|
|
@ -11,99 +9,39 @@ import { useCameras } from "./CamerasProvider";
|
|||
import { useInputContext } from "./InputContext";
|
||||
import { useTouchDevice } from "./useTouchDevice";
|
||||
import { cameraTourStore } from "../state/cameraTourStore";
|
||||
import {
|
||||
useInputAction,
|
||||
useInputState,
|
||||
clearInputDeltas,
|
||||
type ActionState,
|
||||
type DragState,
|
||||
type KeyState,
|
||||
type ScrollState,
|
||||
} from "./InputControls";
|
||||
|
||||
export const Controls = {
|
||||
forward: "forward",
|
||||
backward: "backward",
|
||||
left: "left",
|
||||
right: "right",
|
||||
up: "up",
|
||||
down: "down",
|
||||
lookUp: "lookUp",
|
||||
lookDown: "lookDown",
|
||||
lookLeft: "lookLeft",
|
||||
lookRight: "lookRight",
|
||||
camera1: "camera1",
|
||||
camera2: "camera2",
|
||||
camera3: "camera3",
|
||||
camera4: "camera4",
|
||||
camera5: "camera5",
|
||||
camera6: "camera6",
|
||||
camera7: "camera7",
|
||||
camera8: "camera8",
|
||||
camera9: "camera9",
|
||||
} as const;
|
||||
|
||||
export type ControlName = (typeof Controls)[keyof typeof Controls];
|
||||
|
||||
export const KEYBOARD_CONTROLS = [
|
||||
{ name: Controls.forward, keys: ["KeyW"] },
|
||||
{ name: Controls.backward, keys: ["KeyS"] },
|
||||
{ name: Controls.left, keys: ["KeyA"] },
|
||||
{ name: Controls.right, keys: ["KeyD"] },
|
||||
{ name: Controls.up, keys: ["Space"] },
|
||||
{ name: Controls.down, keys: ["ShiftLeft", "ShiftRight"] },
|
||||
{ name: Controls.lookUp, keys: ["ArrowUp"] },
|
||||
{ name: Controls.lookDown, keys: ["ArrowDown"] },
|
||||
{ name: Controls.lookLeft, keys: ["ArrowLeft"] },
|
||||
{ name: Controls.lookRight, keys: ["ArrowRight"] },
|
||||
{ name: Controls.camera1, keys: ["Digit1"] },
|
||||
{ name: Controls.camera2, keys: ["Digit2"] },
|
||||
{ name: Controls.camera3, keys: ["Digit3"] },
|
||||
{ name: Controls.camera4, keys: ["Digit4"] },
|
||||
{ name: Controls.camera5, keys: ["Digit5"] },
|
||||
{ name: Controls.camera6, keys: ["Digit6"] },
|
||||
{ name: Controls.camera7, keys: ["Digit7"] },
|
||||
{ name: Controls.camera8, keys: ["Digit8"] },
|
||||
{ name: Controls.camera9, keys: ["Digit9"] },
|
||||
];
|
||||
|
||||
const TOUR_CANCEL_KEYS = new Set([
|
||||
"KeyW", "KeyA", "KeyS", "KeyD",
|
||||
"Space", "ShiftLeft", "ShiftRight",
|
||||
"ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight",
|
||||
]);
|
||||
|
||||
const MIN_SPEED_ADJUSTMENT = 2;
|
||||
const MAX_SPEED_ADJUSTMENT = 11;
|
||||
const DRAG_THRESHOLD = 3; // px of movement before it counts as a drag
|
||||
|
||||
/** Hardcoded drag sensitivity (not affected by user setting). */
|
||||
const DRAG_SENSITIVITY = 0.002;
|
||||
export const ARROW_LOOK_SPEED = 1; // radians/sec
|
||||
|
||||
const MIN_SPEED_ADJUSTMENT = 1;
|
||||
const MAX_SPEED_ADJUSTMENT = 11;
|
||||
|
||||
/** Hardcoded drag sensitivity for non-locked drag (not affected by user setting). */
|
||||
const DRAG_SENSITIVITY = 0.002;
|
||||
|
||||
function quantizeSpeed(speedMultiplier: number): number {
|
||||
// Map [0.01, 1] → [1/16, 1], snapped to the 6-bit grid (multiples of 1/16).
|
||||
const t =
|
||||
(speedMultiplier - MIN_SPEED_MULTIPLIER) / (1 - MIN_SPEED_MULTIPLIER);
|
||||
const steps = Math.round(t * 15); // 0..15 → 16 levels (1/16 to 16/16)
|
||||
const steps = Math.round(t * 15);
|
||||
return (steps + 1) / 16;
|
||||
}
|
||||
|
||||
function isPressed(state: Record<string, ActionState>, name: string): boolean {
|
||||
const s = state[name];
|
||||
return s != null && "pressed" in s && (s as KeyState).pressed;
|
||||
}
|
||||
|
||||
export function MouseAndKeyboardHandler() {
|
||||
const isTouch = useTouchDevice();
|
||||
|
||||
// Don't let KeyboardControls handle stuff when metaKey is held.
|
||||
useEffect(() => {
|
||||
const handleKey = (e: KeyboardEvent) => {
|
||||
// Let Cmd/Ctrl+K pass through for search focus.
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
||||
return;
|
||||
}
|
||||
if (e.metaKey) {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKey, { capture: true });
|
||||
window.addEventListener("keyup", handleKey, { capture: true });
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKey, { capture: true });
|
||||
window.removeEventListener("keyup", handleKey, { capture: true });
|
||||
};
|
||||
}, []);
|
||||
|
||||
const {
|
||||
speedMultiplier,
|
||||
setSpeedMultiplier,
|
||||
|
|
@ -112,269 +50,136 @@ export function MouseAndKeyboardHandler() {
|
|||
invertDrag,
|
||||
} = useControls();
|
||||
const { onInput, mode } = useInputContext();
|
||||
const [subscribe, getKeys] = useKeyboardControls<ControlName>();
|
||||
const camera = useThree((state) => state.camera);
|
||||
const [, getInputState] = useInputState();
|
||||
const gl = useThree((state) => state.gl);
|
||||
const { nextCamera, setCameraIndex, cameraCount } = useCameras();
|
||||
const controlsRef = useRef<PointerLockControls | null>(null);
|
||||
|
||||
const getInvertScroll = useEffectEvent(() => invertScroll);
|
||||
const getInvertDrag = useEffectEvent(() => invertDrag);
|
||||
const getMode = useEffectEvent(() => mode);
|
||||
const getMouseSensitivity = useEffectEvent(() => mouseSensitivity);
|
||||
const getIsTouch = useEffectEvent(() => isTouch);
|
||||
|
||||
// Accumulated mouse deltas between frames.
|
||||
const mouseDeltaYaw = useRef(0);
|
||||
const mouseDeltaPitch = useRef(0);
|
||||
const { setCameraIndex, cameraCount } = useCameras();
|
||||
|
||||
// Trigger flags set by event handlers, consumed in useFrame.
|
||||
const triggerFire = useRef(false);
|
||||
const triggerObserve = useRef(false);
|
||||
|
||||
// Setup pointer lock controls
|
||||
// Exit pointer lock when switching to touch mode.
|
||||
useEffect(() => {
|
||||
const controls = new PointerLockControls(camera, gl.domElement);
|
||||
controlsRef.current = controls;
|
||||
|
||||
return () => {
|
||||
controls.dispose();
|
||||
};
|
||||
}, [camera, gl.domElement]);
|
||||
|
||||
// Exit pointer lock when switching to touch mode or when a tour starts.
|
||||
useEffect(() => {
|
||||
if (isTouch && controlsRef.current?.isLocked) {
|
||||
controlsRef.current.unlock();
|
||||
if (isTouch && document.pointerLockElement) {
|
||||
document.exitPointerLock();
|
||||
}
|
||||
}, [isTouch]);
|
||||
|
||||
// Exit pointer lock when a tour starts.
|
||||
useEffect(() => {
|
||||
return cameraTourStore.subscribe((state) => {
|
||||
if (state.animation && controlsRef.current?.isLocked) {
|
||||
controlsRef.current.unlock();
|
||||
if (state.animation && document.pointerLockElement) {
|
||||
document.exitPointerLock();
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Mouse handling: accumulate deltas for input frames.
|
||||
// In local mode, drag-to-look works without pointer lock.
|
||||
// Pointer lock and click behavior depend on mode.
|
||||
useEffect(() => {
|
||||
const canvas = gl.domElement;
|
||||
let dragging = false;
|
||||
let didDrag = false;
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
// Canvas click: lock pointer (only fires when not already locked).
|
||||
useInputAction("canvasClick", () => {
|
||||
if (!isTouch && !cameraTourStore.getState().animation) {
|
||||
gl.domElement.requestPointerLock();
|
||||
}
|
||||
});
|
||||
|
||||
const handleMouseDown = (e: MouseEvent) => {
|
||||
if (controlsRef.current?.isLocked) return;
|
||||
if (e.target !== canvas) return;
|
||||
dragging = true;
|
||||
didDrag = false;
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
};
|
||||
// Next player (live observer follow mode): fire trigger 0.
|
||||
useInputAction("nextPlayer", () => {
|
||||
triggerFire.current = true;
|
||||
});
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (controlsRef.current?.isLocked) {
|
||||
// Pointer is locked: accumulate raw deltas.
|
||||
const sens = getMouseSensitivity();
|
||||
mouseDeltaYaw.current += e.movementX * sens;
|
||||
mouseDeltaPitch.current += e.movementY * sens;
|
||||
return;
|
||||
}
|
||||
// Handle mousewheel for speed adjustment.
|
||||
useInputAction("adjustSpeed", () => {
|
||||
const scroll = getInputState().adjustSpeed as ScrollState | undefined;
|
||||
if (!scroll || scroll.deltaY === 0) return;
|
||||
|
||||
if (!dragging) return;
|
||||
if (
|
||||
!didDrag &&
|
||||
Math.abs(e.clientX - startX) < DRAG_THRESHOLD &&
|
||||
Math.abs(e.clientY - startY) < DRAG_THRESHOLD
|
||||
) {
|
||||
return;
|
||||
}
|
||||
didDrag = true;
|
||||
const scrollSign = invertScroll ? -1 : 1;
|
||||
const direction = (scroll.deltaY > 0 ? -1 : 1) * scrollSign;
|
||||
const scaledDeltaY = Math.ceil(Math.log2(Math.abs(scroll.deltaY) + 1));
|
||||
const speedDelta =
|
||||
Math.max(
|
||||
MIN_SPEED_ADJUSTMENT,
|
||||
Math.min(MAX_SPEED_ADJUSTMENT, scaledDeltaY),
|
||||
) * direction;
|
||||
|
||||
// In follow/orbit mode, drag direction is reversed because the camera
|
||||
// orbits around a target — dragging right should move the camera right
|
||||
// (decreasing yaw), opposite of fly mode.
|
||||
const orbitFlip = getMode() === "follow" ? -1 : 1;
|
||||
const dragSign = (getInvertDrag() ? 1 : -1) * orbitFlip;
|
||||
mouseDeltaYaw.current += dragSign * e.movementX * DRAG_SENSITIVITY;
|
||||
mouseDeltaPitch.current += dragSign * e.movementY * DRAG_SENSITIVITY;
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
dragging = false;
|
||||
};
|
||||
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
const controls = controlsRef.current;
|
||||
if (controls?.isLocked) {
|
||||
if (mode === "follow") {
|
||||
// In follow mode, click cycles to next player (trigger 0).
|
||||
triggerFire.current = true;
|
||||
} else if (mode === "local") {
|
||||
// In local mode, click while locked cycles preset cameras.
|
||||
nextCamera();
|
||||
}
|
||||
// In fly mode, clicks while locked do nothing special.
|
||||
} else if (e.target === canvas && !didDrag && !getIsTouch() && !cameraTourStore.getState().animation) {
|
||||
controls?.lock();
|
||||
}
|
||||
};
|
||||
|
||||
canvas.addEventListener("mousedown", handleMouseDown);
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
document.addEventListener("click", handleClick);
|
||||
|
||||
return () => {
|
||||
canvas.removeEventListener("mousedown", handleMouseDown);
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
document.removeEventListener("click", handleClick);
|
||||
};
|
||||
}, [camera, gl.domElement, nextCamera, mode]);
|
||||
|
||||
// Handle number keys 1-9 for camera selection (local-only UI action).
|
||||
useEffect(() => {
|
||||
const cameraControls = [
|
||||
Controls.camera1,
|
||||
Controls.camera2,
|
||||
Controls.camera3,
|
||||
Controls.camera4,
|
||||
Controls.camera5,
|
||||
Controls.camera6,
|
||||
Controls.camera7,
|
||||
Controls.camera8,
|
||||
Controls.camera9,
|
||||
];
|
||||
|
||||
return subscribe((state) => {
|
||||
for (let i = 0; i < cameraControls.length; i++) {
|
||||
if (state[cameraControls[i]] && i < cameraCount) {
|
||||
setCameraIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
setSpeedMultiplier((prev) => {
|
||||
const newSpeed = Math.round(prev * 100) + speedDelta;
|
||||
return Math.max(
|
||||
MIN_SPEED_MULTIPLIER,
|
||||
Math.min(MAX_SPEED_MULTIPLIER, newSpeed / 100),
|
||||
);
|
||||
});
|
||||
}, [subscribe, setCameraIndex, cameraCount]);
|
||||
});
|
||||
|
||||
// Handle mousewheel for speed adjustment (local setting, stays in KMH).
|
||||
useEffect(() => {
|
||||
const handleWheel = (e: WheelEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const scrollSign = getInvertScroll() ? -1 : 1;
|
||||
const direction = (e.deltaY > 0 ? -1 : 1) * scrollSign;
|
||||
|
||||
// scale deltaY in a way that feels natural for both trackpads (often just
|
||||
// a deltaY of 1 at a time!) and scroll wheels (can be 100s or more).
|
||||
const scaledDeltaY = Math.ceil(Math.log2(Math.abs(e.deltaY) + 1));
|
||||
|
||||
const delta =
|
||||
Math.max(
|
||||
MIN_SPEED_ADJUSTMENT,
|
||||
Math.min(MAX_SPEED_ADJUSTMENT, scaledDeltaY),
|
||||
) * direction;
|
||||
|
||||
setSpeedMultiplier((prev) => {
|
||||
const newSpeed = Math.round(prev * 100) + delta;
|
||||
return Math.max(
|
||||
MIN_SPEED_MULTIPLIER,
|
||||
Math.min(MAX_SPEED_MULTIPLIER, newSpeed / 100),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const canvas = gl.domElement;
|
||||
canvas.addEventListener("wheel", handleWheel, { passive: false });
|
||||
|
||||
return () => {
|
||||
canvas.removeEventListener("wheel", handleWheel);
|
||||
};
|
||||
}, [gl.domElement, setSpeedMultiplier]);
|
||||
|
||||
// Escape or movement keys: cancel active camera tour.
|
||||
useEffect(() => {
|
||||
const handleKey = (e: KeyboardEvent) => {
|
||||
if (!cameraTourStore.getState().animation) return;
|
||||
if (e.code === "Escape" || TOUR_CANCEL_KEYS.has(e.code)) {
|
||||
cameraTourStore.getState().cancel();
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", handleKey);
|
||||
return () => window.removeEventListener("keydown", handleKey);
|
||||
}, []);
|
||||
// Handle number keys 1-9 for camera selection.
|
||||
const selectCamera = (i: number) => {
|
||||
if (i < cameraCount) setCameraIndex(i);
|
||||
};
|
||||
useInputAction("camera1", () => selectCamera(0));
|
||||
useInputAction("camera2", () => selectCamera(1));
|
||||
useInputAction("camera3", () => selectCamera(2));
|
||||
useInputAction("camera4", () => selectCamera(3));
|
||||
useInputAction("camera5", () => selectCamera(4));
|
||||
useInputAction("camera6", () => selectCamera(5));
|
||||
useInputAction("camera7", () => selectCamera(6));
|
||||
useInputAction("camera8", () => selectCamera(7));
|
||||
useInputAction("camera9", () => selectCamera(8));
|
||||
|
||||
// 'O' key: toggle observer mode (sets trigger 2).
|
||||
useEffect(() => {
|
||||
if (mode === "local") return;
|
||||
|
||||
const handleKey = (e: KeyboardEvent) => {
|
||||
if (e.code !== "KeyO" || e.metaKey || e.ctrlKey || e.altKey) return;
|
||||
const target = e.target as HTMLElement;
|
||||
if (
|
||||
target.tagName === "INPUT" ||
|
||||
target.tagName === "TEXTAREA" ||
|
||||
target.isContentEditable
|
||||
) {
|
||||
return;
|
||||
}
|
||||
triggerObserve.current = true;
|
||||
};
|
||||
window.addEventListener("keydown", handleKey);
|
||||
return () => window.removeEventListener("keydown", handleKey);
|
||||
}, [mode]);
|
||||
useInputAction("toggleObserverMode", () => {
|
||||
triggerObserve.current = true;
|
||||
});
|
||||
|
||||
// Build and emit InputFrame each render frame.
|
||||
useFrame((_state, delta) => {
|
||||
// Suppress all input while a camera tour is active.
|
||||
if (cameraTourStore.getState().animation) return;
|
||||
|
||||
const {
|
||||
forward,
|
||||
backward,
|
||||
left,
|
||||
right,
|
||||
up,
|
||||
down,
|
||||
lookUp,
|
||||
lookDown,
|
||||
lookLeft,
|
||||
lookRight,
|
||||
} = getKeys();
|
||||
const inputState = getInputState();
|
||||
|
||||
// ── Look deltas ──
|
||||
let deltaYaw = 0;
|
||||
let deltaPitch = 0;
|
||||
|
||||
// Pointer-locked mouse movement (raw deltas, user sensitivity).
|
||||
const locked = inputState.lockedLook as DragState | undefined;
|
||||
if (locked && (locked.deltaX !== 0 || locked.deltaY !== 0)) {
|
||||
deltaYaw = locked.deltaX * mouseSensitivity;
|
||||
deltaPitch = locked.deltaY * mouseSensitivity;
|
||||
}
|
||||
|
||||
// Drag-to-look (unlocked, fixed sensitivity, orbit flip in follow mode).
|
||||
const drag = inputState.dragLook as DragState | undefined;
|
||||
if (drag?.dragging && (drag.deltaX !== 0 || drag.deltaY !== 0)) {
|
||||
const orbitFlip = mode === "follow" ? -1 : 1;
|
||||
const dragSign = (invertDrag ? 1 : -1) * orbitFlip;
|
||||
deltaYaw += dragSign * drag.deltaX * DRAG_SENSITIVITY;
|
||||
deltaPitch += dragSign * drag.deltaY * DRAG_SENSITIVITY;
|
||||
}
|
||||
|
||||
// Arrow keys contribute to look deltas.
|
||||
let deltaYaw = mouseDeltaYaw.current;
|
||||
let deltaPitch = mouseDeltaPitch.current;
|
||||
mouseDeltaYaw.current = 0;
|
||||
mouseDeltaPitch.current = 0;
|
||||
if (isPressed(inputState, "lookLeft")) deltaYaw -= ARROW_LOOK_SPEED * delta;
|
||||
if (isPressed(inputState, "lookRight"))
|
||||
deltaYaw += ARROW_LOOK_SPEED * delta;
|
||||
if (isPressed(inputState, "lookUp")) deltaPitch -= ARROW_LOOK_SPEED * delta;
|
||||
if (isPressed(inputState, "lookDown"))
|
||||
deltaPitch += ARROW_LOOK_SPEED * delta;
|
||||
|
||||
if (lookLeft) deltaYaw -= ARROW_LOOK_SPEED * delta;
|
||||
if (lookRight) deltaYaw += ARROW_LOOK_SPEED * delta;
|
||||
if (lookUp) deltaPitch -= ARROW_LOOK_SPEED * delta;
|
||||
if (lookDown) deltaPitch += ARROW_LOOK_SPEED * delta;
|
||||
|
||||
// Movement axes, pre-scaled by speedMultiplier (clamped to [-1, 1]).
|
||||
// ── Movement axes ──
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let z = 0;
|
||||
if (left) x -= 1;
|
||||
if (right) x += 1;
|
||||
if (forward) y += 1;
|
||||
if (backward) y -= 1;
|
||||
if (up) z += 1;
|
||||
if (down) z -= 1;
|
||||
if (isPressed(inputState, "moveLeft")) x -= 1;
|
||||
if (isPressed(inputState, "moveRight")) x += 1;
|
||||
if (isPressed(inputState, "moveForward")) y += 1;
|
||||
if (isPressed(inputState, "moveBackward")) y -= 1;
|
||||
if (isPressed(inputState, "moveUp")) z += 1;
|
||||
if (isPressed(inputState, "moveDown")) z -= 1;
|
||||
|
||||
const quantizedSpeedMultiplier = quantizeSpeed(speedMultiplier);
|
||||
|
||||
x = Math.max(-1, Math.min(1, x * quantizedSpeedMultiplier));
|
||||
y = Math.max(-1, Math.min(1, y * quantizedSpeedMultiplier));
|
||||
z = Math.max(-1, Math.min(1, z * quantizedSpeedMultiplier));
|
||||
|
||||
// Triggers.
|
||||
// ── Triggers ──
|
||||
const triggers = [false, false, false, false, false, false];
|
||||
if (triggerFire.current) {
|
||||
triggers[0] = true;
|
||||
|
|
@ -385,6 +190,9 @@ export function MouseAndKeyboardHandler() {
|
|||
triggerObserve.current = false;
|
||||
}
|
||||
|
||||
// Always clear deltas so stale values don't linger in the store.
|
||||
clearInputDeltas();
|
||||
|
||||
// Only emit if there's actual input.
|
||||
const hasLook = deltaYaw !== 0 || deltaPitch !== 0;
|
||||
const hasMove = x !== 0 || y !== 0 || z !== 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { useStore } from "zustand";
|
||||
import { useEngineSelector } from "../state/engineStore";
|
||||
import { streamPlaybackStore } from "../state/streamPlaybackStore";
|
||||
import { DEFAULT_TEAM_NAMES } from "../stringUtils";
|
||||
import { textureToUrl } from "../loaders";
|
||||
import type { StreamEntity, TeamScore, WeaponsHudSlot } from "../stream/types";
|
||||
|
|
@ -410,18 +412,24 @@ export function PlayerHUD() {
|
|||
const hasControlPlayer = useEngineSelector(
|
||||
(state) => !!state.playback.streamSnapshot?.controlPlayerGhostId,
|
||||
);
|
||||
const cameraMode = useStore(streamPlaybackStore, (s) => s.cameraMode);
|
||||
|
||||
// In free-fly mode the camera is disconnected from the player, so
|
||||
// player-specific HUD elements (health, energy, weapons, etc.) are hidden.
|
||||
const showPlayerElements =
|
||||
hasControlPlayer && cameraMode !== "freeFly";
|
||||
|
||||
return (
|
||||
<div className={styles.PlayerHUD}>
|
||||
<ChatWindow />
|
||||
{hasControlPlayer && (
|
||||
{showPlayerElements && (
|
||||
<div className={styles.Bars}>
|
||||
<HealthBar />
|
||||
<EnergyBar />
|
||||
</div>
|
||||
)}
|
||||
<Compass />
|
||||
{hasControlPlayer && (
|
||||
{showPlayerElements && (
|
||||
<>
|
||||
<WeaponHUD />
|
||||
<PackAndInventoryHUD />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {
|
|||
LoopOnce,
|
||||
LoopRepeat,
|
||||
Object3D,
|
||||
Audio as ThreeAudio,
|
||||
PositionalAudio,
|
||||
Vector3,
|
||||
} from "three";
|
||||
|
|
@ -43,10 +42,7 @@ import {
|
|||
} from "./AudioEmitter";
|
||||
import type { ResolvedAudioProfile } from "./AudioEmitter";
|
||||
import { audioToUrl } from "../loaders";
|
||||
import { createLogger } from "../logger";
|
||||
import { useSettings } from "./SettingsProvider";
|
||||
|
||||
const log = createLogger("PlayerModel");
|
||||
import { useEngineStoreApi, useEngineSelector } from "../state/engineStore";
|
||||
import { streamPlaybackStore } from "../state/streamPlaybackStore";
|
||||
import type { PlayerEntity } from "../state/gameEntityTypes";
|
||||
|
|
@ -495,7 +491,7 @@ export function PlayerModel({ entity }: { entity: PlayerEntity }) {
|
|||
} catch {
|
||||
// File not in manifest.
|
||||
}
|
||||
}, [audioLoader, entity.dataBlockId]);
|
||||
}, [audioLoader, engineStore, entity.dataBlockId]);
|
||||
|
||||
// Cleanup jet sound on unmount.
|
||||
useEffect(() => {
|
||||
|
|
@ -740,12 +736,7 @@ export function PlayerModel({ entity }: { entity: PlayerEntity }) {
|
|||
const jetSound = jetSoundRef.current;
|
||||
const soundActuallyPlaying = jetSound?.isPlaying ?? false;
|
||||
if (isJetting && !soundActuallyPlaying) {
|
||||
if (
|
||||
audioEnabled &&
|
||||
audioListener &&
|
||||
jetBufferRef.current &&
|
||||
jetProfile
|
||||
) {
|
||||
if (audioEnabled && audioListener && jetBufferRef.current && jetProfile) {
|
||||
let sound = jetSound;
|
||||
if (!sound) {
|
||||
sound = new PositionalAudio(audioListener);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { useCallback, type ReactNode } from "react";
|
|||
import type { StreamRecording } from "../stream/types";
|
||||
import { useEngineSelector } from "../state/engineStore";
|
||||
|
||||
export const SPEED_OPTIONS = [0.25, 0.5, 1, 2, 4];
|
||||
|
||||
export function RecordingProvider({ children }: { children: ReactNode }) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue