improve toolbar on mobile, add joystick modes

This commit is contained in:
Brian Beck 2026-02-12 12:04:26 -08:00
parent 68c7b303aa
commit d9293b1a0b
30 changed files with 480 additions and 152 deletions

View file

@ -123,6 +123,8 @@ function MapInspector() {
const cameraRef = useRef<Camera | null>(null); const cameraRef = useRef<Camera | null>(null);
const joystickStateRef = useRef<JoystickState>({ angle: 0, force: 0 }); const joystickStateRef = useRef<JoystickState>({ angle: 0, force: 0 });
const joystickZoneRef = useRef<HTMLDivElement | null>(null); const joystickZoneRef = useRef<HTMLDivElement | null>(null);
const lookJoystickStateRef = useRef<JoystickState>({ angle: 0, force: 0 });
const lookJoystickZoneRef = useRef<HTMLDivElement | null>(null);
return ( return (
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
@ -147,6 +149,8 @@ function MapInspector() {
<TouchJoystick <TouchJoystick
joystickState={joystickStateRef} joystickState={joystickStateRef}
joystickZone={joystickZoneRef} joystickZone={joystickZoneRef}
lookJoystickState={lookJoystickStateRef}
lookJoystickZone={lookJoystickZoneRef}
/> />
)} )}
<Canvas <Canvas
@ -171,6 +175,8 @@ function MapInspector() {
<TouchCameraMovement <TouchCameraMovement
joystickState={joystickStateRef} joystickState={joystickStateRef}
joystickZone={joystickZoneRef} joystickZone={joystickZoneRef}
lookJoystickState={lookJoystickStateRef}
lookJoystickZone={lookJoystickZoneRef}
/> />
) : ( ) : (
<ObserverControls /> <ObserverControls />
@ -184,6 +190,7 @@ function MapInspector() {
missionType={missionType} missionType={missionType}
onChangeMission={changeMission} onChangeMission={changeMission}
cameraRef={cameraRef} cameraRef={cameraRef}
isTouch={isTouch}
/> />
</SettingsProvider> </SettingsProvider>
</main> </main>

View file

@ -49,9 +49,6 @@ main {
} }
#controls { #controls {
display: flex;
align-items: center;
gap: 20px;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@ -67,7 +64,8 @@ input[type="range"] {
max-width: 80px; max-width: 80px;
} }
.CheckboxField { .CheckboxField,
.LabelledButton {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
@ -79,6 +77,64 @@ input[type="range"] {
gap: 6px; gap: 6px;
} }
#controls,
.Controls-dropdown,
.Controls-group {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
}
@media (max-width: 1279px) {
.Controls-dropdown[data-open="false"] {
display: none;
}
.Controls-dropdown {
position: absolute;
top: calc(100% + 2px);
left: 2px;
right: 2px;
display: flex;
overflow: auto;
max-height: calc(100dvh - 56px);
flex-direction: column;
align-items: center;
gap: 12px;
background: rgba(0, 0, 0, 0.8);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
padding: 12px;
box-shadow: 0 0 12px rgba(0, 0, 0, 0.4);
}
.Controls-group {
flex-wrap: wrap;
gap: 12px 20px;
}
}
@media (max-width: 639px) {
#controls {
right: 0;
border-radius: 0;
}
#controls > .MissionSelect-inputWrapper {
flex: 1 1 0;
min-width: 0;
}
#controls > .MissionSelect-inputWrapper .MissionSelect-input {
width: 100%;
}
.Controls-toggle {
flex: 0 0 auto;
}
}
.IconButton { .IconButton {
position: relative; position: relative;
display: flex; display: flex;
@ -104,17 +160,49 @@ input[type="range"] {
border-color 0.2s; border-color 0.2s;
} }
.IconButton:hover { .ButtonLabel {
background: rgba(0, 98, 179, 0.8); font-size: 12px;
border-color: rgba(255, 255, 255, 0.4);
} }
.IconButton:active { .IconButton svg {
pointer-events: none;
}
@media (hover: hover) {
.IconButton:hover {
background: rgba(0, 98, 179, 0.8);
border-color: rgba(255, 255, 255, 0.4);
}
}
.IconButton:active,
.IconButton[aria-expanded="true"] {
background: rgba(0, 98, 179, 0.7); background: rgba(0, 98, 179, 0.7);
border-color: rgba(255, 255, 255, 0.3); border-color: rgba(255, 255, 255, 0.3);
transform: translate(0, 1px); transform: translate(0, 1px);
} }
.Controls-toggle {
margin: 0;
}
@media (max-width: 1279px) {
.LabelledButton {
width: auto;
padding: 0 10px;
}
}
@media (min-width: 1280px) {
.Controls-toggle {
display: none;
}
.LabelledButton .ButtonLabel {
display: none;
}
}
.CopyCoordinatesButton[data-copied="true"] { .CopyCoordinatesButton[data-copied="true"] {
background: rgba(0, 117, 213, 0.9); background: rgba(0, 117, 213, 0.9);
border-color: rgba(255, 255, 255, 0.4); border-color: rgba(255, 255, 255, 0.4);
@ -146,7 +234,9 @@ input[type="range"] {
.StatsPanel { .StatsPanel {
left: auto !important; left: auto !important;
top: auto !important;
right: 0; right: 0;
bottom: 0;
} }
.AxisLabel { .AxisLabel {
@ -428,6 +518,19 @@ input[type="range"] {
width: 140px; width: 140px;
height: 140px; height: 140px;
z-index: 10; z-index: 10;
-webkit-touch-callout: none;
user-select: none;
}
.TouchJoystick--left {
left: 20px;
transform: none;
}
.TouchJoystick--right {
left: auto;
right: 20px;
transform: none;
} }
.TouchJoystick .back { .TouchJoystick .back {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,9 +1,9 @@
1:"$Sreact.fragment" 1:"$Sreact.fragment"
2:I[47257,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ClientPageRoot"] 2:I[47257,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ClientPageRoot"]
3:I[31713,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js","/t2-mapper/_next/static/chunks/69160059bd4715b0.js","/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","/t2-mapper/_next/static/chunks/f5fbb30641aa8588.js","/t2-mapper/_next/static/chunks/7cc8b088c0b74e6e.js"],"default"] 3:I[31713,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js","/t2-mapper/_next/static/chunks/69160059bd4715b0.js","/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","/t2-mapper/_next/static/chunks/affab05731a53281.js","/t2-mapper/_next/static/chunks/5109a68f602829f3.js"],"default"]
6:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"OutletBoundary"] 6:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"OutletBoundary"]
7:"$Sreact.suspense" 7:"$Sreact.suspense"
0:{"buildId":"FMM2sqIA1T3SyE5jWLnR9","rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/69160059bd4715b0.js","async":true}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","async":true}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/f5fbb30641aa8588.js","async":true}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/7cc8b088c0b74e6e.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"loading":null,"isPartial":false} 0:{"buildId":"44qtzzWDxYYxKQeshxCwM","rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/69160059bd4715b0.js","async":true}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","async":true}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/affab05731a53281.js","async":true}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/5109a68f602829f3.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"loading":null,"isPartial":false}
4:{} 4:{}
5:"$0:rsc:props:children:0:props:serverProvidedParams:params" 5:"$0:rsc:props:children:0:props:serverProvidedParams:params"
8:null 8:null

View file

@ -3,14 +3,14 @@
3:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 3:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
4:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 4:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
5:I[47257,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ClientPageRoot"] 5:I[47257,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ClientPageRoot"]
6:I[31713,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js","/t2-mapper/_next/static/chunks/69160059bd4715b0.js","/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","/t2-mapper/_next/static/chunks/f5fbb30641aa8588.js","/t2-mapper/_next/static/chunks/7cc8b088c0b74e6e.js"],"default"] 6:I[31713,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js","/t2-mapper/_next/static/chunks/69160059bd4715b0.js","/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","/t2-mapper/_next/static/chunks/affab05731a53281.js","/t2-mapper/_next/static/chunks/5109a68f602829f3.js"],"default"]
9:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"OutletBoundary"] 9:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"OutletBoundary"]
a:"$Sreact.suspense" a:"$Sreact.suspense"
c:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ViewportBoundary"] c:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ViewportBoundary"]
e:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"] e:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"]
10:I[68027,[],"default"] 10:I[68027,[],"default"]
:HL["/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","style"] :HL["/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","style"]
0:{"P":null,"b":"FMM2sqIA1T3SyE5jWLnR9","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{"Component":"$6","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@7","$@8"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/69160059bd4715b0.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/f5fbb30641aa8588.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/7cc8b088c0b74e6e.js","async":true,"nonce":"$undefined"}]],["$","$L9",null,{"children":["$","$a",null,{"name":"Next.MetadataOutlet","children":"$@b"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lc",null,{"children":"$Ld"}],["$","div",null,{"hidden":true,"children":["$","$Le",null,{"children":["$","$a",null,{"name":"Next.Metadata","children":"$Lf"}]}]}],null]}],false]],"m":"$undefined","G":["$10",[]],"S":true} 0:{"P":null,"b":"44qtzzWDxYYxKQeshxCwM","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{"Component":"$6","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@7","$@8"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/69160059bd4715b0.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/affab05731a53281.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/5109a68f602829f3.js","async":true,"nonce":"$undefined"}]],["$","$L9",null,{"children":["$","$a",null,{"name":"Next.MetadataOutlet","children":"$@b"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lc",null,{"children":"$Ld"}],["$","div",null,{"hidden":true,"children":["$","$Le",null,{"children":["$","$a",null,{"name":"Next.Metadata","children":"$Lf"}]}]}],null]}],false]],"m":"$undefined","G":["$10",[]],"S":true}
7:{} 7:{}
8:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params" 8:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params"
d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]] d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]

View file

@ -3,4 +3,4 @@
3:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"] 3:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"]
4:"$Sreact.suspense" 4:"$Sreact.suspense"
5:I[27201,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"IconMark"] 5:I[27201,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"IconMark"]
0:{"buildId":"FMM2sqIA1T3SyE5jWLnR9","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"MapGenius  Explore maps for Tribes 2"}],["$","meta","1",{"name":"description","content":"Tribes 2 forever."}],["$","link","2",{"rel":"icon","href":"/t2-mapper/icon.png?icon.2911bba1.png","sizes":"108x128","type":"image/png"}],["$","$L5","3",{}]]}]}]}],null]}],"loading":null,"isPartial":false} 0:{"buildId":"44qtzzWDxYYxKQeshxCwM","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"MapGenius  Explore maps for Tribes 2"}],["$","meta","1",{"name":"description","content":"Tribes 2 forever."}],["$","link","2",{"rel":"icon","href":"/t2-mapper/icon.png?icon.2911bba1.png","sizes":"108x128","type":"image/png"}],["$","$L5","3",{}]]}]}]}],null]}],"loading":null,"isPartial":false}

View file

@ -2,5 +2,5 @@
2:I[12985,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js"],"NuqsAdapter"] 2:I[12985,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js"],"NuqsAdapter"]
3:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 3:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
4:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 4:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
:HL["/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","style"] :HL["/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","style"]
0:{"buildId":"FMM2sqIA1T3SyE5jWLnR9","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]]}],"loading":null,"isPartial":false} 0:{"buildId":"44qtzzWDxYYxKQeshxCwM","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]]}],"loading":null,"isPartial":false}

View file

@ -1,2 +1,2 @@
:HL["/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","style"] :HL["/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","style"]
0:{"buildId":"FMM2sqIA1T3SyE5jWLnR9","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":true},"staleTime":300} 0:{"buildId":"44qtzzWDxYYxKQeshxCwM","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":true},"staleTime":300}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,8 +7,8 @@
8:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ViewportBoundary"] 8:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ViewportBoundary"]
a:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"] a:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"]
c:I[68027,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] c:I[68027,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
:HL["/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","style"] :HL["/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","style"]
0:{"P":null,"b":"FMM2sqIA1T3SyE5jWLnR9","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],null]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true} 0:{"P":null,"b":"44qtzzWDxYYxKQeshxCwM","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],null]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true}
9:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]] 9:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]
d:I[27201,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"IconMark"] d:I[27201,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"IconMark"]
7:null 7:null

View file

@ -3,4 +3,4 @@
3:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"] 3:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"]
4:"$Sreact.suspense" 4:"$Sreact.suspense"
5:I[27201,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"IconMark"] 5:I[27201,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"IconMark"]
0:{"buildId":"FMM2sqIA1T3SyE5jWLnR9","rsc":["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"MapGenius  Explore maps for Tribes 2"}],["$","meta","1",{"name":"description","content":"Tribes 2 forever."}],["$","link","2",{"rel":"icon","href":"/t2-mapper/icon.png?icon.2911bba1.png","sizes":"108x128","type":"image/png"}],["$","$L5","3",{}]]}]}]}],null]}],"loading":null,"isPartial":false} 0:{"buildId":"44qtzzWDxYYxKQeshxCwM","rsc":["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"MapGenius  Explore maps for Tribes 2"}],["$","meta","1",{"name":"description","content":"Tribes 2 forever."}],["$","link","2",{"rel":"icon","href":"/t2-mapper/icon.png?icon.2911bba1.png","sizes":"108x128","type":"image/png"}],["$","$L5","3",{}]]}]}]}],null]}],"loading":null,"isPartial":false}

View file

@ -2,5 +2,5 @@
2:I[12985,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js"],"NuqsAdapter"] 2:I[12985,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js"],"NuqsAdapter"]
3:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 3:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
4:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 4:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
:HL["/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","style"] :HL["/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","style"]
0:{"buildId":"FMM2sqIA1T3SyE5jWLnR9","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]]}],"loading":null,"isPartial":false} 0:{"buildId":"44qtzzWDxYYxKQeshxCwM","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]]}],"loading":null,"isPartial":false}

View file

@ -1,5 +1,5 @@
1:"$Sreact.fragment" 1:"$Sreact.fragment"
2:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"OutletBoundary"] 2:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"OutletBoundary"]
3:"$Sreact.suspense" 3:"$Sreact.suspense"
0:{"buildId":"FMM2sqIA1T3SyE5jWLnR9","rsc":["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],null,["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false} 0:{"buildId":"44qtzzWDxYYxKQeshxCwM","rsc":["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],null,["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false}
4:null 4:null

View file

@ -1,4 +1,4 @@
1:"$Sreact.fragment" 1:"$Sreact.fragment"
2:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 2:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
3:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 3:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
0:{"buildId":"FMM2sqIA1T3SyE5jWLnR9","rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"loading":null,"isPartial":false} 0:{"buildId":"44qtzzWDxYYxKQeshxCwM","rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"loading":null,"isPartial":false}

View file

@ -1,2 +1,2 @@
:HL["/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","style"] :HL["/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","style"]
0:{"buildId":"FMM2sqIA1T3SyE5jWLnR9","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"/_not-found","paramType":null,"paramKey":"/_not-found","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":false}},"isRootLayout":true},"staleTime":300} 0:{"buildId":"44qtzzWDxYYxKQeshxCwM","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"/_not-found","paramType":null,"paramKey":"/_not-found","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":false}},"isRootLayout":true},"staleTime":300}

File diff suppressed because one or more lines are too long

View file

@ -7,8 +7,8 @@
8:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ViewportBoundary"] 8:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ViewportBoundary"]
a:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"] a:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"]
c:I[68027,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] c:I[68027,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
:HL["/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","style"] :HL["/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","style"]
0:{"P":null,"b":"FMM2sqIA1T3SyE5jWLnR9","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],null]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true} 0:{"P":null,"b":"44qtzzWDxYYxKQeshxCwM","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],null]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true}
9:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]] 9:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]
d:I[27201,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"IconMark"] d:I[27201,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"IconMark"]
7:null 7:null

File diff suppressed because one or more lines are too long

View file

@ -3,14 +3,14 @@
3:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 3:I[39756,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
4:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"] 4:I[37457,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"default"]
5:I[47257,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ClientPageRoot"] 5:I[47257,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ClientPageRoot"]
6:I[31713,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js","/t2-mapper/_next/static/chunks/69160059bd4715b0.js","/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","/t2-mapper/_next/static/chunks/f5fbb30641aa8588.js","/t2-mapper/_next/static/chunks/7cc8b088c0b74e6e.js"],"default"] 6:I[31713,["/t2-mapper/_next/static/chunks/e6da73430a674f20.js","/t2-mapper/_next/static/chunks/69160059bd4715b0.js","/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","/t2-mapper/_next/static/chunks/affab05731a53281.js","/t2-mapper/_next/static/chunks/5109a68f602829f3.js"],"default"]
9:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"OutletBoundary"] 9:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"OutletBoundary"]
a:"$Sreact.suspense" a:"$Sreact.suspense"
c:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ViewportBoundary"] c:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"ViewportBoundary"]
e:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"] e:I[97367,["/t2-mapper/_next/static/chunks/4fd93823156e59e8.js"],"MetadataBoundary"]
10:I[68027,[],"default"] 10:I[68027,[],"default"]
:HL["/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","style"] :HL["/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","style"]
0:{"P":null,"b":"FMM2sqIA1T3SyE5jWLnR9","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/5c8f13e9504ceb7f.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{"Component":"$6","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@7","$@8"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/69160059bd4715b0.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/f5fbb30641aa8588.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/7cc8b088c0b74e6e.js","async":true,"nonce":"$undefined"}]],["$","$L9",null,{"children":["$","$a",null,{"name":"Next.MetadataOutlet","children":"$@b"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lc",null,{"children":"$Ld"}],["$","div",null,{"hidden":true,"children":["$","$Le",null,{"children":["$","$a",null,{"name":"Next.Metadata","children":"$Lf"}]}]}],null]}],false]],"m":"$undefined","G":["$10",[]],"S":true} 0:{"P":null,"b":"44qtzzWDxYYxKQeshxCwM","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/4b117a58395a92c9.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/e6da73430a674f20.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{"Component":"$6","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@7","$@8"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/69160059bd4715b0.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/93b588fa7f31935c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/affab05731a53281.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/5109a68f602829f3.js","async":true,"nonce":"$undefined"}]],["$","$L9",null,{"children":["$","$a",null,{"name":"Next.MetadataOutlet","children":"$@b"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lc",null,{"children":"$Ld"}],["$","div",null,{"hidden":true,"children":["$","$Le",null,{"children":["$","$a",null,{"name":"Next.Metadata","children":"$Lf"}]}]}],null]}],false]],"m":"$undefined","G":["$10",[]],"S":true}
7:{} 7:{}
8:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params" 8:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params"
d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]] d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]

View file

@ -47,14 +47,16 @@ export function CopyCoordinatesButton({
return ( return (
<button <button
type="button" type="button"
className="IconButton CopyCoordinatesButton" className="IconButton LabelledButton CopyCoordinatesButton"
aria-label="Copy coordinates URL" aria-label="Copy coordinates URL"
title="Copy coordinates URL" title="Copy coordinates URL"
onClick={handleCopyLink} onClick={handleCopyLink}
data-copied={showCopied ? "true" : "false"} data-copied={showCopied ? "true" : "false"}
id="copyCoordinatesButton"
> >
<FaMapPin className="MapPin" /> <FaMapPin className="MapPin" />
<FaClipboardCheck className="ClipboardCheck" /> <FaClipboardCheck className="ClipboardCheck" />
<span className="ButtonLabel"> Copy coordinates URL</span>
</button> </button>
); );
} }

View file

@ -1,14 +1,21 @@
import { useControls, useDebug, useSettings } from "./SettingsProvider"; import {
useControls,
useDebug,
useSettings,
type TouchMode,
} from "./SettingsProvider";
import { MissionSelect } from "./MissionSelect"; import { MissionSelect } from "./MissionSelect";
import { RefObject } from "react"; import { RefObject, useEffect, useState, useRef } from "react";
import { Camera } from "three"; import { Camera } from "three";
import { CopyCoordinatesButton } from "./CopyCoordinatesButton"; import { CopyCoordinatesButton } from "./CopyCoordinatesButton";
import { FiSettings } from "react-icons/fi";
export function InspectorControls({ export function InspectorControls({
missionName, missionName,
missionType, missionType,
onChangeMission, onChangeMission,
cameraRef, cameraRef,
isTouch,
}: { }: {
missionName: string; missionName: string;
missionType: string; missionType: string;
@ -20,6 +27,7 @@ export function InspectorControls({
missionType: string; missionType: string;
}) => void; }) => void;
cameraRef: RefObject<Camera | null>; cameraRef: RefObject<Camera | null>;
isTouch: boolean | null;
}) { }) {
const { const {
fogEnabled, fogEnabled,
@ -31,8 +39,139 @@ export function InspectorControls({
animationEnabled, animationEnabled,
setAnimationEnabled, setAnimationEnabled,
} = useSettings(); } = useSettings();
const { speedMultiplier, setSpeedMultiplier } = useControls(); const { speedMultiplier, setSpeedMultiplier, touchMode, setTouchMode } =
useControls();
const { debugMode, setDebugMode } = useDebug(); const { debugMode, setDebugMode } = useDebug();
const [settingsOpen, setSettingsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
// Focus the panel when it opens.
useEffect(() => {
if (settingsOpen) {
dropdownRef.current?.focus();
}
}, [settingsOpen]);
const handleDropdownBlur = (e: React.FocusEvent) => {
const related = e.relatedTarget as Node | null;
if (
related &&
(dropdownRef.current?.contains(related) ||
buttonRef.current?.contains(related))
) {
return;
}
setSettingsOpen(false);
};
// Close on Escape and return focus to the gear button.
const handlePanelKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Escape") {
setSettingsOpen(false);
buttonRef.current?.focus();
}
};
const settingsFields = (
<>
<div className="Controls-group">
<CopyCoordinatesButton cameraRef={cameraRef} />
</div>
<div className="Controls-group">
<div className="CheckboxField">
<input
id="fogInput"
type="checkbox"
checked={fogEnabled}
onChange={(event) => {
setFogEnabled(event.target.checked);
}}
/>
<label htmlFor="fogInput">Fog?</label>
</div>
<div className="CheckboxField">
<input
id="audioInput"
type="checkbox"
checked={audioEnabled}
onChange={(event) => {
setAudioEnabled(event.target.checked);
}}
/>
<label htmlFor="audioInput">Audio?</label>
</div>
</div>
<div className="Controls-group">
<div className="CheckboxField">
<input
id="animationInput"
type="checkbox"
checked={animationEnabled}
onChange={(event) => {
setAnimationEnabled(event.target.checked);
}}
/>
<label htmlFor="animationInput">Animation?</label>
</div>
<div className="CheckboxField">
<input
id="debugInput"
type="checkbox"
checked={debugMode}
onChange={(event) => {
setDebugMode(event.target.checked);
}}
/>
<label htmlFor="debugInput">Debug?</label>
</div>
</div>
<div className="Controls-group">
<div className="Field">
<label htmlFor="fovInput">FOV</label>
<input
id="fovInput"
type="range"
min={75}
max={120}
step={5}
value={fov}
onChange={(event) => setFov(parseInt(event.target.value))}
/>
<output htmlFor="fovInput">{fov}</output>
</div>
<div className="Field">
<label htmlFor="speedInput">Speed</label>
<input
id="speedInput"
type="range"
min={0.1}
max={5}
step={0.05}
value={speedMultiplier}
onChange={(event) =>
setSpeedMultiplier(parseFloat(event.target.value))
}
/>
</div>
</div>
{isTouch && (
<div className="Controls-group">
<div className="Field">
<label htmlFor="touchModeInput">Joystick:</label>{" "}
<select
id="touchModeInput"
value={touchMode}
onChange={(e) => setTouchMode(e.target.value as TouchMode)}
>
<option value="dualStick">Dual Stick</option>
<option value="moveLookStick">Single Stick</option>
</select>
</div>
</div>
)}
</>
);
return ( return (
<div <div
@ -46,77 +185,27 @@ export function InspectorControls({
missionType={missionType} missionType={missionType}
onChange={onChangeMission} onChange={onChangeMission}
/> />
<CopyCoordinatesButton cameraRef={cameraRef} /> <button
<div className="CheckboxField"> ref={buttonRef}
<input className="IconButton Controls-toggle"
id="fogInput" onClick={() => setSettingsOpen((isOpen) => !isOpen)}
type="checkbox" onDoubleClick={(e) => e.preventDefault()}
checked={fogEnabled} aria-expanded={settingsOpen}
onChange={(event) => { aria-controls="settingsPanel"
setFogEnabled(event.target.checked); aria-label="Settings"
}} >
/> <FiSettings />
<label htmlFor="fogInput">Fog?</label> </button>
</div> <div
<div className="CheckboxField"> className="Controls-dropdown"
<input ref={dropdownRef}
id="audioInput" id="settingsPanel"
type="checkbox" tabIndex={-1}
checked={audioEnabled} onKeyDown={handlePanelKeyDown}
onChange={(event) => { onBlur={handleDropdownBlur}
setAudioEnabled(event.target.checked); data-open={settingsOpen}
}} >
/> {settingsFields}
<label htmlFor="audioInput">Audio?</label>
</div>
<div className="CheckboxField">
<input
id="animationInput"
type="checkbox"
checked={animationEnabled}
onChange={(event) => {
setAnimationEnabled(event.target.checked);
}}
/>
<label htmlFor="animationInput">Animation?</label>
</div>
<div className="CheckboxField">
<input
id="debugInput"
type="checkbox"
checked={debugMode}
onChange={(event) => {
setDebugMode(event.target.checked);
}}
/>
<label htmlFor="debugInput">Debug?</label>
</div>
<div className="Field">
<label htmlFor="fovInput">FOV</label>
<input
id="fovInput"
type="range"
min={75}
max={120}
step={5}
value={fov}
onChange={(event) => setFov(parseInt(event.target.value))}
/>
<output htmlFor="speedInput">{fov}</output>
</div>
<div className="Field">
<label htmlFor="speedInput">Speed</label>
<input
id="speedInput"
type="range"
min={0.1}
max={5}
step={0.05}
value={speedMultiplier}
onChange={(event) =>
setSpeedMultiplier(parseFloat(event.target.value))
}
/>
</div> </div>
</div> </div>
); );

View file

@ -11,6 +11,8 @@ import {
type StateSetter<T> = ReturnType<typeof useState<T>>[1]; type StateSetter<T> = ReturnType<typeof useState<T>>[1];
export type TouchMode = "dualStick" | "moveLookStick";
type SettingsContext = { type SettingsContext = {
fogEnabled: boolean; fogEnabled: boolean;
setFogEnabled: StateSetter<boolean>; setFogEnabled: StateSetter<boolean>;
@ -32,6 +34,8 @@ type DebugContext = {
type ControlsContext = { type ControlsContext = {
speedMultiplier: number; speedMultiplier: number;
setSpeedMultiplier: StateSetter<number>; setSpeedMultiplier: StateSetter<number>;
touchMode: TouchMode;
setTouchMode: StateSetter<TouchMode>;
}; };
const SettingsContext = createContext<SettingsContext | null>(null); const SettingsContext = createContext<SettingsContext | null>(null);
@ -46,6 +50,7 @@ type PersistedSettings = {
audioEnabled?: boolean; audioEnabled?: boolean;
animationEnabled?: boolean; animationEnabled?: boolean;
debugMode?: boolean; debugMode?: boolean;
touchMode?: TouchMode;
}; };
export function useSettings() { export function useSettings() {
@ -68,6 +73,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
const [audioEnabled, setAudioEnabled] = useState(false); const [audioEnabled, setAudioEnabled] = useState(false);
const [animationEnabled, setAnimationEnabled] = useState(true); const [animationEnabled, setAnimationEnabled] = useState(true);
const [debugMode, setDebugMode] = useState(false); const [debugMode, setDebugMode] = useState(false);
const [touchMode, setTouchMode] = useState<TouchMode>("moveLookStick");
const settingsContext: SettingsContext = useMemo( const settingsContext: SettingsContext = useMemo(
() => ({ () => ({
@ -91,8 +97,8 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
); );
const controlsContext: ControlsContext = useMemo( const controlsContext: ControlsContext = useMemo(
() => ({ speedMultiplier, setSpeedMultiplier }), () => ({ speedMultiplier, setSpeedMultiplier, touchMode, setTouchMode }),
[speedMultiplier, setSpeedMultiplier], [speedMultiplier, setSpeedMultiplier, touchMode, setTouchMode],
); );
// Read persisted settings from localStorage. // Read persisted settings from localStorage.
@ -124,6 +130,9 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
if (savedSettings.fov != null) { if (savedSettings.fov != null) {
setFov(savedSettings.fov); setFov(savedSettings.fov);
} }
if (savedSettings.touchMode != null) {
setTouchMode(savedSettings.touchMode);
}
}, []); }, []);
// Persist settings to localStorage with debouncing to avoid excessive writes // Persist settings to localStorage with debouncing to avoid excessive writes
@ -145,6 +154,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
audioEnabled, audioEnabled,
animationEnabled, animationEnabled,
debugMode, debugMode,
touchMode,
}; };
try { try {
localStorage.setItem("settings", JSON.stringify(settingsToSave)); localStorage.setItem("settings", JSON.stringify(settingsToSave));
@ -166,6 +176,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
audioEnabled, audioEnabled,
animationEnabled, animationEnabled,
debugMode, debugMode,
touchMode,
]); ]);
return ( return (

View file

@ -6,6 +6,8 @@ import { useControls } from "./SettingsProvider";
const BASE_SPEED = 80; const BASE_SPEED = 80;
const LOOK_SENSITIVITY = 0.004; const LOOK_SENSITIVITY = 0.004;
const STICK_LOOK_SENSITIVITY = 2.5;
const SINGLE_STICK_DEADZONE = 0.25;
const MAX_PITCH = Math.PI / 2 - 0.01; // ~89° const MAX_PITCH = Math.PI / 2 - 0.01; // ~89°
export type JoystickState = { export type JoystickState = {
@ -16,10 +18,19 @@ export type JoystickState = {
type SharedProps = { type SharedProps = {
joystickState: RefObject<JoystickState>; joystickState: RefObject<JoystickState>;
joystickZone: RefObject<HTMLDivElement | null>; joystickZone: RefObject<HTMLDivElement | null>;
lookJoystickState: RefObject<JoystickState>;
lookJoystickZone: RefObject<HTMLDivElement | null>;
}; };
/** Renders the joystick zone. Place inside canvasContainer, outside Canvas. */ /** Renders the joystick zone(s). Place inside canvasContainer, outside Canvas. */
export function TouchJoystick({ joystickState, joystickZone }: SharedProps) { export function TouchJoystick({
joystickState,
joystickZone,
lookJoystickState,
lookJoystickZone,
}: SharedProps) {
const { touchMode } = useControls();
// Move joystick
useEffect(() => { useEffect(() => {
const zone = joystickZone.current; const zone = joystickZone.current;
if (!zone) return; if (!zone) return;
@ -39,7 +50,6 @@ export function TouchJoystick({ joystickState, joystickZone }: SharedProps) {
manager.on("move", (_event, data) => { manager.on("move", (_event, data) => {
joystickState.current.angle = data.angle.radian; joystickState.current.angle = data.angle.radian;
// Clamp force to 01 range (nipplejs force can exceed 1)
joystickState.current.force = Math.min(1, data.force); joystickState.current.force = Math.min(1, data.force);
}); });
@ -52,17 +62,77 @@ export function TouchJoystick({ joystickState, joystickZone }: SharedProps) {
cancelled = true; cancelled = true;
manager?.destroy(); manager?.destroy();
}; };
}, [joystickState, joystickZone]); }, [joystickState, joystickZone, touchMode]);
return <div ref={joystickZone} className="TouchJoystick" />; // Look joystick (dual stick mode only)
useEffect(() => {
if (touchMode !== "dualStick") return;
const zone = lookJoystickZone.current;
if (!zone) return;
let manager: nipplejs.JoystickManager | null = null;
let cancelled = false;
import("nipplejs").then((mod) => {
if (cancelled) return;
manager = mod.default.create({
zone,
mode: "static",
position: { right: "70px", bottom: "70px" },
size: 120,
restOpacity: 0.9,
});
manager.on("move", (_event, data) => {
lookJoystickState.current.angle = data.angle.radian;
lookJoystickState.current.force = Math.min(1, data.force);
});
manager.on("end", () => {
lookJoystickState.current.force = 0;
});
});
return () => {
cancelled = true;
manager?.destroy();
};
}, [touchMode, lookJoystickState, lookJoystickZone]);
if (touchMode === "dualStick") {
return (
<>
<div
ref={joystickZone}
className="TouchJoystick TouchJoystick--left"
onContextMenu={(e) => e.preventDefault()}
/>
<div
ref={lookJoystickZone}
className="TouchJoystick TouchJoystick--right"
onContextMenu={(e) => e.preventDefault()}
/>
</>
);
}
return (
<div
ref={joystickZone}
className="TouchJoystick"
onContextMenu={(e) => e.preventDefault()}
/>
);
} }
/** Handles touch look and joystick-driven movement. Place inside Canvas. */ /** Handles touch look and joystick-driven movement. Place inside Canvas. */
export function TouchCameraMovement({ export function TouchCameraMovement({
joystickState, joystickState,
joystickZone, joystickZone,
lookJoystickState,
}: SharedProps) { }: SharedProps) {
const { speedMultiplier } = useControls(); const { speedMultiplier, touchMode } = useControls();
const { camera, gl } = useThree(); const { camera, gl } = useThree();
// Touch look state // Touch look state
@ -80,8 +150,10 @@ export function TouchCameraMovement({
euler.current.setFromQuaternion(camera.quaternion, "YXZ"); euler.current.setFromQuaternion(camera.quaternion, "YXZ");
}, [camera]); }, [camera]);
// Touch look handling // Touch-drag look handling (moveLookStick mode)
useEffect(() => { useEffect(() => {
if (touchMode !== "moveLookStick") return;
const canvas = gl.domElement; const canvas = gl.domElement;
const isTouchOnJoystick = (touch: Touch) => { const isTouchOnJoystick = (touch: Touch) => {
@ -149,38 +221,82 @@ export function TouchCameraMovement({
canvas.removeEventListener("touchmove", handleTouchMove); canvas.removeEventListener("touchmove", handleTouchMove);
canvas.removeEventListener("touchend", handleTouchEnd); canvas.removeEventListener("touchend", handleTouchEnd);
canvas.removeEventListener("touchcancel", handleTouchEnd); canvas.removeEventListener("touchcancel", handleTouchEnd);
lookTouchId.current = null;
}; };
}, [camera, gl.domElement, joystickZone]); }, [camera, gl.domElement, joystickZone, touchMode]);
useFrame((_state, delta) => { useFrame((_state, delta) => {
const { force, angle } = joystickState.current; const { force, angle } = joystickState.current;
if (force === 0) return;
const speed = BASE_SPEED * speedMultiplier * force; if (touchMode === "dualStick") {
// Right stick → camera rotation
const look = lookJoystickState.current;
if (look.force > 0) {
const lookX = Math.cos(look.angle);
const lookY = Math.sin(look.angle);
// Decompose joystick angle into forward/sideways components euler.current.setFromQuaternion(camera.quaternion, "YXZ");
// nipplejs angle: 0 = right, π/2 = up, π = left, 3π/2 = down euler.current.y -= lookX * look.force * STICK_LOOK_SENSITIVITY * delta;
const joyX = Math.cos(angle); // right component euler.current.x += lookY * look.force * STICK_LOOK_SENSITIVITY * delta;
const joyY = Math.sin(angle); // forward component euler.current.x = Math.max(
-MAX_PITCH,
Math.min(MAX_PITCH, euler.current.x),
);
camera.quaternion.setFromEuler(euler.current);
}
// Forward/backward: use full camera direction (including Y) so // Left stick → movement
// looking up and pushing forward moves upward, like ObserverControls. if (force > 0) {
camera.getWorldDirection(forwardVec.current); const speed = BASE_SPEED * speedMultiplier * force;
forwardVec.current.normalize(); const joyX = Math.cos(angle);
const joyY = Math.sin(angle);
// Left/right: move along XZ plane camera.getWorldDirection(forwardVec.current);
sideVec.current.crossVectors(camera.up, forwardVec.current).normalize(); forwardVec.current.normalize();
sideVec.current
.crossVectors(camera.up, forwardVec.current)
.normalize();
// Combine: joyY pushes forward, joyX pushes right. moveVec.current
// sideVec points left (up × forward), so negate joyX. .set(0, 0, 0)
moveVec.current .addScaledVector(forwardVec.current, joyY)
.set(0, 0, 0) .addScaledVector(sideVec.current, -joyX);
.addScaledVector(forwardVec.current, joyY)
.addScaledVector(sideVec.current, -joyX);
if (moveVec.current.lengthSq() > 0) { if (moveVec.current.lengthSq() > 0) {
moveVec.current.normalize().multiplyScalar(speed * delta); moveVec.current.normalize().multiplyScalar(speed * delta);
camera.position.add(moveVec.current); camera.position.add(moveVec.current);
}
}
} else if (touchMode === "moveLookStick") {
if (force > 0) {
// Move forward at half the configured speed.
const speed = BASE_SPEED * speedMultiplier * 0.5;
camera.getWorldDirection(forwardVec.current);
forwardVec.current.normalize();
moveVec.current
.copy(forwardVec.current)
.multiplyScalar(speed * delta);
camera.position.add(moveVec.current);
if (force >= SINGLE_STICK_DEADZONE) {
// Outer zone: also control camera look (yaw + pitch).
const lookX = Math.cos(angle);
const lookY = Math.sin(angle);
const lookForce =
(force - SINGLE_STICK_DEADZONE) / (1 - SINGLE_STICK_DEADZONE);
euler.current.setFromQuaternion(camera.quaternion, "YXZ");
euler.current.y -=
lookX * lookForce * STICK_LOOK_SENSITIVITY * 0.5 * delta;
euler.current.x +=
lookY * lookForce * STICK_LOOK_SENSITIVITY * 0.5 * delta;
euler.current.x = Math.max(
-MAX_PITCH,
Math.min(MAX_PITCH, euler.current.x),
);
camera.quaternion.setFromEuler(euler.current);
}
}
} }
}); });