diff --git a/layout/pages/zoning/zoning.xml b/layout/pages/zoning/zoning.xml index 377cc1cf..fbbaffe3 100644 --- a/layout/pages/zoning/zoning.xml +++ b/layout/pages/zoning/zoning.xml @@ -147,15 +147,15 @@ - + - + - + diff --git a/scripts/pages/zoning/zoning.ts b/scripts/pages/zoning/zoning.ts index ed8cc1c8..7a67dc5d 100644 --- a/scripts/pages/zoning/zoning.ts +++ b/scripts/pages/zoning/zoning.ts @@ -89,6 +89,9 @@ const PickType = { teleDestYaw: 'teleDestYaw' }; +const FLT_MAX = 3.402823466e38; +const DEFAULT_HEIGHT = 160; + class ZoneMenu { static panels = { zoningMenu: $.GetContextPanel(), @@ -103,6 +106,7 @@ class ZoneMenu { checkpointsOrdered: $('#CheckpointsOrdered')!.FindChild('CheckBox') as ToggleButton, segmentName: $('#SegmentName')!, propertiesZone: $('#ZoneProperties')!, + propertyTabs: $('#PropertyTabs')!, filterSelect: $('#FilterSelect')!, volumeSelect: $('#VolumeSelect')!, regionSelect: $('#RegionSelect')!, @@ -130,7 +134,6 @@ class ZoneMenu { zone: null as Zone | null }; static mapZoneData: ZoneDef | null; - static backupZoneData: ZoneDef | null; static filternameList: string[] | null; static teleDestList: string[] | null; static pointPick: string; @@ -184,7 +187,7 @@ class ZoneMenu { this.teleDestList.unshift($.Localize('#Zoning_TPDest_None')!); this.populateDropdown(this.teleDestList, this.panels.regionTPDest, '', true); - this.showRegionMenu('Points'); + this.showRegionMenu('reset'); this.createTrackEntry(this.panels.trackList, this.mapZoneData.tracks.main, 'Main'); @@ -356,8 +359,8 @@ class ZoneMenu { static createRegion() { return { points: [] as number[][], - bottom: 0, - height: 0, + bottom: FLT_MAX, + height: DEFAULT_HEIGHT, teleDestTargetname: '' } as Region; } @@ -412,6 +415,7 @@ class ZoneMenu { this.populateDropdown(zone.regions, this.panels.regionSelect, 'Region', true); this.panels.regionSelect.SetSelectedIndex(0); this.populateRegionProperties(); + this.showRegionMenu(''); } static populateSegmentProperties() { @@ -439,6 +443,8 @@ class ZoneMenu { const filterIndex = this.panels.filterSelect.GetSelected()?.GetAttributeInt('value', 0); this.selectedZone.zone.filtername = filterIndex ? this.filternameList[filterIndex] : ''; + + this.updateZones(); } static populateRegionProperties() { @@ -469,6 +475,8 @@ class ZoneMenu { this.populateDropdown(this.selectedZone.zone.regions, this.panels.regionSelect, 'Region', true); this.panels.regionSelect.SetSelectedIndex(this.selectedZone.zone.regions.length - 1); this.populateRegionProperties(); + + this.updateZones(); } static deleteRegion() { @@ -480,19 +488,25 @@ class ZoneMenu { this.selectedZone.zone?.regions.splice(index, 1); this.panels.regionSelect.SetSelectedIndex(0); this.populateRegionProperties(); + + this.updateZones(); } static pickCorners() { if (!this.selectedZone || !this.selectedZone.zone) return; this.pointPick = PickType.corner; + const index = this.panels.regionSelect.GetSelected().GetAttributeInt('value', -1); + const region = this.selectedZone.zone.regions[index]; if (GameInterfaceAPI.GetSettingBool('mom_zone_two_click')) { - const index = this.panels.regionSelect.GetSelected().GetAttributeInt('value', -1); - this.selectedZone.zone.regions[index].points.length = 0; + region.points.length = 0; this.panels.pointsList.RemoveAndDeleteChildren(); } //@ts-expect-error function name this.panels.zoningMenu.startPointPick(true); + + //@ts-expect-error function name + this.panels.zoningMenu.setCornersFromRegion(region); } static addPointToList(i: number, point: number[]) { @@ -513,6 +527,8 @@ class ZoneMenu { const index = this.panels.regionSelect.GetSelected().GetAttributeInt('value', -1); this.selectedZone.zone?.regions[index].points.splice(n, 1); point.DeleteAsync(0); + + this.updateZones(); } static pickBottom() { @@ -528,6 +544,8 @@ class ZoneMenu { const region = this.selectedZone.zone.regions[regionIndex]; const bottom = Number.parseFloat(this.panels.regionBottom.text); region.bottom = Number.isNaN(bottom) ? 0 : bottom; + + this.updateZones(); } static pickHeight() { @@ -543,6 +561,8 @@ class ZoneMenu { const region = this.selectedZone.zone.regions[regionIndex]; const height = Number.parseFloat(this.panels.regionHeight.text); region.height = Number.isNaN(height) ? 0 : height; + + this.updateZones(); } static pickSafeHeight() { @@ -558,6 +578,8 @@ class ZoneMenu { const region = this.selectedZone.zone.regions[regionIndex]; const height = Number.parseFloat(this.panels.regionSafeHeight.text); region.safeHeight = Number.isNaN(height) ? 0 : height; + + this.updateZones(); } static pickTeleDestPos() { @@ -621,6 +643,8 @@ class ZoneMenu { this.panels.regionTPYaw.text = ''; this.setRegionTPDestTextEntriesActive(false); } + + this.updateZones(); } static setRegionTeleDestOrientation() { @@ -635,6 +659,8 @@ class ZoneMenu { region.teleDestPos = [Number.isNaN(x) ? 0 : x, Number.isNaN(y) ? 0 : y, Number.isNaN(z) ? 0 : z]; region.teleDestYaw = Number.isNaN(yaw) ? 0 : yaw; + + this.updateZones(); } static setRegionTPDestTextEntriesActive(enable: boolean) { @@ -657,6 +683,10 @@ class ZoneMenu { return; case PickType.corner: + if (point.z < region.bottom) { + region.bottom = point.z; + this.panels.regionBottom.text = point.z.toFixed(2); + } if (GameInterfaceAPI.GetSettingBool('mom_zone_two_click') && region.points.length === 1) { region.points.push([region.points[0][0], point.y]); this.addPointToList(region.points.length - 1, [region.points[0][0], point.y]); @@ -696,10 +726,14 @@ class ZoneMenu { this.panels.regionTPYaw.text = region.teleDestYaw.toFixed(2); break; } + + this.updateZones(); } static onPickCanceled() { this.pointPick = PickType.none; + + this.updateZones(); } static addBonus() { @@ -716,6 +750,8 @@ class ZoneMenu { bonus, `${$.Localize('#Zoning_Bonus')!} ${this.mapZoneData.tracks.bonuses.length}` ); + + this.updateZones(); } static addSegment() { @@ -745,6 +781,8 @@ class ZoneMenu { segment: newSegment, zone: newSegment.checkpoints[0] }); + + this.updateZones(); } static addCheckpoint() { @@ -773,6 +811,8 @@ class ZoneMenu { segment: this.selectedZone.segment, zone: newZone }); + + this.updateZones(); } static addEndZone() { @@ -805,6 +845,8 @@ class ZoneMenu { zone: endZone }); } + + this.updateZones(); } static addCancelZone() { @@ -837,6 +879,8 @@ class ZoneMenu { segment: this.selectedZone.segment, zone: newZone }); + + this.updateZones(); } static showAddMenu() { @@ -909,17 +953,23 @@ class ZoneMenu { //hack: this can be a little more surgical this.panels.trackList.RemoveAndDeleteChildren(); this.initMenu(); + + this.updateZones(); } static setMaxVelocity() { if (!this.mapZoneData) return; const velocity = Number.parseFloat(this.panels.maxVelocity.text); this.mapZoneData.maxVelocity = !Number.isNaN(velocity) && velocity > 0 ? velocity : 0; + + this.updateZones(); } static setStageEndAtStageStarts() { if (!this.isSelectionValid().track || !('stagesEndAtStageStarts' in this.selectedZone.track!)) return; this.selectedZone.track.stagesEndAtStageStarts = this.panels.stagesEndAtStageStarts.checked; + + this.updateZones(); } static showDefragFlagMenu() { @@ -975,21 +1025,29 @@ class ZoneMenu { const trackPanel = this.panels.trackList.GetChild(1 + bonusIndex)!; trackPanel.FindChildTraverse('CollapseButton')!.visible = false; trackPanel.FindChildTraverse('ChildContainer')!.RemoveAndDeleteChildren(); + + this.updateZones(); } static setLimitGroundSpeed() { if (!this.isSelectionValid().segment) return; this.selectedZone.segment!.limitStartGroundSpeed = this.panels.limitGroundSpeed.checked; + + this.updateZones(); } static setCheckpointsOrdered() { if (!this.isSelectionValid().segment) return; this.selectedZone.segment!.checkpointsOrdered = this.panels.checkpointsOrdered.checked; + + this.updateZones(); } static setCheckpointsRequired() { if (!this.isSelectionValid().segment) return; this.selectedZone.segment!.checkpointsRequired = this.panels.checkpointsRequired.checked; + + this.updateZones(); } static setSegmentName() { @@ -997,25 +1055,46 @@ class ZoneMenu { this.selectedZone.segment!.name = this.panels.segmentName.text; // feat: later // update segment name in trasklist tree + + this.updateZones(); } static showRegionMenu(menu: string) { + const pointsTab = this.panels.propertyTabs.GetChild(0) as RadioButton; + if (menu === 'reset') { + pointsTab.SetSelected(true); + } + menu = menu || pointsTab.GetSelectedButton().GetAttributeString('value', 'Points'); + this.panels.pointsSection.visible = menu === 'Points'; this.panels.propertiesSection.visible = menu === 'Properties'; this.panels.teleportSection.visible = menu === 'Teleport'; } + static updateZones() { + if (!this.mapZoneData) return; + + //future: validation here + + this.mapZoneData.dataTimestamp = Date.now(); + //@ts-expect-error API name not recognized + MomentumTimerAPI.SetActiveZoneDefs(this.mapZoneData); + } + static saveZones() { if (!this.mapZoneData) return; this.mapZoneData.dataTimestamp = Date.now(); //@ts-expect-error API name not recognized MomentumTimerAPI.SaveZoneDefs(this.mapZoneData); - // reload zones } static cancelEdit() { this.panels.trackList.RemoveAndDeleteChildren(); this.mapZoneData = null; + + //@ts-expect-error API name not recognized + MomentumTimerAPI.LoadZoneDefs(); + this.initMenu(); }