allow selecting different game types

This commit is contained in:
Brian Beck 2025-12-14 11:06:57 -08:00
parent 7f75ed84da
commit 049566cdbb
56 changed files with 436 additions and 207 deletions

View file

@ -1,5 +1,5 @@
"use client";
import { useState, useEffect, useCallback, Suspense } from "react";
import { useState, useEffect, useCallback, Suspense, useMemo } from "react";
import { useSearchParams, useRouter } from "next/navigation";
import { Canvas, GLProps } from "@react-three/fiber";
import { NoToneMapping, SRGBColorSpace, PCFShadowMap } from "three";
@ -30,10 +30,19 @@ function MapInspector() {
const searchParams = useSearchParams();
const router = useRouter();
// Initialize state from query params
const [missionName, setMissionName] = useState(
searchParams.get("mission") || "TWL2_WoodyMyrk",
const [initialMissionName, initialMissionType] = useMemo(
() => (searchParams.get("mission") || "RiverDance:CTF").split("~"),
[],
);
const [missionName, setMissionName] = useState(initialMissionName);
const availableMissionTypes = getMissionInfo(missionName).missionTypes;
const [missionType, setMissionType] = useState(() =>
initialMissionType && availableMissionTypes.includes(initialMissionType)
? initialMissionType
: availableMissionTypes[0],
);
const isOnlyMissionType = availableMissionTypes.length === 1;
const [loadingProgress, setLoadingProgress] = useState(0);
const [showLoadingIndicator, setShowLoadingIndicator] = useState(true);
const isLoading = loadingProgress < 1;
@ -64,9 +73,12 @@ function MapInspector() {
// Update query params when state changes
useEffect(() => {
const params = new URLSearchParams();
params.set("mission", missionName);
const value = isOnlyMissionType
? missionName
: `${missionName}~${missionType}`;
params.set("mission", value);
router.replace(`?${params.toString()}`, { scroll: false });
}, [missionName, router]);
}, [missionName, missionType, isOnlyMissionType, router]);
const handleLoadingChange = useCallback(
(_loading: boolean, progress: number = 0) => {
@ -94,13 +106,19 @@ function MapInspector() {
</div>
</div>
)}
<Canvas frameloop="always" gl={glSettings} shadows={{ type: PCFShadowMap }}>
<Canvas
frameloop="always"
gl={glSettings}
shadows={{ type: PCFShadowMap }}
>
<CamerasProvider>
<AudioProvider>
<Mission
key={missionName}
key={`${missionName}~${missionType}`}
name={missionName}
missionType={missionType}
onLoadingChange={handleLoadingChange}
setMissionType={setMissionType}
/>
<ObserverCamera />
<DebugElements />
@ -111,7 +129,11 @@ function MapInspector() {
</div>
<InspectorControls
missionName={missionName}
onChangeMission={setMissionName}
missionType={missionType}
onChangeMission={({ missionName, missionType }) => {
setMissionName(missionName);
setMissionType(missionType);
}}
/>
</SettingsProvider>
</main>

View file

@ -61,6 +61,10 @@ main {
z-index: 1;
}
input[type="range"] {
max-width: 80px;
}
.CheckboxField {
display: flex;
align-items: center;
@ -73,11 +77,6 @@ main {
gap: 6px;
}
#fovInput,
#speedInput {
max-width: 80px;
}
.StaticShapeLabel {
background: rgba(0, 0, 0, 0.5);
color: #fff;
@ -85,6 +84,7 @@ main {
white-space: nowrap;
padding: 1px 3px;
border-radius: 1px;
text-align: center;
}
.StatsPanel {
@ -133,7 +133,7 @@ main {
}
.MissionSelect-input {
width: 240px;
width: 280px;
padding: 6px 36px 6px 8px;
font-size: 14px;
border: 1px solid rgba(255, 255, 255, 0.3);
@ -152,8 +152,37 @@ main {
}
.MissionSelect-input::placeholder {
color: transparent;
}
.MissionSelect-selectedValue {
position: absolute;
left: 8px;
right: 36px;
display: flex;
align-items: center;
gap: 6px;
pointer-events: none;
overflow: hidden;
}
.MissionSelect-input[aria-expanded="true"] ~ .MissionSelect-selectedValue {
display: none;
}
.MissionSelect-selectedName {
color: #fff;
font-weight: 600;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 1;
min-width: 0;
}
.MissionSelect-selectedValue > .MissionSelect-itemType {
flex-shrink: 0;
}
.MissionSelect-popover {
@ -246,6 +275,10 @@ main {
color: #fff;
}
.MissionSelect-itemType:hover {
background: rgba(255, 157, 0, 0.7);
}
.MissionSelect-itemMissionName {
font-size: 12px;
color: rgba(255, 255, 255, 0.5);

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"
2:I[47257,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"ClientPageRoot"]
3:I[31713,["/t2-mapper/_next/static/chunks/2a5938bd223c7e45.js","/t2-mapper/_next/static/chunks/39bb94591a93cbfc.js","/t2-mapper/_next/static/chunks/d634c91b8578ac9d.js","/t2-mapper/_next/static/chunks/b9952519fd76ae55.js"],"default"]
3:I[31713,["/t2-mapper/_next/static/chunks/3a3cff0360e2ba9f.js","/t2-mapper/_next/static/chunks/581a9039979a2b81.js","/t2-mapper/_next/static/chunks/ed074071f28b33e1.js","/t2-mapper/_next/static/chunks/acd032a5b4d059f4.js"],"default"]
6:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"OutletBoundary"]
7:"$Sreact.suspense"
0:{"buildId":"k7CY5XBOHhhqM_Ap9uzeM","rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/2a5938bd223c7e45.js","async":true}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/39bb94591a93cbfc.js","async":true}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/d634c91b8578ac9d.js","async":true}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/b9952519fd76ae55.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"loading":null,"isPartial":false}
0:{"buildId":"S0fmd6Eh3TfTF-_linDwH","rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/3a3cff0360e2ba9f.js","async":true}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/581a9039979a2b81.js","async":true}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/ed074071f28b33e1.js","async":true}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/acd032a5b4d059f4.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"loading":null,"isPartial":false}
4:{}
5:"$0:rsc:props:children:0:props:serverProvidedParams:params"
8:null

View file

@ -2,14 +2,14 @@
2:I[39756,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
3:I[37457,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
4:I[47257,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"ClientPageRoot"]
5:I[31713,["/t2-mapper/_next/static/chunks/2a5938bd223c7e45.js","/t2-mapper/_next/static/chunks/39bb94591a93cbfc.js","/t2-mapper/_next/static/chunks/d634c91b8578ac9d.js","/t2-mapper/_next/static/chunks/b9952519fd76ae55.js"],"default"]
5:I[31713,["/t2-mapper/_next/static/chunks/3a3cff0360e2ba9f.js","/t2-mapper/_next/static/chunks/581a9039979a2b81.js","/t2-mapper/_next/static/chunks/ed074071f28b33e1.js","/t2-mapper/_next/static/chunks/acd032a5b4d059f4.js"],"default"]
8:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"OutletBoundary"]
9:"$Sreact.suspense"
b:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"ViewportBoundary"]
d:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"MetadataBoundary"]
f:I[68027,[],"default"]
:HL["/t2-mapper/_next/static/chunks/7174717ef9c02374.css","style"]
0:{"P":null,"b":"k7CY5XBOHhhqM_Ap9uzeM","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/7174717ef9c02374.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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":[["$","$L4",null,{"Component":"$5","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@6","$@7"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/2a5938bd223c7e45.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/39bb94591a93cbfc.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/d634c91b8578ac9d.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/b9952519fd76ae55.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$","$9",null,{"name":"Next.MetadataOutlet","children":"$@a"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$@c"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$9",null,{"name":"Next.Metadata","children":"$@e"}]}]}],null]}],false]],"m":"$undefined","G":["$f",[]],"S":true}
:HL["/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","style"]
0:{"P":null,"b":"S0fmd6Eh3TfTF-_linDwH","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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":[["$","$L4",null,{"Component":"$5","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@6","$@7"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/3a3cff0360e2ba9f.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/581a9039979a2b81.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/ed074071f28b33e1.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/acd032a5b4d059f4.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$","$9",null,{"name":"Next.MetadataOutlet","children":"$@a"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$@c"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$9",null,{"name":"Next.Metadata","children":"$@e"}]}]}],null]}],false]],"m":"$undefined","G":["$f",[]],"S":true}
6:{}
7:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params"
c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]

View file

@ -3,6 +3,6 @@
4:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"MetadataBoundary"]
5:"$Sreact.suspense"
7:I[27201,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"IconMark"]
0:{"buildId":"k7CY5XBOHhhqM_Ap9uzeM","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":"$@3"}],["$","div",null,{"hidden":true,"children":["$","$L4",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$@6"}]}]}],null]}],"loading":null,"isPartial":false}
0:{"buildId":"S0fmd6Eh3TfTF-_linDwH","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":"$@3"}],["$","div",null,{"hidden":true,"children":["$","$L4",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$@6"}]}]}],null]}],"loading":null,"isPartial":false}
3:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
6:[["$","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"}],["$","$L7","3",{}]]

View file

@ -1,5 +1,5 @@
1:"$Sreact.fragment"
2:I[39756,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
3:I[37457,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
:HL["/t2-mapper/_next/static/chunks/7174717ef9c02374.css","style"]
0:{"buildId":"k7CY5XBOHhhqM_Ap9uzeM","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/7174717ef9c02374.css","precedence":"next"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",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}
:HL["/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","style"]
0:{"buildId":"S0fmd6Eh3TfTF-_linDwH","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","precedence":"next"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",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/7174717ef9c02374.css","style"]
0:{"buildId":"k7CY5XBOHhhqM_Ap9uzeM","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}
:HL["/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","style"]
0:{"buildId":"S0fmd6Eh3TfTF-_linDwH","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}

View file

@ -208,4 +208,4 @@
void main() {
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
`}),[P]);return o.createElement("group",(0,s.default)({},L,{ref:q}),E&&!et&&o.createElement("mesh",{castShadow:T,receiveShadow:w,ref:Q},D||o.createElement("planeGeometry",null),O||o.createElement("shaderMaterial",{side:l.DoubleSide,vertexShader:ea.vertexShader,fragmentShader:ea.fragmentShader})))});e.s(["Html",()=>y],60099);let F=[0,0,0],b=(0,o.memo)(function({children:e,color:t="white",position:r=F,opacity:s="fadeWithDistance"}){let c="fadeWithDistance"===s,u=(0,o.useRef)(null),f=function(e){let t,{camera:r}=(0,n.useThree)(),i=(0,o.useRef)(null),s=(t=(0,o.useRef)(null),(0,a.useFrame)(()=>{e.current&&(t.current??=new l.Vector3,e.current.getWorldPosition(t.current))}),t);return(0,a.useFrame)(()=>{s.current?i.current=r.position.distanceTo(s.current):i.current=null}),i}(u),[d,m]=(0,o.useState)(0!==s),g=(0,o.useRef)(null);return(0,o.useEffect)(()=>{if(c&&g.current&&null!=f.current){let e=Math.max(0,Math.min(1,1-f.current/200));g.current.style.opacity=e.toString()}},[d,c]),(0,a.useFrame)(()=>{if(c){let e=f.current,t=null!=e&&e<200;if(d!==t&&m(t),g.current&&t){let t=Math.max(0,Math.min(1,1-e/200));g.current.style.opacity=t.toString()}}else m(0!==s),g.current&&(g.current.style.opacity=s.toString())}),(0,i.jsx)("group",{ref:u,children:d?(0,i.jsx)(y,{position:r,center:!0,children:(0,i.jsx)("div",{ref:g,className:"StaticShapeLabel",style:{color:t},children:e})}):null})});e.s(["FloatingLabel",0,b],89887)},51434,e=>{"use strict";var t=e.i(43476),r=e.i(71645),i=e.i(73949),o=e.i(90072);let a=(0,r.createContext)(void 0);function n({children:e}){let{camera:n}=(0,i.useThree)(),[l,s]=(0,r.useState)({audioLoader:null,audioListener:null});return(0,r.useEffect)(()=>{let e=new o.AudioLoader,t=n.children.find(e=>e instanceof o.AudioListener);t||(t=new o.AudioListener,n.add(t)),s({audioLoader:e,audioListener:t})},[n]),(0,t.jsx)(a.Provider,{value:l,children:e})}function l(){let e=(0,r.useContext)(a);if(void 0===e)throw Error("useAudio must be used within AudioProvider");return e}e.s(["AudioProvider",()=>n,"useAudio",()=>l])},61921,e=>{e.v(t=>Promise.all(["static/chunks/d6b468212f2cc982.js"].map(t=>e.l(t))).then(()=>t(29055)))},25147,e=>{e.v(t=>Promise.all(["static/chunks/4d1bea7fed55073e.js"].map(t=>e.l(t))).then(()=>t(63724)))},18599,e=>{e.v(t=>Promise.all(["static/chunks/23cc8efc786b36f2.js"].map(t=>e.l(t))).then(()=>t(42585)))}]);
`}),[P]);return o.createElement("group",(0,s.default)({},L,{ref:q}),E&&!et&&o.createElement("mesh",{castShadow:T,receiveShadow:w,ref:Q},D||o.createElement("planeGeometry",null),O||o.createElement("shaderMaterial",{side:l.DoubleSide,vertexShader:ea.vertexShader,fragmentShader:ea.fragmentShader})))});e.s(["Html",()=>y],60099);let F=[0,0,0],b=(0,o.memo)(function({children:e,color:t="white",position:r=F,opacity:s="fadeWithDistance"}){let c="fadeWithDistance"===s,u=(0,o.useRef)(null),f=function(e){let t,{camera:r}=(0,n.useThree)(),i=(0,o.useRef)(null),s=(t=(0,o.useRef)(null),(0,a.useFrame)(()=>{e.current&&(t.current??=new l.Vector3,e.current.getWorldPosition(t.current))}),t);return(0,a.useFrame)(()=>{s.current?i.current=r.position.distanceTo(s.current):i.current=null}),i}(u),[d,m]=(0,o.useState)(0!==s),g=(0,o.useRef)(null);return(0,o.useEffect)(()=>{if(c&&g.current&&null!=f.current){let e=Math.max(0,Math.min(1,1-f.current/200));g.current.style.opacity=e.toString()}},[d,c]),(0,a.useFrame)(()=>{if(c){let e=f.current,t=null!=e&&e<200;if(d!==t&&m(t),g.current&&t){let t=Math.max(0,Math.min(1,1-e/200));g.current.style.opacity=t.toString()}}else m(0!==s),g.current&&(g.current.style.opacity=s.toString())}),(0,i.jsx)("group",{ref:u,children:d?(0,i.jsx)(y,{position:r,center:!0,children:(0,i.jsx)("div",{ref:g,className:"StaticShapeLabel",style:{color:t},children:e})}):null})});e.s(["FloatingLabel",0,b],89887)},51434,e=>{"use strict";var t=e.i(43476),r=e.i(71645),i=e.i(73949),o=e.i(90072);let a=(0,r.createContext)(void 0);function n({children:e}){let{camera:n}=(0,i.useThree)(),[l,s]=(0,r.useState)({audioLoader:null,audioListener:null});return(0,r.useEffect)(()=>{let e=new o.AudioLoader,t=n.children.find(e=>e instanceof o.AudioListener);t||(t=new o.AudioListener,n.add(t)),s({audioLoader:e,audioListener:t})},[n]),(0,t.jsx)(a.Provider,{value:l,children:e})}function l(){let e=(0,r.useContext)(a);if(void 0===e)throw Error("useAudio must be used within AudioProvider");return e}e.s(["AudioProvider",()=>n,"useAudio",()=>l])},61921,e=>{e.v(t=>Promise.all(["static/chunks/d6b468212f2cc982.js"].map(t=>e.l(t))).then(()=>t(29055)))},25147,e=>{e.v(t=>Promise.all(["static/chunks/4d1bea7fed55073e.js"].map(t=>e.l(t))).then(()=>t(63724)))},18599,e=>{e.v(t=>Promise.all(["static/chunks/ae94dbdee9f8feee.js"].map(t=>e.l(t))).then(()=>t(42585)))}]);

View file

@ -1 +0,0 @@
html{box-sizing:border-box;background:#000;margin:0;padding:0}*,:before,:after{box-sizing:inherit}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;font-size:100%}body{margin:0;padding:0}main{width:100vw;height:100vh}#canvasContainer{z-index:0;position:absolute;inset:0}#controls{color:#fff;z-index:1;background:#00000080;border-radius:0 0 4px;align-items:center;gap:20px;padding:8px 12px 8px 8px;font-size:13px;display:flex;position:fixed;top:0;left:0}.CheckboxField,.Field{align-items:center;gap:6px;display:flex}#fovInput,#speedInput{max-width:80px}.StaticShapeLabel{color:#fff;white-space:nowrap;background:#00000080;border-radius:1px;padding:1px 3px;font-size:11px}.StatsPanel{right:0;left:auto!important}.AxisLabel{pointer-events:none;font-size:12px}.AxisLabel[data-axis=x]{color:#f90}.AxisLabel[data-axis=y]{color:#9f0}.AxisLabel[data-axis=z]{color:#09f}.MissionSelect-inputWrapper{align-items:center;display:flex;position:relative}.MissionSelect-shortcut{color:#fff9;pointer-events:none;background:#ffffff26;border-radius:3px;padding:1px 4px;font-family:system-ui,sans-serif;font-size:11px;position:absolute;right:7px}.MissionSelect-input[aria-expanded=true]~.MissionSelect-shortcut{display:none}.MissionSelect-input{color:#fff;background:#0009;border:1px solid #ffffff4d;border-radius:3px;outline:none;width:240px;padding:6px 36px 6px 8px;font-size:14px}.MissionSelect-input[aria-expanded=true]{padding-right:8px}.MissionSelect-input:focus{border-color:#fff9}.MissionSelect-input::placeholder{color:#fff;font-weight:600}.MissionSelect-popover{z-index:100;min-width:320px;max-height:var(--popover-available-height,90vh);overscroll-behavior:contain;background:#141414f2;border:1px solid #ffffff80;border-radius:3px;overflow-y:auto;box-shadow:0 8px 24px #0009}.MissionSelect-list{padding:4px 0}.MissionSelect-list:has(>.MissionSelect-group:first-child){padding-top:0}.MissionSelect-group{padding-bottom:4px}.MissionSelect-groupLabel{color:#c6caca;z-index:1;background:#3a4548f2;border-bottom:1px solid #ffffff4d;padding:6px 8px 6px 12px;font-size:13px;font-weight:600;position:sticky;top:0}.MissionSelect-group:not(:last-child){border-bottom:1px solid #ffffff4d}.MissionSelect-item{cursor:pointer;border-radius:4px;outline:none;flex-direction:column;gap:1px;margin:4px 4px 0;padding:6px 8px;scroll-margin-top:32px;display:flex}.MissionSelect-list>.MissionSelect-item:first-child{margin-top:0}.MissionSelect-item[data-active-item]{background:#ffffff26}.MissionSelect-item[aria-selected=true]{background:#6496ff4d}.MissionSelect-itemHeader{align-items:center;gap:6px;display:flex}.MissionSelect-itemName{color:#fff;font-size:14px;font-weight:600}.MissionSelect-itemTypes{gap:3px;display:flex}.MissionSelect-itemType{color:#fff;background:#ff9d0066;border-radius:3px;padding:2px 5px;font-size:10px;font-weight:600}.MissionSelect-itemMissionName{color:#ffffff80;font-size:12px}.MissionSelect-noResults{color:#ffffff80;text-align:center;padding:12px 8px;font-size:13px}.LoadingSpinner{border:4px solid #fff3;border-top-color:#fff;border-radius:50%;width:48px;height:48px;animation:1s linear infinite LoadingSpinner-spin}@keyframes LoadingSpinner-spin{to{transform:rotate(360deg)}}#loadingIndicator{pointer-events:none;z-index:1;opacity:.8;flex-direction:column;align-items:center;gap:16px;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}#loadingIndicator[data-complete=true]{animation:.3s ease-out forwards loadingComplete}@keyframes loadingComplete{0%{opacity:1}to{opacity:0}}.LoadingProgress{background:#fff3;border-radius:2px;width:200px;height:4px;overflow:hidden}.LoadingProgress-bar{background:#fff;border-radius:2px;height:100%;transition:width .1s ease-out}.LoadingProgress-text{color:#ffffffb3;font-variant-numeric:tabular-nums;font-size:14px}

View file

@ -163,4 +163,4 @@
// Apply volumetric fog using shared Torque-style fog shader
${d.fogFragmentShader}
}
`;var g=e.i(79123);function h({surfaceTexture:e,attach:a}){let r=(0,u.textureToUrl)(e),i=(0,n.useTexture)(r,e=>(0,f.setupTexture)(e));return(0,t.jsx)("meshStandardMaterial",{attach:a,map:i,transparent:!0,opacity:.8,side:o.DoubleSide})}let x=(0,a.memo)(function({object:e}){let r,{debugMode:n}=(0,g.useDebug)(),u=(0,a.useMemo)(()=>(0,c.getRotation)(e),[e]),f=(0,a.useMemo)(()=>(0,c.getPosition)(e),[e]),v=(0,a.useMemo)(()=>(0,c.getScale)(e),[e]),[d,m,p]=v,h=(0,s.useThree)(e=>e.camera),x=(r=(0,a.useRef)(null),(0,a.useCallback)(e=>{if(!r.current)return r.current=e.clone(),!0;let t=r.current.x===e.x&&r.current.y===e.y&&r.current.z===e.z;return t||r.current.copy(e),t},[]));f[1];let b=(0,c.getFloat)(e,"waveMagnitude")??1,y=(0,a.useMemo)(()=>{let[e,t,a]=f,r=Math.round((e+1024)/8),o=Math.round((a+1024)/8);return[8*(r=Math.max(0,Math.min(2040,r))),t,8*(o=Math.max(0,Math.min(2040,o)))]},[f]),M=(e,t)=>{let a=e+1024,r=t+1024,o=Math.trunc(a/2048),i=Math.trunc(r/2048);a<0&&o--,r<0&&i--;let n=[];for(let e=i-1;e<=i+1;e++)for(let t=o-1;t<=o+1;t++)n.push([t,e]);return n},[w,E]=(0,a.useState)(()=>M(h.position.x,h.position.z));(0,l.useFrame)(()=>{if(!x(h.position))return;let e=M(h.position.x,h.position.z);E(t=>JSON.stringify(t)===JSON.stringify(e)?t:e)});let P=(0,c.getProperty)(e,"surfaceTexture")??"liquidTiles/BlueWater",V=(0,c.getProperty)(e,"envMapTexture"),_=(0,c.getFloat)(e,"surfaceOpacity")??.75,S=(0,c.getFloat)(e,"envMapIntensity")??1,U=(0,a.useMemo)(()=>{let e,[t,a]=(e=d<=1024&&p<=1024?8:16,[Math.max(4,Math.ceil(d/e)),Math.max(4,Math.ceil(p/e))]),r=new o.PlaneGeometry(d,p,t,a);return r.rotateX(-Math.PI/2),r.translate(d/2,m,p/2),r},[d,m,p]);return(0,a.useEffect)(()=>()=>{U.dispose()},[U]),(0,t.jsxs)("group",{quaternion:u,children:[n&&(0,t.jsx)(i,{args:v,position:[f[0]+d/2,f[1]+m/2,f[2]+p/2],children:(0,t.jsx)("meshBasicMaterial",{color:"#00fbff",wireframe:!0})}),(0,t.jsx)(a.Suspense,{fallback:w.map(([e,a])=>{let r=y[0]+2048*e-1024,i=y[2]+2048*a-1024;return(0,t.jsx)("mesh",{geometry:U,position:[r,y[1],i],children:(0,t.jsx)("meshStandardMaterial",{color:"#00fbff",transparent:!0,opacity:.4,wireframe:!0,side:o.DoubleSide})},`${e},${a}`)}),children:(0,t.jsx)(T,{reps:w,basePosition:y,surfaceGeometry:U,surfaceTexture:P,envMapTexture:V,opacity:_,waveMagnitude:b,envMapIntensity:S})})]})}),T=(0,a.memo)(function({reps:e,basePosition:r,surfaceGeometry:i,surfaceTexture:s,envMapTexture:c,opacity:d,waveMagnitude:h,envMapIntensity:x}){let T=(0,u.textureToUrl)(s),b=(0,u.textureToUrl)(c??"special/lush_env"),[y,M]=(0,n.useTexture)([T,b],e=>{(Array.isArray(e)?e:[e]).forEach(e=>{(0,f.setupTexture)(e),e.colorSpace=o.NoColorSpace,e.wrapS=o.RepeatWrapping,e.wrapT=o.RepeatWrapping})}),{animationEnabled:w}=(0,g.useSettings)(),E=(0,a.useMemo)(()=>{var e;return e={opacity:d,waveMagnitude:h,envMapIntensity:x,baseTexture:y,envMapTexture:M},new o.ShaderMaterial({uniforms:{uTime:{value:0},uOpacity:{value:e?.opacity??.75},uWaveMagnitude:{value:e?.waveMagnitude??1},uEnvMapIntensity:{value:e?.envMapIntensity??1},uBaseTexture:{value:e?.baseTexture??null},uEnvMapTexture:{value:e?.envMapTexture??null},fogColor:{value:new o.Color},fogNear:{value:1},fogFar:{value:2e3},fogVolumeData:v.globalFogUniforms.fogVolumeData,cameraHeight:v.globalFogUniforms.cameraHeight,fogEnabled:v.globalFogUniforms.fogEnabled},vertexShader:m,fragmentShader:p,transparent:!0,side:o.DoubleSide,depthWrite:!0,fog:!0})},[d,h,x,y,M]),P=(0,a.useRef)(0);return(0,l.useFrame)((e,t)=>{w?(P.current+=t,E.uniforms.uTime.value=P.current):(P.current=0,E.uniforms.uTime.value=0)}),(0,a.useEffect)(()=>()=>{E.dispose()},[E]),(0,t.jsx)(t.Fragment,{children:e.map(([e,a])=>{let o=r[0]+2048*e-1024,n=r[2]+2048*a-1024;return(0,t.jsx)("mesh",{geometry:i,material:E,position:[o,r[1],n]},`${e},${a}`)})})});e.s(["WaterBlock",0,x,"WaterMaterial",()=>h],42585)}]);
`;var g=e.i(79123);function h({surfaceTexture:e,attach:a}){let r=(0,u.textureToUrl)(e),i=(0,n.useTexture)(r,e=>(0,f.setupTexture)(e));return(0,t.jsx)("meshStandardMaterial",{attach:a,map:i,transparent:!0,opacity:.8,side:o.DoubleSide})}let x=(0,a.memo)(function({object:e}){let r,{debugMode:n}=(0,g.useDebug)(),u=(0,a.useMemo)(()=>(0,c.getRotation)(e),[e]),f=(0,a.useMemo)(()=>(0,c.getPosition)(e),[e]),v=(0,a.useMemo)(()=>(0,c.getScale)(e),[e]),[d,m,p]=v,h=(0,s.useThree)(e=>e.camera),x=(r=(0,a.useRef)(null),(0,a.useCallback)(e=>{if(!r.current)return r.current=e.clone(),!0;let t=r.current.x===e.x&&r.current.y===e.y&&r.current.z===e.z;return t||r.current.copy(e),t},[])),b=(0,c.getFloat)(e,"waveMagnitude")??1,y=(0,a.useMemo)(()=>{let[e,t,a]=f,r=Math.round((e+1024)/8),o=Math.round((a+1024)/8);return[8*(r=Math.max(0,Math.min(2040,r))),t,8*(o=Math.max(0,Math.min(2040,o)))]},[f]),M=(e,t)=>{let a=e+1024,r=t+1024,o=Math.trunc(a/2048),i=Math.trunc(r/2048);a<0&&o--,r<0&&i--;let n=[];for(let e=i-1;e<=i+1;e++)for(let t=o-1;t<=o+1;t++)n.push([t,e]);return n},[w,E]=(0,a.useState)(()=>M(h.position.x,h.position.z));(0,l.useFrame)(()=>{if(!x(h.position))return;let e=M(h.position.x,h.position.z);E(t=>JSON.stringify(t)===JSON.stringify(e)?t:e)});let P=(0,c.getProperty)(e,"surfaceTexture")??"liquidTiles/BlueWater",V=(0,c.getProperty)(e,"envMapTexture"),_=(0,c.getFloat)(e,"surfaceOpacity")??.75,S=(0,c.getFloat)(e,"envMapIntensity")??1,U=(0,a.useMemo)(()=>{let e,[t,a]=(e=d<=1024&&p<=1024?8:16,[Math.max(4,Math.ceil(d/e)),Math.max(4,Math.ceil(p/e))]),r=new o.PlaneGeometry(d,p,t,a);return r.rotateX(-Math.PI/2),r.translate(d/2,m,p/2),r},[d,m,p]);return(0,a.useEffect)(()=>()=>{U.dispose()},[U]),(0,t.jsxs)("group",{quaternion:u,children:[n&&(0,t.jsx)(i,{args:v,position:[f[0]+d/2,f[1]+m/2,f[2]+p/2],children:(0,t.jsx)("meshBasicMaterial",{color:"#00fbff",wireframe:!0})}),(0,t.jsx)(a.Suspense,{fallback:w.map(([e,a])=>{let r=y[0]+2048*e-1024,i=y[2]+2048*a-1024;return(0,t.jsx)("mesh",{geometry:U,position:[r,y[1],i],children:(0,t.jsx)("meshStandardMaterial",{color:"#00fbff",transparent:!0,opacity:.4,wireframe:!0,side:o.DoubleSide})},`${e},${a}`)}),children:(0,t.jsx)(T,{reps:w,basePosition:y,surfaceGeometry:U,surfaceTexture:P,envMapTexture:V,opacity:_,waveMagnitude:b,envMapIntensity:S})})]})}),T=(0,a.memo)(function({reps:e,basePosition:r,surfaceGeometry:i,surfaceTexture:s,envMapTexture:c,opacity:d,waveMagnitude:h,envMapIntensity:x}){let T=(0,u.textureToUrl)(s),b=(0,u.textureToUrl)(c??"special/lush_env"),[y,M]=(0,n.useTexture)([T,b],e=>{(Array.isArray(e)?e:[e]).forEach(e=>{(0,f.setupTexture)(e),e.colorSpace=o.NoColorSpace,e.wrapS=o.RepeatWrapping,e.wrapT=o.RepeatWrapping})}),{animationEnabled:w}=(0,g.useSettings)(),E=(0,a.useMemo)(()=>{var e;return e={opacity:d,waveMagnitude:h,envMapIntensity:x,baseTexture:y,envMapTexture:M},new o.ShaderMaterial({uniforms:{uTime:{value:0},uOpacity:{value:e?.opacity??.75},uWaveMagnitude:{value:e?.waveMagnitude??1},uEnvMapIntensity:{value:e?.envMapIntensity??1},uBaseTexture:{value:e?.baseTexture??null},uEnvMapTexture:{value:e?.envMapTexture??null},fogColor:{value:new o.Color},fogNear:{value:1},fogFar:{value:2e3},fogVolumeData:v.globalFogUniforms.fogVolumeData,cameraHeight:v.globalFogUniforms.cameraHeight,fogEnabled:v.globalFogUniforms.fogEnabled},vertexShader:m,fragmentShader:p,transparent:!0,side:o.DoubleSide,depthWrite:!0,fog:!0})},[d,h,x,y,M]),P=(0,a.useRef)(0);return(0,l.useFrame)((e,t)=>{w?(P.current+=t,E.uniforms.uTime.value=P.current):(P.current=0,E.uniforms.uTime.value=0)}),(0,a.useEffect)(()=>()=>{E.dispose()},[E]),(0,t.jsx)(t.Fragment,{children:e.map(([e,a])=>{let o=r[0]+2048*e-1024,n=r[2]+2048*a-1024;return(0,t.jsx)("mesh",{geometry:i,material:E,position:[o,r[1],n]},`${e},${a}`)})})});e.s(["WaterBlock",0,x,"WaterMaterial",()=>h],42585)}]);

View file

@ -0,0 +1 @@
html{box-sizing:border-box;background:#000;margin:0;padding:0}*,:before,:after{box-sizing:inherit}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;font-size:100%}body{margin:0;padding:0}main{width:100vw;height:100vh}#canvasContainer{z-index:0;position:absolute;inset:0}#controls{color:#fff;z-index:1;background:#00000080;border-radius:0 0 4px;align-items:center;gap:20px;padding:8px 12px 8px 8px;font-size:13px;display:flex;position:fixed;top:0;left:0}input[type=range]{max-width:80px}.CheckboxField,.Field{align-items:center;gap:6px;display:flex}.StaticShapeLabel{color:#fff;white-space:nowrap;text-align:center;background:#00000080;border-radius:1px;padding:1px 3px;font-size:11px}.StatsPanel{right:0;left:auto!important}.AxisLabel{pointer-events:none;font-size:12px}.AxisLabel[data-axis=x]{color:#f90}.AxisLabel[data-axis=y]{color:#9f0}.AxisLabel[data-axis=z]{color:#09f}.MissionSelect-inputWrapper{align-items:center;display:flex;position:relative}.MissionSelect-shortcut{color:#fff9;pointer-events:none;background:#ffffff26;border-radius:3px;padding:1px 4px;font-family:system-ui,sans-serif;font-size:11px;position:absolute;right:7px}.MissionSelect-input[aria-expanded=true]~.MissionSelect-shortcut{display:none}.MissionSelect-input{color:#fff;background:#0009;border:1px solid #ffffff4d;border-radius:3px;outline:none;width:280px;padding:6px 36px 6px 8px;font-size:14px}.MissionSelect-input[aria-expanded=true]{padding-right:8px}.MissionSelect-input:focus{border-color:#fff9}.MissionSelect-input::placeholder{color:#0000}.MissionSelect-selectedValue{pointer-events:none;align-items:center;gap:6px;display:flex;position:absolute;left:8px;right:36px;overflow:hidden}.MissionSelect-input[aria-expanded=true]~.MissionSelect-selectedValue{display:none}.MissionSelect-selectedName{color:#fff;white-space:nowrap;text-overflow:ellipsis;flex-shrink:1;min-width:0;font-size:14px;font-weight:600;overflow:hidden}.MissionSelect-selectedValue>.MissionSelect-itemType{flex-shrink:0}.MissionSelect-popover{z-index:100;min-width:320px;max-height:var(--popover-available-height,90vh);overscroll-behavior:contain;background:#141414f2;border:1px solid #ffffff80;border-radius:3px;overflow-y:auto;box-shadow:0 8px 24px #0009}.MissionSelect-list{padding:4px 0}.MissionSelect-list:has(>.MissionSelect-group:first-child){padding-top:0}.MissionSelect-group{padding-bottom:4px}.MissionSelect-groupLabel{color:#c6caca;z-index:1;background:#3a4548f2;border-bottom:1px solid #ffffff4d;padding:6px 8px 6px 12px;font-size:13px;font-weight:600;position:sticky;top:0}.MissionSelect-group:not(:last-child){border-bottom:1px solid #ffffff4d}.MissionSelect-item{cursor:pointer;border-radius:4px;outline:none;flex-direction:column;gap:1px;margin:4px 4px 0;padding:6px 8px;scroll-margin-top:32px;display:flex}.MissionSelect-list>.MissionSelect-item:first-child{margin-top:0}.MissionSelect-item[data-active-item]{background:#ffffff26}.MissionSelect-item[aria-selected=true]{background:#6496ff4d}.MissionSelect-itemHeader{align-items:center;gap:6px;display:flex}.MissionSelect-itemName{color:#fff;font-size:14px;font-weight:600}.MissionSelect-itemTypes{gap:3px;display:flex}.MissionSelect-itemType{color:#fff;background:#ff9d0066;border-radius:3px;padding:2px 5px;font-size:10px;font-weight:600}.MissionSelect-itemType:hover{background:#ff9d00b3}.MissionSelect-itemMissionName{color:#ffffff80;font-size:12px}.MissionSelect-noResults{color:#ffffff80;text-align:center;padding:12px 8px;font-size:13px}.LoadingSpinner{border:4px solid #fff3;border-top-color:#fff;border-radius:50%;width:48px;height:48px;animation:1s linear infinite LoadingSpinner-spin}@keyframes LoadingSpinner-spin{to{transform:rotate(360deg)}}#loadingIndicator{pointer-events:none;z-index:1;opacity:.8;flex-direction:column;align-items:center;gap:16px;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}#loadingIndicator[data-complete=true]{animation:.3s ease-out forwards loadingComplete}@keyframes loadingComplete{0%{opacity:1}to{opacity:0}}.LoadingProgress{background:#fff3;border-radius:2px;width:200px;height:4px;overflow:hidden}.LoadingProgress-bar{background:#fff;border-radius:2px;height:100%;transition:width .1s ease-out}.LoadingProgress-text{color:#ffffffb3;font-variant-numeric:tabular-nums;font-size:14px}

View file

@ -6,8 +6,8 @@
7:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"ViewportBoundary"]
9:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"MetadataBoundary"]
b:I[68027,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
:HL["/t2-mapper/_next/static/chunks/7174717ef9c02374.css","style"]
0:{"P":null,"b":"k7CY5XBOHhhqM_Ap9uzeM","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/7174717ef9c02374.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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: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: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: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:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L7",null,{"children":"$@8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$@a"}]}]}],null]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
:HL["/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","style"]
0:{"P":null,"b":"S0fmd6Eh3TfTF-_linDwH","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/c2a0c8ce789a084e.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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: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: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: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:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L7",null,{"children":"$@8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$@a"}]}]}],null]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
8:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
c:I[27201,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"IconMark"]
a:[["$","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"}],["$","$Lc","3",{}]]

View file

@ -3,6 +3,6 @@
4:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"MetadataBoundary"]
5:"$Sreact.suspense"
7:I[27201,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"IconMark"]
0:{"buildId":"k7CY5XBOHhhqM_Ap9uzeM","rsc":["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L2",null,{"children":"$@3"}],["$","div",null,{"hidden":true,"children":["$","$L4",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$@6"}]}]}],null]}],"loading":null,"isPartial":false}
0:{"buildId":"S0fmd6Eh3TfTF-_linDwH","rsc":["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L2",null,{"children":"$@3"}],["$","div",null,{"hidden":true,"children":["$","$L4",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$@6"}]}]}],null]}],"loading":null,"isPartial":false}
3:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
6:[["$","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"}],["$","$L7","3",{}]]

View file

@ -1,5 +1,5 @@
1:"$Sreact.fragment"
2:I[39756,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
3:I[37457,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
:HL["/t2-mapper/_next/static/chunks/7174717ef9c02374.css","style"]
0:{"buildId":"k7CY5XBOHhhqM_Ap9uzeM","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/7174717ef9c02374.css","precedence":"next"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",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}
:HL["/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","style"]
0:{"buildId":"S0fmd6Eh3TfTF-_linDwH","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","precedence":"next"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",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"
2:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"OutletBoundary"]
3:"$Sreact.suspense"
0:{"buildId":"k7CY5XBOHhhqM_Ap9uzeM","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":"S0fmd6Eh3TfTF-_linDwH","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

View file

@ -1,4 +1,4 @@
1:"$Sreact.fragment"
2:I[39756,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
3:I[37457,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
0:{"buildId":"k7CY5XBOHhhqM_Ap9uzeM","rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"loading":null,"isPartial":false}
0:{"buildId":"S0fmd6Eh3TfTF-_linDwH","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/7174717ef9c02374.css","style"]
0:{"buildId":"k7CY5XBOHhhqM_Ap9uzeM","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}
:HL["/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","style"]
0:{"buildId":"S0fmd6Eh3TfTF-_linDwH","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

@ -6,8 +6,8 @@
7:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"ViewportBoundary"]
9:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"MetadataBoundary"]
b:I[68027,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
:HL["/t2-mapper/_next/static/chunks/7174717ef9c02374.css","style"]
0:{"P":null,"b":"k7CY5XBOHhhqM_Ap9uzeM","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/7174717ef9c02374.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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: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: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: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:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L7",null,{"children":"$@8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$@a"}]}]}],null]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
:HL["/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","style"]
0:{"P":null,"b":"S0fmd6Eh3TfTF-_linDwH","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/c2a0c8ce789a084e.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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: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: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: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:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L7",null,{"children":"$@8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$@a"}]}]}],null]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
8:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
c:I[27201,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"IconMark"]
a:[["$","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"}],["$","$Lc","3",{}]]

File diff suppressed because one or more lines are too long

View file

@ -2,14 +2,14 @@
2:I[39756,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
3:I[37457,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"default"]
4:I[47257,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"ClientPageRoot"]
5:I[31713,["/t2-mapper/_next/static/chunks/2a5938bd223c7e45.js","/t2-mapper/_next/static/chunks/39bb94591a93cbfc.js","/t2-mapper/_next/static/chunks/d634c91b8578ac9d.js","/t2-mapper/_next/static/chunks/b9952519fd76ae55.js"],"default"]
5:I[31713,["/t2-mapper/_next/static/chunks/3a3cff0360e2ba9f.js","/t2-mapper/_next/static/chunks/581a9039979a2b81.js","/t2-mapper/_next/static/chunks/ed074071f28b33e1.js","/t2-mapper/_next/static/chunks/acd032a5b4d059f4.js"],"default"]
8:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"OutletBoundary"]
9:"$Sreact.suspense"
b:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"ViewportBoundary"]
d:I[97367,["/t2-mapper/_next/static/chunks/42879de7b8087bc9.js"],"MetadataBoundary"]
f:I[68027,[],"default"]
:HL["/t2-mapper/_next/static/chunks/7174717ef9c02374.css","style"]
0:{"P":null,"b":"k7CY5XBOHhhqM_Ap9uzeM","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/7174717ef9c02374.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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":[["$","$L4",null,{"Component":"$5","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@6","$@7"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/2a5938bd223c7e45.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/39bb94591a93cbfc.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/d634c91b8578ac9d.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/b9952519fd76ae55.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$","$9",null,{"name":"Next.MetadataOutlet","children":"$@a"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$@c"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$9",null,{"name":"Next.Metadata","children":"$@e"}]}]}],null]}],false]],"m":"$undefined","G":["$f",[]],"S":true}
:HL["/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","style"]
0:{"P":null,"b":"S0fmd6Eh3TfTF-_linDwH","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/c2a0c8ce789a084e.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",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":[["$","$L4",null,{"Component":"$5","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@6","$@7"]}}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/3a3cff0360e2ba9f.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/581a9039979a2b81.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/ed074071f28b33e1.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/acd032a5b4d059f4.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$","$9",null,{"name":"Next.MetadataOutlet","children":"$@a"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$@c"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$9",null,{"name":"Next.Metadata","children":"$@e"}]}]}],null]}],false]],"m":"$undefined","G":["$f",[]],"S":true}
6:{}
7:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params"
c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -95,14 +95,14 @@ await mapViewer.press(cameraKey);
await page.waitForNetworkIdle({ idleTime: 250 });
await sleep(100);
const date = new Date()
const date = new Date();
const tempDir = path.join(os.tmpdir(), "t2-mapper");
await fs.mkdir(tempDir, { recursive: true });
const filePrefix = date.toISOString().replace(/([:-]|\..*$)/g, '')
const filePrefix = date.toISOString().replace(/([:-]|\..*$)/g, "");
const outputPath = path.join(
tempDir,
`${filePrefix}.${missionName}.${cameraNumber}.${outputType}`
`${filePrefix}.${missionName}.${cameraNumber}.${outputType}`,
);
// Take screenshot

View file

@ -1,38 +1,38 @@
/**
* Color parsing utilities for Tribes 2 mission files.
*
* Torque (2001) worked in gamma/sRGB space - colors in mission files are
* specified as they should appear on screen. Three.js expects linear colors
* for lighting calculations, so convert with .convertSRGBToLinear() when
* passing to lit materials.
*/
import { Color, SRGBColorSpace } from "three";
/**
* Parse a Tribes 2 color string (space-separated RGB or RGBA values 0-1).
* The values are interpreted as sRGB and stored as linear internally by Three.js.
*
* @param colorString - Space-separated "R G B" or "R G B A" string (0-1 range)
* @returns Color (linear internally), or undefined if no string
*/
export function parseColor(colorString: string | undefined): Color | undefined {
if (!colorString) return undefined;
const parts = colorString.split(" ").map((s) => parseFloat(s));
const [r = 0, g = 0, b = 0] = parts;
// Interpret as sRGB, Three.js converts to linear internally
return new Color().setRGB(r, g, b, SRGBColorSpace);
}
/**
* Parse a Tribes 2 color string and convert to linear color space.
* Use this when passing colors to Three.js lit materials.
*
* @param colorString - Space-separated "R G B" or "R G B A" string (0-1 range)
* @returns Color in linear space, or undefined if no string
*/
export function parseColorLinear(
colorString: string | undefined,
): Color | undefined {
const color = parseColor(colorString);
return color?.convertSRGBToLinear();
}
/**
* Color parsing utilities for Tribes 2 mission files.
*
* Torque (2001) worked in gamma/sRGB space - colors in mission files are
* specified as they should appear on screen. Three.js expects linear colors
* for lighting calculations, so convert with .convertSRGBToLinear() when
* passing to lit materials.
*/
import { Color, SRGBColorSpace } from "three";
/**
* Parse a Tribes 2 color string (space-separated RGB or RGBA values 0-1).
* The values are interpreted as sRGB and stored as linear internally by Three.js.
*
* @param colorString - Space-separated "R G B" or "R G B A" string (0-1 range)
* @returns Color (linear internally), or undefined if no string
*/
export function parseColor(colorString: string | undefined): Color | undefined {
if (!colorString) return undefined;
const parts = colorString.split(" ").map((s) => parseFloat(s));
const [r = 0, g = 0, b = 0] = parts;
// Interpret as sRGB, Three.js converts to linear internally
return new Color().setRGB(r, g, b, SRGBColorSpace);
}
/**
* Parse a Tribes 2 color string and convert to linear color space.
* Use this when passing colors to Three.js lit materials.
*
* @param colorString - Space-separated "R G B" or "R G B A" string (0-1 range)
* @returns Color in linear space, or undefined if no string
*/
export function parseColorLinear(
colorString: string | undefined,
): Color | undefined {
const color = parseColor(colorString);
return color?.convertSRGBToLinear();
}

View file

@ -70,7 +70,6 @@ function createMaterialFromFlags(
const isTranslucent = flagNames.has("Translucent");
const isAdditive = flagNames.has("Additive");
const isSelfIlluminating = flagNames.has("SelfIlluminating");
const neverEnvMap = flagNames.has("NeverEnvMap");
// SelfIlluminating materials are unlit (use MeshBasicMaterial)
if (isSelfIlluminating) {
@ -334,21 +333,25 @@ export function DebugPlaceholder({
* pattern used across shape-rendering components.
*/
export function ShapeRenderer({
shapeName,
loadingColor = "yellow",
children,
}: {
shapeName: string | undefined;
loadingColor?: string;
children?: React.ReactNode;
}) {
const { object, shapeName } = useShapeInfo();
if (!shapeName) {
return <DebugPlaceholder color="orange" />;
return (
<DebugPlaceholder color="orange" label={`${object._id}: <missing>`} />
);
}
return (
<ErrorBoundary
fallback={<DebugPlaceholder color="red" label={shapeName} />}
fallback={
<DebugPlaceholder color="red" label={`${object._id}: ${shapeName}`} />
}
>
<Suspense fallback={<ShapePlaceholder color={loadingColor} />}>
<ShapeModel />
@ -359,7 +362,7 @@ export function ShapeRenderer({
}
export const ShapeModel = memo(function ShapeModel() {
const { shapeName, isOrganic } = useShapeInfo();
const { object, shapeName, isOrganic } = useShapeInfo();
const { debugMode } = useDebug();
const { nodes } = useStaticShape(shapeName);
@ -501,7 +504,11 @@ export const ShapeModel = memo(function ShapeModel() {
) : null}
</Suspense>
))}
{debugMode ? <FloatingLabel>{shapeName}</FloatingLabel> : null}
{debugMode ? (
<FloatingLabel>
{object._id}: {shapeName}
</FloatingLabel>
) : null}
</group>
);
});

View file

@ -3,10 +3,18 @@ import { MissionSelect } from "./MissionSelect";
export function InspectorControls({
missionName,
missionType,
onChangeMission,
}: {
missionName: string;
onChangeMission: (name: string) => void;
missionType: string;
onChangeMission: ({
missionName,
missionType,
}: {
missionName: string;
missionType: string;
}) => void;
}) {
const {
fogEnabled,
@ -28,7 +36,11 @@ export function InspectorControls({
onPointerDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
>
<MissionSelect value={missionName} onChange={onChangeMission} />
<MissionSelect
value={missionName}
missionType={missionType}
onChange={onChangeMission}
/>
<div className="CheckboxField">
<input
id="fogInput"

View file

@ -186,7 +186,13 @@ function InteriorMesh({ node }: { node: Mesh }) {
}
export const InteriorModel = memo(
({ interiorFile }: { interiorFile: string }) => {
({
object,
interiorFile,
}: {
object: TorqueObject;
interiorFile: string;
}) => {
const { nodes } = useInterior(interiorFile);
const debugContext = useDebug();
const debugMode = debugContext?.debugMode ?? false;
@ -198,7 +204,11 @@ export const InteriorModel = memo(
.map(([name, node]: [string, any]) => (
<InteriorMesh key={name} node={node} />
))}
{debugMode ? <FloatingLabel>{interiorFile}</FloatingLabel> : null}
{debugMode ? (
<FloatingLabel>
{object._id}: {interiorFile}
</FloatingLabel>
) : null}
</group>
);
},
@ -239,10 +249,12 @@ export const InteriorInstance = memo(function InteriorInstance({
return (
<group position={position} quaternion={q} scale={scale}>
<ErrorBoundary
fallback={<DebugInteriorPlaceholder label={interiorFile} />}
fallback={
<DebugInteriorPlaceholder label={`${object._id}: ${interiorFile}`} />
}
>
<Suspense fallback={<InteriorPlaceholder color="orange" />}>
<InteriorModel interiorFile={interiorFile} />
<InteriorModel object={object} interiorFile={interiorFile} />
</Suspense>
</ErrorBoundary>
</group>

View file

@ -33,9 +33,9 @@ export function Item({ object }: { object: TorqueObject }) {
const label = isFlag && teamName ? `${teamName} Flag` : null;
return (
<ShapeInfoProvider shapeName={shapeName} type="Item">
<ShapeInfoProvider type="Item" object={object} shapeName={shapeName}>
<group position={position} quaternion={q} scale={scale}>
<ShapeRenderer shapeName={shapeName} loadingColor="pink">
<ShapeRenderer loadingColor="pink">
{label ? <FloatingLabel opacity={0.6}>{label}</FloatingLabel> : null}
</ShapeRenderer>
</group>

View file

@ -3,8 +3,8 @@ import picomatch from "picomatch";
import { loadMission } from "../loaders";
import { type ParsedMission } from "../mission";
import { createScriptLoader } from "../torqueScript/scriptLoader.browser";
import { renderObject } from "./renderObject";
import { memo, useEffect, useState } from "react";
import { SimObject } from "./SimObject";
import { memo, useEffect, useMemo, useState } from "react";
import { RuntimeProvider } from "./RuntimeProvider";
import {
createProgressTracker,
@ -20,6 +20,7 @@ import {
getResourceMap,
getSourceAndPath,
} from "../manifest";
import { MissionProvider } from "./MissionContext";
const loadScript = createScriptLoader();
// Shared cache for parsed scripts - survives runtime restarts
@ -30,7 +31,7 @@ const fileSystem: FileSystemHandler = {
return getResourceList()
.filter((path) => isMatch(path))
.map((resourceKey) => {
const [sourcePath, actualPath] = getSourceAndPath(resourceKey);
const [, actualPath] = getSourceAndPath(resourceKey);
return actualPath;
});
},
@ -56,6 +57,7 @@ interface ExecutedMissionState {
function useExecutedMission(
missionName: string,
missionType: string,
parsedMission: ParsedMission | undefined,
): ExecutedMissionState {
const [state, setState] = useState<ExecutedMissionState>({
@ -70,8 +72,6 @@ function useExecutedMission(
}
const controller = new AbortController();
// FIXME: Always just runs as the first game type for now...
const missionType = parsedMission.missionTypes[0];
// Create progress tracker and update state on changes
const progressTracker = createProgressTracker();
@ -138,19 +138,33 @@ function useExecutedMission(
interface MissionProps {
name: string;
missionType: string;
setMissionType: (type: string) => void;
onLoadingChange?: (isLoading: boolean, progress?: number) => void;
}
export const Mission = memo(function Mission({
name,
missionType,
onLoadingChange,
}: MissionProps) {
const { data: parsedMission } = useParsedMission(name);
const { missionGroup, runtime, progress } = useExecutedMission(
name,
missionType,
parsedMission,
);
const isLoading = !missionGroup || !runtime;
const isLoading = !parsedMission || !missionGroup || !runtime;
const missionContext = useMemo(
() => ({
metadata: parsedMission,
missionType,
missionGroup,
}),
[parsedMission, missionType, missionGroup],
);
useEffect(() => {
onLoadingChange?.(isLoading, progress);
@ -161,8 +175,10 @@ export const Mission = memo(function Mission({
}
return (
<RuntimeProvider runtime={runtime}>
{renderObject(missionGroup)}
</RuntimeProvider>
<MissionProvider value={missionContext}>
<RuntimeProvider runtime={runtime}>
<SimObject object={missionGroup} />
</RuntimeProvider>
</MissionProvider>
);
});

View file

@ -0,0 +1,17 @@
import { createContext, useContext } from "react";
import { ParsedMission } from "../mission";
import { TorqueObject } from "../torqueScript";
export type MissionContextType = {
metadata: ParsedMission;
missionType: string;
missionGroup: TorqueObject;
};
const MissionContext = createContext<MissionContextType | null>(null);
export const MissionProvider = MissionContext.Provider;
export function useMission() {
return useContext(MissionContext);
}

View file

@ -132,7 +132,11 @@ function MissionItemContent({ mission }: { mission: MissionItem }) {
{mission.missionTypes.length > 0 && (
<span className="MissionSelect-itemTypes">
{mission.missionTypes.map((type) => (
<span key={type} className="MissionSelect-itemType">
<span
key={type}
className="MissionSelect-itemType"
data-mission-type={type}
>
{type}
</span>
))}
@ -148,19 +152,41 @@ function MissionItemContent({ mission }: { mission: MissionItem }) {
export function MissionSelect({
value,
missionType,
onChange,
}: {
value: string;
onChange: (missionName: string) => void;
missionType: string;
onChange: ({
missionName,
missionType,
}: {
missionName: string;
missionType: string | undefined;
}) => void;
}) {
const [searchValue, setSearchValue] = useState("");
const inputRef = useRef<HTMLInputElement>(null);
const missionTypeRef = useRef<string | null>(missionType);
const combobox = useComboboxStore({
resetValueOnHide: true,
selectedValue: value,
setSelectedValue: (newValue) => {
if (newValue) onChange(newValue);
if (newValue) {
let newMissionType = missionTypeRef.current;
const availableMissionTypes = getMissionInfo(newValue).missionTypes;
if (
!newMissionType ||
!availableMissionTypes.includes(newMissionType)
) {
newMissionType = availableMissionTypes[0];
}
onChange({
missionName: newValue,
missionType: newMissionType,
});
}
},
setValue: (value) => {
startTransition(() => setSearchValue(value));
@ -200,6 +226,40 @@ export function MissionSelect({
? filteredResults.missions.length === 0
: filteredResults.groups.length === 0;
const renderItem = (mission) => {
return (
<ComboboxItem
key={mission.missionName}
value={mission.missionName}
className="MissionSelect-item"
focusOnHover
onClick={(event) => {
if (event.target && event.target instanceof HTMLElement) {
const missionType = event.target.dataset.missionType;
if (missionType) {
missionTypeRef.current = missionType;
const isOnlyMissionTypeChange = mission.missionName === value;
if (isOnlyMissionTypeChange) {
// Need to trigger change ourselves, because Combobox sees this
// as no change.
onChange({
missionName: mission.missionName,
missionType,
});
}
} else {
missionTypeRef.current = null;
}
} else {
missionTypeRef.current = null;
}
}}
>
<MissionItemContent mission={mission} />
</ComboboxItem>
);
};
return (
<ComboboxProvider store={combobox}>
<div className="MissionSelect-inputWrapper">
@ -213,21 +273,23 @@ export function MissionSelect({
combobox.show();
}}
/>
<div className="MissionSelect-selectedValue">
<span className="MissionSelect-selectedName">{displayValue}</span>
{missionType && (
<span
className="MissionSelect-itemType"
data-mission-type={missionType}
>
{missionType}
</span>
)}
</div>
<kbd className="MissionSelect-shortcut">{isMac ? "⌘K" : "^K"}</kbd>
</div>
<ComboboxPopover gutter={4} fitViewport className="MissionSelect-popover">
<ComboboxList className="MissionSelect-list">
{filteredResults.type === "flat"
? filteredResults.missions.map((mission) => (
<ComboboxItem
key={mission.missionName}
value={mission.missionName}
className="MissionSelect-item"
focusOnHover
>
<MissionItemContent mission={mission} />
</ComboboxItem>
))
? filteredResults.missions.map(renderItem)
: filteredResults.groups.map(([groupName, missions]) =>
groupName ? (
<ComboboxGroup
@ -237,29 +299,11 @@ export function MissionSelect({
<ComboboxGroupLabel className="MissionSelect-groupLabel">
{groupName}
</ComboboxGroupLabel>
{missions.map((mission) => (
<ComboboxItem
key={mission.missionName}
value={mission.missionName}
className="MissionSelect-item"
focusOnHover
>
<MissionItemContent mission={mission} />
</ComboboxItem>
))}
{missions.map(renderItem)}
</ComboboxGroup>
) : (
<Fragment key="ungrouped">
{missions.map((mission) => (
<ComboboxItem
key={mission.missionName}
value={mission.missionName}
className="MissionSelect-item"
focusOnHover
>
<MissionItemContent mission={mission} />
</ComboboxItem>
))}
{missions.map(renderItem)}
</Fragment>
),
)}

View file

@ -1,4 +1,5 @@
import { createContext, ReactNode, useContext, useMemo } from "react";
import { TorqueObject } from "../torqueScript";
export type StaticShapeType = "TSStatic" | "StaticShape" | "Item" | "Turret";
@ -17,25 +18,44 @@ export function isOrganicShape(shapeName: string): boolean {
return ORGANIC_PATTERN.test(shapeName);
}
const ShapeInfoContext = createContext(null);
interface ShapeInfoContextValue {
object: TorqueObject;
shapeName: string;
type: StaticShapeType;
isOrganic: boolean;
}
export function useShapeInfo() {
return useContext(ShapeInfoContext);
const ShapeInfoContext = createContext<ShapeInfoContextValue | null>(null);
export function useShapeInfo(): ShapeInfoContextValue {
const context = useContext(ShapeInfoContext);
if (!context) {
throw new Error("useShapeInfo must be used within ShapeInfoProvider");
}
return context;
}
export function ShapeInfoProvider({
children,
object,
shapeName,
type,
}: {
object: TorqueObject;
children: ReactNode;
shapeName: string;
type: StaticShapeType;
}) {
const isOrganic = useMemo(() => isOrganicShape(shapeName), [shapeName]);
const context = useMemo(
() => ({ shapeName, type, isOrganic }),
[shapeName, type, isOrganic],
() => ({
object,
shapeName,
type,
isOrganic,
}),
[object, shapeName, type, isOrganic],
);
return (

View file

@ -1,6 +1,6 @@
import { createContext, useContext, useMemo } from "react";
import type { TorqueObject } from "../torqueScript";
import { renderObject } from "./renderObject";
import { SimObject } from "./SimObject";
export type SimGroupContextType = {
object: TorqueObject;
@ -51,7 +51,9 @@ export function SimGroup({ object }: { object: TorqueObject }) {
return (
<SimGroupContext.Provider value={simGroup}>
{(object._children ?? []).map((child, i) => renderObject(child, i))}
{(object._children ?? []).map((child, i) => (
<SimObject object={child} key={child._id} />
))}
</SimGroupContext.Provider>
);
}

View file

@ -1,4 +1,4 @@
import { lazy, Suspense } from "react";
import { lazy, Suspense, useMemo } from "react";
import type { TorqueObject } from "../torqueScript";
import { TerrainBlock } from "./TerrainBlock";
import { SimGroup } from "./SimGroup";
@ -12,6 +12,8 @@ import { Turret } from "./Turret";
import { WayPoint } from "./WayPoint";
import { Camera } from "./Camera";
import { useSettings } from "./SettingsProvider";
import { useMission } from "./MissionContext";
import { getProperty } from "../mission";
const AudioEmitter = lazy(() =>
import("./AudioEmitter").then((mod) => ({ default: mod.AudioEmitter })),
@ -27,7 +29,7 @@ const ForceFieldBare = lazy(() =>
import("./ForceFieldBare").then((mod) => ({ default: mod.ForceFieldBare })),
);
// Not every map will have force fields.
// Not every map will have water.
const WaterBlock = lazy(() =>
import("./WaterBlock").then((mod) => ({ default: mod.WaterBlock })),
);
@ -49,10 +51,26 @@ const componentMap = {
WayPoint,
};
export function renderObject(object: TorqueObject, key?: string | number) {
export function SimObject({ object }: { object: TorqueObject }) {
const { missionType } = useMission();
// FIXME: In theory we could make sure TorqueScript is calling `hide()`
// based on the mission type already, which is built-in behavior, then just
// make sure we respect the hidden/visible state here. For now do it this way.
const shouldShowObject = useMemo(() => {
const missionTypesList = new Set(
(getProperty(object, "missionTypesList") ?? "")
.toLowerCase()
.split(/s+/)
.filter(Boolean),
);
return (
!missionTypesList.size || missionTypesList.has(missionType.toLowerCase())
);
}, [object, missionType]);
const Component = componentMap[object._className];
return Component ? (
<Suspense key={key}>
return shouldShowObject && Component ? (
<Suspense>
<Component object={object} />
</Suspense>
) : null;

View file

@ -6,9 +6,9 @@ import { Color, Fog } from "three";
import type { TorqueObject } from "../torqueScript";
import { getInt, getProperty } from "../mission";
import { useSettings } from "./SettingsProvider";
import { BASE_URL, loadDetailMapList, textureToUrl } from "../loaders";
import { loadDetailMapList, textureToUrl } from "../loaders";
import { CloudLayers } from "./CloudLayers";
import { parseFogState, type FogState, type FogVolume } from "./FogProvider";
import { parseFogState, type FogState } from "./FogProvider";
import { installCustomFogShader } from "../fogShader";
import {
globalFogUniforms,
@ -17,8 +17,6 @@ import {
resetGlobalFogUniforms,
} from "../globalFogUniforms";
const FALLBACK_TEXTURE_URL = `${BASE_URL}/black.png`;
// Track if fog shader has been installed (idempotent installation)
let fogShaderInstalled = false;

View file

@ -22,9 +22,9 @@ export function StaticShape({ object }: { object: TorqueObject }) {
}
return (
<ShapeInfoProvider shapeName={shapeName} type="StaticShape">
<ShapeInfoProvider type="StaticShape" object={object} shapeName={shapeName}>
<group position={position} quaternion={q} scale={scale}>
<ShapeRenderer shapeName={shapeName} />
<ShapeRenderer />
</group>
</ShapeInfoProvider>
);

View file

@ -16,9 +16,9 @@ export function TSStatic({ object }: { object: TorqueObject }) {
}
return (
<ShapeInfoProvider shapeName={shapeName} type="TSStatic">
<ShapeInfoProvider type="TSStatic" object={object} shapeName={shapeName}>
<group position={position} quaternion={q} scale={scale}>
<ShapeRenderer shapeName={shapeName} />
<ShapeRenderer />
</group>
</ShapeInfoProvider>
);

View file

@ -30,13 +30,17 @@ export function Turret({ object }: { object: TorqueObject }) {
}
return (
<ShapeInfoProvider shapeName={shapeName} type="Turret">
<ShapeInfoProvider type="Turret" object={object} shapeName={shapeName}>
<group position={position} quaternion={q} scale={scale}>
<ShapeRenderer shapeName={shapeName} />
<ShapeRenderer />
{barrelShapeName ? (
<ShapeInfoProvider shapeName={barrelShapeName} type="Turret">
<ShapeInfoProvider
type="Turret"
object={object}
shapeName={barrelShapeName}
>
<group position={[0, 1.5, 0]}>
<ShapeRenderer shapeName={barrelShapeName} />
<ShapeRenderer />
</group>
</ShapeInfoProvider>
) : null}

View file

@ -109,7 +109,7 @@ export const WaterBlock = memo(function WaterBlock({
// TODO: Use this for terrain intersection masking (reject water blocks where
// terrain height > surfaceZ + waveMagnitude/2). Requires TerrainProvider.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const surfaceZ = position[1] + scaleY;
// const surfaceZ = position[1] + scaleY;
// Wave magnitude affects terrain masking (Torque adds half to surface height)
const waveMagnitude = getFloat(object, "waveMagnitude") ?? 1.0;
@ -148,7 +148,10 @@ export const WaterBlock = memo(function WaterBlock({
// Matches fluidQuadTree.cc RunQuadTree():
// I = (s32)(m_Eye.X / 2048.0f);
// if( m_Eye.X < 0.0f ) I--;
const calculateReps = (camX: number, camZ: number): Array<[number, number]> => {
const calculateReps = (
camX: number,
camZ: number,
): Array<[number, number]> => {
// Convert camera to terrain space
const terrainCamX = camX + TERRAIN_OFFSET;
const terrainCamZ = camZ + TERRAIN_OFFSET;

View file

@ -2,10 +2,8 @@ import { useMemo } from "react";
import type { TorqueObject } from "../torqueScript";
import { getPosition, getProperty } from "../mission";
import { FloatingLabel } from "./FloatingLabel";
import { useSimGroup } from "./SimGroup";
export function WayPoint({ object }: { object: TorqueObject }) {
const simGroup = useSimGroup();
const position = useMemo(() => getPosition(object), [object]);
const label = getProperty(object, "name");

View file

@ -46,7 +46,11 @@ function createAtlas(textures: Texture[]): IflAtlas {
textures.forEach((tex, i) => {
const col = i % columns;
const row = Math.floor(i / columns);
ctx.drawImage(tex.image as CanvasImageSource, col * frameWidth, row * frameHeight);
ctx.drawImage(
tex.image as CanvasImageSource,
col * frameWidth,
row * frameHeight,
);
});
const texture = new CanvasTexture(canvas);

View file

@ -120,7 +120,7 @@ export function getStandardTextureResourceKey(resourcePath: string) {
* 6. .gif
*/
// Not used for now!
const palettedTextureExt = ["", ".bm8", ".bmp", ".jpg", ".png", ".gif"];
// const palettedTextureExt = ["", ".bm8", ".bmp", ".jpg", ".png", ".gif"];
export function getLocalFilePath(resourcePath: string): string {
const resourceKey = getResourceKey(resourcePath);

View file

@ -19,6 +19,28 @@ interface CommentSection {
comments: string[];
}
const normalizedMissionTypes = {
arena: "Arena",
bounty: "Bounty",
cnh: "CnH",
ctf: "CTF",
dm: "DM",
dnd: "DnD",
hunters: "Hunters",
lakrabbit: "LakRabbit",
lakzm: "LakZM",
lctf: "LCTF",
none: "None",
rabbit: "Rabbit",
sctf: "SCtF",
siege: "Siege",
singleplayer: "SinglePlayer",
tdm: "TDM",
teamhunters: "TeamHunters",
teamlak: "TeamLak",
tr2: "TR2",
};
function parseCommentMarker(text: string) {
let match;
match = text.match(sectionBeginComment);
@ -121,7 +143,12 @@ export function parseMissionScript(script: string): ParsedMission {
return {
displayName: pragma.displayname ?? null,
missionTypes: pragma.missiontypes?.split(/\s+/).filter(Boolean) ?? [],
missionTypes:
pragma.missiontypes
?.split(/\s+/)
.filter(Boolean)
.map((name) => normalizedMissionTypes[name.toLowerCase()] ?? name) ??
[],
missionBriefing: getSection("MISSION BRIEFING"),
briefingWav: pragma.briefingwav ?? null,
bitmap: pragma.bitmap ?? null,

View file

@ -46,7 +46,7 @@ export function parseTerrainBuffer(arrayBuffer: ArrayBufferLike): TerrainFile {
const alphaMaps = [];
for (const textureName of textureNames) {
for (const _textureName of textureNames) {
const alphaMap = new Uint8Array(SIZE * SIZE);
for (let j = 0; j < SIZE * SIZE; j++) {
var alphaMats = dataView.getUint8(offset++);

View file

@ -360,7 +360,7 @@ void RE_Direct_TerrainShadow( const in IncidentLight directLight, const in vec3
// Debug mode: overlay green grid matching terrain grid squares (256x256)
float gridIntensity = terrainDebugGrid(vMapUv, 256.0, 1.5);
vec3 gridColor = vec3(0.0, 0.8, 0.4); // Green
gl_FragColor.rgb = mix(gl_FragColor.rgb, gridColor, gridIntensity * 0.05);
gl_FragColor.rgb = mix(gl_FragColor.rgb, gridColor, gridIntensity * 0.1);
#endif
#include <tonemapping_fragment>`,

View file

@ -481,7 +481,7 @@ describe("TorqueScript Runtime", () => {
});
it("resolves objects by numeric ID", () => {
const { $, $g } = run(`
const { $ } = run(`
new ScriptObject(TestObj) {
value = 100;
};
@ -497,7 +497,7 @@ describe("TorqueScript Runtime", () => {
it("handles direct indexed access on bareword objects", () => {
// In TorqueScript, obj[idx] sets a property directly on the object
const { $, $g } = run(`
const { $g } = run(`
new ScriptObject(Data) {};
Data[0] = "first";
Data[1] = "second";
@ -527,7 +527,7 @@ describe("TorqueScript Runtime", () => {
});
it("handles post-increment on bareword object properties", () => {
const { $, $g } = run(`
const { $g } = run(`
new ScriptObject(Counter) {
value = 10;
};
@ -539,7 +539,7 @@ describe("TorqueScript Runtime", () => {
});
it("handles compound assignment on bareword object properties", () => {
const { $, $g } = run(`
const { $g } = run(`
new ScriptObject(Score) {
points = 100;
};
@ -2037,7 +2037,6 @@ describe("TorqueScript Runtime", () => {
// If parallel: starts happen before all ends
// If serial: each end would happen before the next start
const aStart = loadOrder.indexOf("start:scripts/a.cs");
const bStart = loadOrder.indexOf("start:scripts/b.cs");
const cStart = loadOrder.indexOf("start:scripts/c.cs");
const aEnd = loadOrder.indexOf("end:scripts/a.cs");

View file

@ -1,14 +1,11 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"noUnusedLocals": true,
"noEmit": true,
"incremental": true,
"module": "esnext",
@ -19,9 +16,7 @@
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@/*": [
"*"
]
"@/*": ["*"]
},
"plugins": [
{
@ -36,7 +31,5 @@
"**/*.tsx",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
"exclude": ["node_modules"]
}