Skip to content
71 changes: 43 additions & 28 deletions fission/src/mirabuf/MirabufSceneObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,22 +600,28 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier {
return true
}

public updateScoringZones(render?: boolean) {
this._scoringZones.filter(zone => zone.id != -1).forEach(zone => World.sceneRenderer.removeSceneObject(zone.id))
this._scoringZones = []

if (this._fieldPreferences && this._fieldPreferences.scoringZones) {
for (let i = 0; i < this._fieldPreferences.scoringZones.length; i++) {
const newZone = new ScoringZoneSceneObject(
this,
i,
render ?? PreferencesSystem.getGlobalPreference("RenderScoringZones")
)
this._scoringZones.push(newZone)
World.sceneRenderer.registerSceneObject(newZone)
}
}
}
public updateScoringZones(render?: boolean) {
this._scoringZones.filter(zone => zone.id != -1).forEach(zone => World.sceneRenderer.removeSceneObject(zone.id))
this._scoringZones = []

if (this._fieldPreferences && this._fieldPreferences.scoringZones) {
// Auto-sync devtool data so scoring zones persist across reloads
const parts = this._mirabufInstance.parser.assembly.data?.parts
if (parts) {
const editor = new FieldMiraEditor(parts)
editor.setUserData("devtool:scoring_zones", this._fieldPreferences.scoringZones)
}
for (let i = 0; i < this._fieldPreferences.scoringZones.length; i++) {
const newZone = new ScoringZoneSceneObject(
this,
i,
render ?? PreferencesSystem.getGlobalPreference("RenderScoringZones")
)
this._scoringZones.push(newZone)
World.sceneRenderer.registerSceneObject(newZone)
}
}
}

public updateProtectedZones(render?: boolean) {
this._protectedZones
Expand Down Expand Up @@ -809,18 +815,27 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier {

this._fieldPreferences = PreferencesSystem.getFieldPreferences(this.assemblyName)

// For fields, sync devtool data with field preferences
if (this.miraType === MiraType.FIELD) {
const parts = this._mirabufInstance.parser.assembly.data?.parts
if (parts) {
const editor = new FieldMiraEditor(parts)
devtoolKeys.forEach(key => {
devtoolHandlers[key].set(this, editor.getUserData(key))
})
PreferencesSystem.setFieldPreferences(this.assemblyName, this._fieldPreferences)
PreferencesSystem.savePreferences()
}
}
// For fields, sync devtool data with field preferences
if (this.miraType === MiraType.FIELD) {
const parts = this._mirabufInstance.parser.assembly.data?.parts
if (parts) {
const editor = new FieldMiraEditor(parts)
// First, push current preferences into devtool so handlers don't overwrite saved prefs
if (this._fieldPreferences) {
if (this._fieldPreferences.scoringZones && this._fieldPreferences.scoringZones.length > 0) {
editor.setUserData("devtool:scoring_zones", this._fieldPreferences.scoringZones)
}
if (this._fieldPreferences.spawnLocations?.hasConfiguredLocations) {
editor.setUserData("devtool:spawn_locations", this._fieldPreferences.spawnLocations)
}
}
devtoolKeys.forEach(key => {
devtoolHandlers[key].set(this, editor.getUserData(key))
})
PreferencesSystem.setFieldPreferences(this.assemblyName, this._fieldPreferences)
PreferencesSystem.savePreferences()
}
}
}

public updateSimConfig(config: SimConfigData | undefined) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import PreferencesSystem from "@/systems/preferences/PreferencesSystem"
import type { ProtectedZonePreferences } from "@/systems/preferences/PreferenceTypes"
import Label from "@/ui/components/Label"
import ManageProtectedZonesInterface from "./ManageProtectedZonesInterface"
import ZoneConfigInterface from "./ProtectedZoneConfigInterface"
import ProtectedZoneConfigInterface from "./ProtectedZoneConfigInterface"

const protectedZones = (zones: ProtectedZonePreferences[] | undefined, field: MirabufSceneObject | undefined) => {
const saveProtectedZones = (zones: ProtectedZonePreferences[] | undefined, field: MirabufSceneObject | undefined) => {
if (!zones || !field) return

const fieldPrefs = field.fieldPreferences
Expand Down Expand Up @@ -48,11 +48,11 @@ const ConfigureProtectedZonesInterface: React.FC<ConfigureZonesProps> = ({ selec
</Stack>
</Stack>
<Divider />
<ZoneConfigInterface
<ProtectedZoneConfigInterface
selectedField={selectedField}
selectedZone={selectedZone}
saveAllZones={() => {
protectedZones(selectedField.fieldPreferences?.protectedZones, selectedField)
saveProtectedZones(selectedField.fieldPreferences?.protectedZones, selectedField)
}}
/>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { ScoringZonePreferences } from "@/systems/preferences/PreferenceTyp
import Label from "@/ui/components/Label"
import { SynthesisIcons } from "@/ui/components/StyledComponents"
import ManageScoringZonesInterface from "./ManageScoringZonesInterface"
import ZoneConfigInterface from "./ScoringZoneConfigInterface"
import ScoringZoneConfigInterface from "./ScoringZoneConfigInterface"

const saveZones = (zones: ScoringZonePreferences[] | undefined, field: MirabufSceneObject | undefined) => {
if (!zones || !field) return
Expand Down Expand Up @@ -60,7 +60,7 @@ const ConfigureScoringZonesInterface: React.FC<ConfigureZonesProps> = ({ selecte
</Stack>
</Stack>
<Divider />
<ZoneConfigInterface
<ScoringZoneConfigInterface
selectedField={selectedField}
selectedZone={selectedZone}
saveAllZones={() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface ProtectedZonesProps {
selectZone: (zone: ProtectedZonePreferences) => void
}

const ManageZonesInterface: React.FC<ProtectedZonesProps> = ({ selectedField, initialZones, selectZone }) => {
const ManageProtectedZonesInterface: React.FC<ProtectedZonesProps> = ({ selectedField, initialZones, selectZone }) => {
const baseProps: ManageZonesBaseProps<ProtectedZonePreferences> = {
selectedField,
initialZones,
Expand All @@ -41,4 +41,4 @@ const ManageZonesInterface: React.FC<ProtectedZonesProps> = ({ selectedField, in
return <ManageZonesBase {...baseProps} />
}

export default ManageZonesInterface
export default ManageProtectedZonesInterface
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface ScoringZonesProps {
selectZone: (zone: ScoringZonePreferences) => void
}

const ManageZonesInterface: React.FC<ScoringZonesProps> = ({ selectedField, initialZones, selectZone }) => {
const ManageScoringZonesInterface: React.FC<ScoringZonesProps> = ({ selectedField, initialZones, selectZone }) => {
const baseProps: ManageZonesBaseProps<ScoringZonePreferences> = {
selectedField,
initialZones,
Expand All @@ -39,4 +39,4 @@ const ManageZonesInterface: React.FC<ScoringZonesProps> = ({ selectedField, init
return <ManageZonesBase {...baseProps} />
}

export default ManageZonesInterface
export default ManageScoringZonesInterface
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import {
Select,
TextField,
} from "@mui/material"
import { useState } from "react"
import type * as THREE from "three"
import { useState, useCallback, useMemo } from "react"
import ProtectedZoneSceneObject from "@/mirabuf/ProtectedZoneSceneObject"
import { ContactType } from "@/mirabuf/ZoneTypes"
import { MatchModeType } from "@/systems/match_mode/MatchModeTypes"
Expand Down Expand Up @@ -43,28 +42,34 @@ interface ZoneConfigProps {
saveAllZones: () => void
}

const ZoneConfigInterface: React.FC<ZoneConfigProps> = ({ selectedField, selectedZone, saveAllZones }) => {
const ProtectedZoneConfigInterface: React.FC<ZoneConfigProps> = ({ selectedField, selectedZone, saveAllZones }) => {
const [penaltyPoints, setPenaltyPoints] = useState<number>(selectedZone.penaltyPoints)
const [contactType, setContactType] = useState<ContactType>(selectedZone.contactType || ContactType.ROBOT_ENTERS)
const [activeDuring, setActiveDuring] = useState<MatchModeType[]>(selectedZone.activeDuring)

// Use the cloned FIRST materials like before
const materials = {
red: ProtectedZoneSceneObject.redMaterial.clone() as THREE.MeshPhongMaterial,
blue: ProtectedZoneSceneObject.blueMaterial.clone() as THREE.MeshPhongMaterial,
}
const materials = useMemo(() => ({
red: ProtectedZoneSceneObject.redMaterial.clone(),
blue: ProtectedZoneSceneObject.blueMaterial.clone(),
}), [])

const applyExtrasOnSave = useCallback((zone: ProtectedZonePreferences) => {
zone.penaltyPoints = penaltyPoints
zone.contactType = contactType
zone.activeDuring = activeDuring
}, [penaltyPoints, contactType, activeDuring])

const removeZoneObject = useCallback((field: MirabufSceneObject, zone: ProtectedZonePreferences) => {
field.removeProtectedZoneObject(zone)
}, [])

return (
<ZoneConfigBase
selectedField={selectedField}
selectedZone={selectedZone}
attachAndPersistZone={attachAndPersistZone}
applyExtrasOnSave={zone => {
zone.penaltyPoints = penaltyPoints
zone.contactType = contactType
zone.activeDuring = activeDuring
}}
removeZoneObject={(field, zone) => field.removeProtectedZoneObject(zone)}
applyExtrasOnSave={applyExtrasOnSave}
removeZoneObject={removeZoneObject}
saveAllZones={saveAllZones}
materials={materials}
>
Expand All @@ -85,12 +90,12 @@ const ZoneConfigInterface: React.FC<ZoneConfigProps> = ({ selectedField, selecte
target: { value },
} = e
setActiveDuring(
(typeof value === "string" ? (value as string).split(",") : value) as MatchModeType[]
typeof value === "string" ? value.split(",") as MatchModeType[] : value
)
}}
value={activeDuring}
input={<OutlinedInput label="Contact Type" />}
renderValue={selected => (selected as MatchModeType[]).join(", ")}
renderValue={selected => selected.join(", ")}
multiple
>
{MATCH_MODE_OPTIONS.map(opt => (
Expand Down Expand Up @@ -119,4 +124,4 @@ const ZoneConfigInterface: React.FC<ZoneConfigProps> = ({ selectedField, selecte
)
}

export default ZoneConfigInterface
export default ProtectedZoneConfigInterface
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TextField } from "@mui/material"
import { useState } from "react"
import { useState, useCallback } from "react"
import Checkbox from "@/ui/components/Checkbox"
import type MirabufSceneObject from "@/mirabuf/MirabufSceneObject"
import type { ScoringZonePreferences } from "@/systems/preferences/PreferenceTypes"
Expand All @@ -22,35 +22,38 @@ interface ZoneConfigProps {
saveAllZones: () => void
}

const ZoneConfigInterface: React.FC<ZoneConfigProps> = ({ selectedField, selectedZone, saveAllZones }) => {
const ScoringZoneConfigInterface: React.FC<ZoneConfigProps> = ({ selectedField, selectedZone, saveAllZones }) => {
const [points, setPoints] = useState<number>(selectedZone.points)
const [persistent, setPersistent] = useState<boolean>(selectedZone.persistentPoints)

const applyExtrasOnSave = useCallback((zone: ScoringZonePreferences) => {
zone.points = points
zone.persistentPoints = persistent
}, [points, persistent])

const removeZoneObject = useCallback((field: MirabufSceneObject, zone: ScoringZonePreferences) => {
field.removeScoringZoneObject(zone)
}, [])

return (
<ZoneConfigBase
selectedField={selectedField}
selectedZone={selectedZone}
attachAndPersistZone={attachAndPersistZone}
applyExtrasOnSave={zone => {
zone.points = zone.points ?? selectedZone.points
zone.destroyGamepiece = selectedZone.destroyGamepiece
zone.persistentPoints = persistent
}}
removeZoneObject={(field, zone) => field.removeScoringZoneObject(zone)}
applyExtrasOnSave={applyExtrasOnSave}
removeZoneObject={removeZoneObject}
saveAllZones={saveAllZones}
>
<TextField
inputProps={{ type: "number" }}
label="Points"
placeholder="Zone points"
defaultValue={selectedZone.points}
onChange={v => {
const parsed = parseInt(v.target.value)
selectedZone.points = Number.isNaN(parsed) ? 1 : parsed
}}
onChange={v => setPoints(parseInt(v.target.value) || 1)}
/>
<Checkbox label="Persistent Points" checked={persistent} onClick={checked => setPersistent(checked)} />
</ZoneConfigBase>
)
}

export default ZoneConfigInterface
export default ScoringZoneConfigInterface
Original file line number Diff line number Diff line change
Expand Up @@ -178,32 +178,32 @@ export default function ZoneConfigBase<TZone extends BaseZonePreferences>(props:
return new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), getAllianceMaterial(selectedZone.alliance, materials))
}, [selectedZone, selectedZone?.alliance, materials])

const gizmoComponent = useMemo(() => {
if (selectedField && selectedZone) {
const postGizmoCreation = (gizmo: GizmoSceneObject) => {
const material = (gizmo.obj as THREE.Mesh).material as THREE.Material
material.depthTest = false
const postGizmoCreation = useCallback((gizmo: GizmoSceneObject) => {
const material = (gizmo.obj as THREE.Mesh).material as THREE.Material
material.depthTest = false

const deltaTransformation = convertArrayToThreeMatrix4(selectedZone.deltaTransformation)
let nodeBodyId = selectedField.mechanism.nodeToBody.get(
selectedZone.parentNode ?? selectedField.rootNodeId
)
if (!nodeBodyId) {
nodeBodyId = selectedField.mechanism.nodeToBody.get(selectedField.rootNodeId)!
}
const deltaTransformation = convertArrayToThreeMatrix4(selectedZone.deltaTransformation)
let nodeBodyId = selectedField.mechanism.nodeToBody.get(
selectedZone.parentNode ?? selectedField.rootNodeId
)
if (!nodeBodyId) {
nodeBodyId = selectedField.mechanism.nodeToBody.get(selectedField.rootNodeId)!
}

const fieldTransformation = convertJoltMat44ToThreeMatrix4(
World.physicsSystem.getBody(nodeBodyId).GetWorldTransform()
)
const props = deltaFieldTransformsPhysicalProp(deltaTransformation, fieldTransformation)
const fieldTransformation = convertJoltMat44ToThreeMatrix4(
World.physicsSystem.getBody(nodeBodyId).GetWorldTransform()
)
const props = deltaFieldTransformsPhysicalProp(deltaTransformation, fieldTransformation)

gizmo.obj.position.set(props.translation.x, props.translation.y, props.translation.z)
gizmo.obj.rotation.setFromQuaternion(props.rotation)
gizmo.obj.scale.set(props.scale.x, props.scale.y, props.scale.z)
gizmo.obj.position.set(props.translation.x, props.translation.y, props.translation.z)
gizmo.obj.rotation.setFromQuaternion(props.rotation)
gizmo.obj.scale.set(props.scale.x, props.scale.y, props.scale.z)

removeZoneObject(selectedField, selectedZone)
}
removeZoneObject(selectedField, selectedZone)
}, [selectedField, selectedZone, removeZoneObject])

const gizmoComponent = useMemo(() => {
if (selectedField && selectedZone) {
return (
<TransformGizmoControl
key="zone-transform-gizmo"
Expand All @@ -218,7 +218,7 @@ export default function ZoneConfigBase<TZone extends BaseZonePreferences>(props:
gizmoRef.current = undefined
return <></>
}
}, [selectedField, selectedZone, defaultGizmoMesh, removeZoneObject])
}, [selectedField, selectedZone, defaultGizmoMesh, postGizmoCreation])

const trySetSelectedNode = useCallback(
(body: Jolt.BodyID) => {
Expand Down
Loading