diff --git a/images/adjust.svg b/images/adjust.svg
new file mode 100644
index 00000000..64ae74f4
--- /dev/null
+++ b/images/adjust.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/close-box-outline.svg b/images/close-box-outline.svg
new file mode 100644
index 00000000..e052af7a
--- /dev/null
+++ b/images/close-box-outline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/list-box-outline.svg b/images/list-box-outline.svg
new file mode 100644
index 00000000..2dcd8c9c
--- /dev/null
+++ b/images/list-box-outline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/pencil-off-outline.svg b/images/pencil-off-outline.svg
new file mode 100644
index 00000000..d42b413f
--- /dev/null
+++ b/images/pencil-off-outline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/pencil-outline.svg b/images/pencil-outline.svg
new file mode 100644
index 00000000..c184d5ef
--- /dev/null
+++ b/images/pencil-outline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/ray-start-arrow.svg b/images/ray-start-arrow.svg
new file mode 100644
index 00000000..26170e89
--- /dev/null
+++ b/images/ray-start-arrow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/vector-square-close.svg b/images/vector-square-close.svg
new file mode 100644
index 00000000..77d8cdd0
--- /dev/null
+++ b/images/vector-square-close.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/layout/hud/tab-menu.xml b/layout/hud/tab-menu.xml
index de26117b..7a2eb3aa 100644
--- a/layout/hud/tab-menu.xml
+++ b/layout/hud/tab-menu.xml
@@ -52,13 +52,19 @@
diff --git a/layout/modals/context-menus/zoning-df-flags.xml b/layout/modals/context-menus/zoning-df-flags.xml
new file mode 100644
index 00000000..e47fb670
--- /dev/null
+++ b/layout/modals/context-menus/zoning-df-flags.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/layout/pages/zoning/zoning.xml b/layout/pages/zoning/zoning.xml
index 9fa54cee..6bfdbf3d 100644
--- a/layout/pages/zoning/zoning.xml
+++ b/layout/pages/zoning/zoning.xml
@@ -1,3 +1,243 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/hud/tab-menu.js b/scripts/hud/tab-menu.js
index a513c205..bb41b164 100644
--- a/scripts/hud/tab-menu.js
+++ b/scripts/hud/tab-menu.js
@@ -9,6 +9,12 @@ class HudTabMenu {
leaderboardsContainer: $('#LeaderboardsContainer'),
/** @type {Panel} @static */
endOfRunContainer: $('#EndOfRunContainer'),
+ /** @type {Panel} @static */
+ zoningContainer: $('#ZoningContainer'),
+ /** @type {Panel} @static */
+ zoningOpenButton: $('#ZoningOpen'),
+ /** @type {Panel} @static */
+ zoningCloseButton: $('#ZoningClose'),
/** @type {Image} @static */
gamemodeImage: $('#HudTabMenuGamemodeImage'),
/** @type {Panel} @static */
@@ -20,16 +26,40 @@ class HudTabMenu {
$.RegisterForUnhandledEvent('HudTabMenu_ForceClose', this.close.bind(this));
$.RegisterForUnhandledEvent('EndOfRun_Show', this.showEndOfRun.bind(this));
$.RegisterForUnhandledEvent('EndOfRun_Hide', this.hideEndOfRun.bind(this));
+ $.RegisterForUnhandledEvent('ZoneMenu_Show', this.showZoneMenu.bind(this));
+ $.RegisterForUnhandledEvent('ZoneMenu_Hide', this.hideZoneMenu.bind(this));
}
static showEndOfRun(_showReason) {
this.panels.leaderboardsContainer.AddClass('hud-tab-menu__leaderboards--hidden');
this.panels.endOfRunContainer.RemoveClass('hud-tab-menu__endofrun--hidden');
+ this.panels.zoningContainer.AddClass('hud-tab-menu__zoning--hidden');
}
static hideEndOfRun() {
this.panels.leaderboardsContainer.RemoveClass('hud-tab-menu__leaderboards--hidden');
this.panels.endOfRunContainer.AddClass('hud-tab-menu__endofrun--hidden');
+ this.panels.zoningContainer.AddClass('hud-tab-menu__zoning--hidden');
+ }
+
+ static showZoneMenu() {
+ this.panels.tabMenu.AddClass('hud-tab-menu--offset');
+ this.panels.leaderboardsContainer.AddClass('hud-tab-menu__leaderboards--hidden');
+ this.panels.endOfRunContainer.AddClass('hud-tab-menu__endofrun--hidden');
+
+ this.panels.zoningContainer.RemoveClass('hud-tab-menu__zoning--hidden');
+ this.panels.zoningOpenButton.AddClass('hud-tab-menu__zoning--hidden');
+ this.panels.zoningCloseButton.RemoveClass('hud-tab-menu__zoning--hidden');
+ }
+
+ static hideZoneMenu() {
+ this.panels.tabMenu.RemoveClass('hud-tab-menu--offset');
+ this.panels.leaderboardsContainer.RemoveClass('hud-tab-menu__leaderboards--hidden');
+ this.panels.endOfRunContainer.AddClass('hud-tab-menu__endofrun--hidden');
+
+ this.panels.zoningContainer.AddClass('hud-tab-menu__zoning--hidden');
+ this.panels.zoningOpenButton.RemoveClass('hud-tab-menu__zoning--hidden');
+ this.panels.zoningCloseButton.AddClass('hud-tab-menu__zoning--hidden');
}
static setMapData(isOfficial) {
diff --git a/scripts/pages/zoning/zoning.ts b/scripts/pages/zoning/zoning.ts
new file mode 100644
index 00000000..ff0e3da8
--- /dev/null
+++ b/scripts/pages/zoning/zoning.ts
@@ -0,0 +1,999 @@
+/***************************************************************************************** */
+
+interface Region extends JsonObject {
+ points: number[][];
+ bottom: number;
+ height: number;
+ teleDestTargetname: string; // mutually exclusive to other two teleport fields
+ teleDestPos: number[]; // TODO: This below are required if region is part of a volume used by stafe or major checkpoint zone
+ teleDestYaw: number; // See convo in mom red 25/09/23 02:00 GMT
+ safeHeight: number;
+}
+
+interface Zone extends JsonObject {
+ regions: Region[];
+ filtername: string;
+}
+
+interface Segment extends JsonObject {
+ limitStartGroundSpeed: boolean;
+ checkpointsRequired: boolean;
+ checkpointsOrdered: boolean;
+ checkpoints: Zone[];
+ cancel: Zone[];
+ name: string;
+}
+
+interface TrackZones extends JsonObject {
+ segments: Segment[];
+ end: Zone;
+}
+
+interface MainTrack extends JsonObject {
+ zones: TrackZones;
+ stagesEndAtStageStarts: boolean;
+}
+
+interface BonusTrack extends JsonObject {
+ zones: TrackZones;
+ defragFlags: number;
+}
+
+interface MapTracks extends JsonObject {
+ main: MainTrack;
+ bonuses: BonusTrack[];
+}
+
+interface ZoneDef extends JsonObject {
+ formatVersion: number;
+ dataTimestamp: number;
+ maxVelocity: number;
+ tracks: MapTracks;
+}
+
+interface EntityList {
+ filter: string[];
+ teleport: string[];
+}
+/**************************************************************************************************************/
+
+/**
+ * Zoning UI logic
+ */
+
+const TracklistSnippet = {
+ TRACK: 'tracklist-track',
+ SEGMENT: 'tracklist-segment',
+ CHECKPOINT: 'tracklist-checkpoint'
+};
+
+const DefragFlags = {
+ HASTE: 1 << 0,
+ SLICK: 1 << 1,
+ DAMAGEBOOST: 1 << 2,
+ ROCKETS: 1 << 3,
+ PLASMA: 1 << 4,
+ BFG: 1 << 5
+};
+
+// future: get this from c++
+const FORMAT_VERSION = 1;
+
+const PickType = {
+ none: '""',
+ corner: 'corner',
+ bottom: 'bottom',
+ height: 'height',
+ safeHeight: 'safe height',
+ teleDestPos: 'teleDestPos',
+ teleDestYaw: 'teleDestYaw'
+};
+
+class ZoneMenu {
+ static panels = {
+ zoningMenu: $.GetContextPanel(),
+ trackList: $('#TrackList')!,
+ propertiesTrack: $('#TrackProperties')!,
+ maxVelocity: $('#MaxVelocity')!,
+ defragFlags: $('#DefragFlags')!,
+ stagesEndAtStageStarts: $('#StagesEndAtStageStarts')!.FindChild('CheckBox') as ToggleButton,
+ propertiesSegment: $('#SegmentProperties')!,
+ limitGroundSpeed: $('#LimitGroundSpeed')!.FindChild('CheckBox') as ToggleButton,
+ checkpointsRequired: $('#CheckpointsRequired')!.FindChild('CheckBox') as ToggleButton,
+ checkpointsOrdered: $('#CheckpointsOrdered')!.FindChild('CheckBox') as ToggleButton,
+ segmentName: $('#SegmentName')!,
+ propertiesZone: $('#ZoneProperties')!,
+ filterSelect: $('#FilterSelect')!,
+ volumeSelect: $('#VolumeSelect')!,
+ regionSelect: $('#RegionSelect')!,
+ pointsSection: $('#PointsSection')!,
+ pointsList: $('#PointsList')!,
+ propertiesSection: $('#PropertiesSection')!,
+ teleportSection: $('#TeleportSection')!,
+ regionBottom: $('#RegionBottom')!,
+ regionHeight: $('#RegionHeight')!,
+ regionSafeHeight: $('#RegionSafeHeight')!,
+ regionTPDest: $('#RegionTPDest')!,
+ regionTPPos: {
+ x: $('#TeleX')!,
+ y: $('#TeleY')!,
+ z: $('#TeleZ')!
+ },
+ regionTPPosPick: $