File size: 10,310 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
----- Local coordinate system setting for MoveGizmo, RotateGizmo

if FirstLoad then
	LocalStorage.LocalCS = LocalStorage.LocalCS or {}
end

function GetLocalCS()
	local dialog = GetDialog("XSelectObjectsTool") or GetDialog("XPlaceObjectTool")
	local helper = dialog and g_Classes[dialog.helper_class]
	if helper and helper.HasLocalCSSetting then return LocalStorage.LocalCS[helper.class] end
end

function SetLocalCS(localCS)
	local dialog = GetDialog("XSelectObjectsTool") or GetDialog("XPlaceObjectTool")
	local helper = dialog and g_Classes[dialog.helper_class]
	if helper and helper.HasLocalCSSetting then
		LocalStorage.LocalCS[helper.class] = localCS
		dialog:UpdatePlacementHelper()
		SaveLocalStorage()
	end
end


----- Snap modes

local snap_modes = {
	{ id = "20cm/15°",    description = "Fine snapping", xy = 20*guic, angle = 15 * 60 },
	{ id = "1m/90°",      description = "Meter snapping", xy = guim, angle = 90 * 60 },
	{ id = "Passability", description = "Snap to passability grid", xy = const.PassTileSize },
	{ id = "Custom",      description = "Custom snapping",  },
}
if const.SlabSizeX then
	table.insert(snap_modes, { id = "Voxels", description = "Snap to voxels/slabs", xy = const.SlabSizeX, z = const.SlabSizeZ, angle = 90 * 60, center = true })
end
if const.HexWidth then
	table.insert(snap_modes, { id = "HexGrid", description = "Snap to hex grid", angle = 60 * 60 })
end
if const.BuildLevelHeight then
	table.insert(snap_modes, { id = "BuildLevel", description = "Snap to build levels", z = const.BuildLevelHeight })
end
for i, item in ipairs(snap_modes) do
	item.shortcut = i == 1 and "Alt-~" or "Alt-" .. string.char(48 + i - 1)
end


----- XEditorSettings (stores global editor settings in LocalStorage, see XEditorToolSettings)
-- 
-- Edited via Ged with shortcut Ctrl-F3 in editor mode

function OnMsg.Autorun()
	EditorSettings = XEditorSettings:new()
	EditorSettingsGedThread = false
end

local rightclick_items = {
	{ id = "ContextMenu", name = "Open context menu" },
	{ id = "ObjectProps", name = "Open object properties" },
}

DefineClass.XEditorSettings = {
	__parents = { "XEditorToolSettings" },
	properties = {
		persisted_setting = true, auto_select_all = true,
		{ category = "General", id = "AutosaveTime", name = "Autosave time (min)", editor = "number", default = 0, min = 0, max = 30, slider = true, help = "Set to zero to disable map autosaves.",
			no_edit = config.ModdingToolsInUserMode, -- auto-saving won't work for game original maps or map patches
		},
		{ category = "General", id = "ShowPlayArea", name = "Show play area", editor = "bool", default = true },
		{ category = "General", id = "CloudShadows", name = "Show cloud shadows", editor = "bool", default = false },
		{ category = "General", id = "TestParticlesOnChange", name = "Replay particles on edit", editor = "bool", default = false },
		{ category = "General", id = "TestParticlesOnChangeHelp", editor = "help", help = "(replay with Shift-E or Z, Alt-E toggles this)" },
		{ category = "UI", id = "AutoFocusMenuSearch",  editor = "bool", name = "Auto focus ~ menu search", default = true },
		{ category = "UI", id = "SmartSelection", name = "Smart selection", editor = "bool", default = true, help = "If no object is found at the precise position of the cursor, search for one by bounding box too." },
		{ category = "UI", id = "HighlightOnHover", name = "Highlight on hover", editor = "bool", default = true },
		{ category = "UI", id = "FilterHighlight", name = "Highlight on filter hover", editor = "bool", default = true },
		{ category = "UI", id = "RightClickOpens", name = "RightClick", editor = "combo", default = "ContextMenu", items = rightclick_items },
		{ category = "UI", id = "CtrlRightClickOpens", name = "Ctrl-RightClick", editor = "combo", default = "ObjectProps", items = rightclick_items, read_only = true, persisted_setting = false },
		{ category = "UI", id = "EditorToolbar", name = "Editor toolbar", editor = "bool", default = true },
		{ category = "UI", id = "DarkMode", name = "Dark mode", editor = "choice", default = "Follow system", items = { "Follow system", "Dark", "Light" } },
		{ category = "UI", id = "MessageBoxFont", name = "Message box font", editor = "choice", default = "Tahoma", items = { { name = "Tahoma (clasic)", value = "Tahoma" }, { name = "Consolas (monospace)", value = "Consolas" } },
			no_edit = config.ModdingToolsInUserMode,
		},
		{ category = "Ged (external editor windows)", id = "GedUIScale", name = "UI scale", editor = "number", min = 75, max = 200, slider = true, default = 100, step = 1 },
		{ category = "Ged (external editor windows)", id = "ColorPickerScale", name = "Color picker scale", editor = "number", min = 100, max = 200, slider = true, default = 100, step = 1 },
		{ category = "Ged (external editor windows)", id = "LimitObjectEditorItems", name = "Limit items in Object editor", editor = "bool", default = true, help = "Only the first 500 objects in the selection will be visible and editable in the Object editor for better performance." },
		{ category = "Gizmos", id = "GizmoThickness", name = "Thickness", editor = "number", min = 25, max = 100, slider = true, default = 75, step = 1 },
		{ category = "Gizmos", id = "GizmoOpacity", name = "Opacity", editor = "number", min = 32, max = 255, slider = true, default = 110, step = 1 },
		{ category = "Gizmos", id = "GizmoScale", name = "Scale", editor = "number", min = 25, max = 100, slider = true, default = 85, step = 1 },
		{ category = "Gizmos", id = "GizmoSensitivity", name = "Active area size", editor = "number", min = 50, max = 200, slider = true, default = 100, step = 1, help = "Adjusts the size of the area where you can grab a gizmo control element, e.g. axis." },
		{ category = "Gizmos", id = "GizmoRotateSnapping", name = "Snap angle in Rotate Gizmo", editor = "bool", default = true, },
		
		-- uneditable properties, used for storing data in LocalStorage only
		{ id = "SnapEnabled", editor = "bool", default = false, no_edit = true },
		{ id = "SnapMode",    editor = "choice", items = snap_modes, default = "", no_edit = true, },
		{ id = "SnapXY",      editor = "number", default = 0, no_edit = true, },
		{ id = "SnapZ",       editor = "number", default = 0, no_edit = true, },
		{ id = "SnapAngle",   editor = "number", default = 0, no_edit = true, },
	},
	ged = false,
	should_open = false,
}

function XEditorSettings:GetCtrlRightClickOpens()
	return self:GetRightClickOpens() == "ContextMenu" and "ObjectProps" or "ContextMenu"
end

function XEditorSettings:GetSnapModes()
	return snap_modes
end

function XEditorSettings:PosSnap(pos, by_slabs)
	local snap_mode = table.find_value(snap_modes, "id", self:GetSnapMode())
	
	if not by_slabs then
		if not self:GetSnapEnabled() or not snap_mode then
			return pos
		end
		if snap_mode.id == "Voxels" then
			return SnapToVoxel(pos + point(0, 0, const.SlabSizeZ / 2))
		end
		if snap_mode.id == "HexGrid" then
			return HexGetNearestCenter(pos)
		end
	end
	
	local center
	local x, y, z = pos:xyz()
	local sx, sy, sz = self:GetSnapXY(), self:GetSnapXY(), self:GetSnapZ()
	if by_slabs then
		sx, sy, sz = const.SlabSizeX, const.SlabSizeY, const.SlabSizeZ
	else
		center = snap_mode.center
	end
	if sx > 0 and sy > 0 then
		if center then x, y = x - sx / 2, y - sy / 2 end
		x = (x + sx / 2) / sx * sx
		y = (y + sy / 2) / sy * sy
		if center then x, y = x + sx / 2, y + sy / 2 end
	end
	if sz > 0 then
		z = z or terrain.GetHeight(pos)
		z = (z + sz / 2) / sz * sz
	end
	return point(x, y, z)
end

function XEditorSettings:AngleSnap(angle, by_slabs)
	local sa = by_slabs and 90 * 60 or self:GetSnapEnabled() and self:GetSnapAngle() or 0
	if sa ~= 0 then
		angle = (angle + sa / 2) / sa * sa
	end
	return angle
end

function XEditorSettings:OnEditorSetProperty(prop_id, old_value, ged)
	if prop_id == "AutosaveTime" then
		EditorAutosaveNextTime = now() + self:GetAutosaveTime() * 60 * 1000
		EditorCreateAutosaveThread()
	elseif prop_id == "CloudShadows" then
		hr.EnableCloudsShadow = self:GetCloudShadows() and 1 or 0
	elseif prop_id == "EditorToolbar" and GetDialog("XEditorToolbar") then
		GetDialog("XEditorToolbar"):SetVisible(self:GetEditorToolbar())
	elseif prop_id == "DarkMode" then
		for id, dlg in pairs(Dialogs) do 
			if IsKindOf(dlg, "XDarkModeAwareDialog") then 
				dlg:SetDarkMode(GetDarkModeSetting())
			end
		end
		for id, socket in pairs(GedConnections) do
			socket:Send("rfnApp", "SetDarkMode", GetDarkModeSetting())
		end
		ReloadShortcuts()
	elseif prop_id == "MessageBoxFont" then
		config.MessageBoxFont = self:GetProperty(prop_id)
	elseif prop_id == "SnapMode" then
		local mode = self:GetSnapMode()
		if mode ~= "Custom" and mode ~= "" then
			local mode = table.find_value(snap_modes, "id", mode)
			self:SetSnapXY(mode.xy)
			self:SetSnapZ(mode.z)
			self:SetSnapAngle(mode.angle)
		end
		self:SetSnapEnabled(true)
		local dialog = GetDialog("XSelectObjectsTool") or GetDialog("XPlaceObjectTool")
		if dialog then dialog:UpdatePlacementHelper() end
		XEditorUpdateToolbars()
	end
	Msg("EditorSettingChanged", prop_id, self:GetProperty(prop_id))
end

function XEditorSettings:OnShortcut(shortcut, source, ...)
	local dialog = GetDialog("XSelectObjectsTool") or GetDialog("XPlaceObjectTool")
	local helper = dialog and g_Classes[dialog.helper_class]
	if shortcut == "X" and helper and helper.HasSnapSetting then
		XEditorSettings:SetSnapEnabled(not XEditorSettings:GetSnapEnabled())
		XEditorUpdateToolbars()
		return "break"
	end
	for _, mode in ipairs(snap_modes) do
		if shortcut == mode.shortcut and helper and helper.HasSnapSetting then
			XEditorSettings:SetSnapMode(mode.id)
			XEditorSettings:OnEditorSetProperty("SnapMode")
			XEditorUpdateToolbars()
			return "break"
		end
	end
	return XEditorTool.OnShortcut(self, shortcut, source, ...)
end

function XEditorSettings:ToggleGedEditor()
	self.should_open = not self.should_open
	
	if not IsValidThread(EditorSettingsGedThread) then
		EditorSettingsGedThread = CreateRealTimeThread(function()
			while true do
				if not self.ged and self.should_open then
					self.ged = OpenGedApp("XEditorSettings", EditorSettings, nil, "XEditorSettings", true)
				elseif self.ged and not self.should_open then
					CloseGedApp(self.ged, "wait")
					self.ged = false
				end
				Sleep(100)
			end
		end)
	end
end