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();
}