File size: 7,864 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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
if FirstLoad then
	GedObjectEditor = false
end

function GedInvokedMapEditorUndo() XEditorUndo:UndoRedo("undo") end
function GedInvokedMapEditorRedo() XEditorUndo:UndoRedo("redo") end

local last_selection_idx = 1
function GedOpViewGameObject(socket, obj)
	local is_obj_starting_table = type(obj) == "table" and IsValid(obj[1])
	if IsKindOf(obj, "GedMultiSelectAdapter") or is_obj_starting_table then
		local objs = is_obj_starting_table and obj or obj.__objects
		if #objs == 0 then return end
		if #objs < last_selection_idx then
			last_selection_idx = 1
		end
		ViewObject(objs[last_selection_idx])
		last_selection_idx = last_selection_idx + 1
	else
		ViewObject(obj)
	end
end

local function GetSelectionTable(socket, obj, allow_root)
	if (type(obj) == "table" and obj[1] and IsValid(obj[1])) then
		return obj
	elseif IsKindOf(obj, "GedMultiSelectAdapter") then
		return obj.__objects
	elseif obj == socket:ResolveObj("root") then
		return allow_root and obj or {}
	else
		return {obj}
	end
end

function GedOpConvertToTemplate(socket, obj)
	local objs = GetSelectionTable(socket, obj)
	if #objs > 0 then
		Template.TurnObjectsIntoTemplates(objs)
	end
end

function GedOpConvertToObject(socket, obj)
	local objs = GetSelectionTable(socket, obj)
	if #objs > 0 then
		Template.TurnTemplatesIntoObjects(objs)
	end
end

local shown_spots = {}
function GedOpToggleSpotVisiblity(socket, obj)
	local objs = GetSelectionTable(socket, obj)
	if #objs == 0 then return nil end
	return ToggleSpotVisibility(objs)
end
	
function ToggleSpotVisibility(objs)
	if not shown_spots[objs[1]] then
		for _, obj in ipairs(objs) do
			if IsValid(obj) then
				obj:ShowSpots()
				shown_spots[obj] = true
			end
		end
	else
		for _, obj in ipairs(objs) do
			if IsValid(obj) then
				obj:HideSpots()
				shown_spots[obj] = nil
			end
		end
	end
end

function ToggleSurfaceVisibility(objs)
	if not ObjToShownSurfaces[objs[1]] then
		for _, obj in ipairs(objs) do
			if IsValid(obj) then
				obj:ShowSurfaces()
			end
		end
	else
		for _, obj in ipairs(objs) do
			if IsValid(obj) then
				obj:HideSurfaces()
			end
		end
	end
end

function GedOpSetSingleSel(socket, obj)
	if IsEditorActive() then
		editor.SetSel{ obj }
	else
		SelectObj(obj)
	end
end
	
function GedOpDisplaySpotsWithFilter(socket, obj)
	if not obj then return end
	
	-- List spots by name & autoattach
	local spots = {}
	if obj["HasEntity"] and obj:HasEntity() then
		local start_id, end_id = obj:GetAllSpots(obj:GetState())
		for i = start_id, end_id do
			local spot_name = GetSpotNameByType(obj:GetSpotsType(i))
			local annotation = obj:GetSpotAnnotation(i) or ""
			local attach_class = annotation:match(".*,(.*),.*")
			spots[spot_name .. (attach_class and ":"..attach_class or "")] = true
		end
	end
	
	-- Construct combo box items
	local items = table.keys(spots)
	if #items > 0 then
		table.sort(items)
		local spot_name = socket:WaitUserInput("Select Spots to Show", items[1], items)
		obj:HideSpots()
		if spot_name then
			obj:ShowSpots(unpack_params(spot_name:split(":")))
			shown_spots[obj] = true
		end
	else
		socket:ShowMessage("Information", "No spots to show for this object.")
	end
end

function GedOpRemoveDuplicated(socket, obj)
	local obj_list = GetSelectionTable(socket, obj, "allow_root")
	local selection = editor.GetSel()
	editor.ClearSel()
	DeleteDuplicates(obj_list)
	table.validate(selection)
	editor.AddToSel(selection)
end

function GedOpDeleteObject(socket, obj)
	local selection = editor.GetSel()
	editor.ClearSel()
	DoneObjects(GetSelectionTable(socket, obj))
	table.validate(selection)
	editor.AddToSel(selection)
end

function GedOpOpenEntityEditor(socket, obj)
	local objs = GetSelectionTable(socket, obj, "allow_root")
	if #objs > 0 then
		CreateEntityViewer(objs[1])
	end
end

function GedOpenAutoattachEditorButton(root, obj, prop_id, ged)
	if not root or not obj then return end
	OpenAutoattachEditor(root, true)
end

function GedOpRemoveUnselected(socket, obj)
	local objs = GetSelectionTable(socket, obj)
	editor.ClearSel()
	editor.AddToSel(objs)
end

local function UpdateAnimationTimeFlags(oldsel, newsel)
	if IsEditorActive() then
		if oldsel then
			for _, o in ipairs(oldsel) do
				if IsValid(o) then 
					if IsKindOf(o, "ParSystem") then
						if o:ShouldBeGameTime() then
							ObjectAnimToGameTime(o)
						end
					elseif GetClassGameFlags(o.class, const.gofRealTimeAnim) == 0 then
						ObjectAnimToGameTime(o)
					end
				end
			end
		end
		if newsel then
			for _, o in ipairs(newsel) do
				o:SetRealtimeAnim(true)
			end
		end
	end
end

local function UpdateForcedLODs(oldsel, newsel)
	if IsEditorActive() then
		if oldsel then
			for _, o in ipairs(oldsel) do
				if IsValid(o) then
					o:RestoreForcedLODState()
					ObjModified(o)
				end
			end
		end
		
		if newsel then
			for _, o in ipairs(newsel) do
				o:CacheForcedLODState()
				if o:GetForcedLODMin() then
					o:SetForcedLOD(Max(o:GetLODsCount(), 1) - 1)
				end
				ObjModified(o)
			end
		end
	end
end

local function EditorFilterObjList(objects)
	if not EditorSettings:GetLimitObjectEditorItems() then
		return objects
	end

	-- show no more than 500 objects in the Object Editor (otherwise the performance is abysmal)
	local i, n, ret = 1, 1, {}
	while i <= #objects and n <= 500 do
		local obj = objects[i]
		if IsValid(obj) and not IsKindOf(obj, "PropertyHelper") then
			ret[n] = obj
			n = n + 1
		end
		i = i + 1
	end
	return ret
end

local obj_modified_list = {}
local obj_modified_thread = false
local obj_rebind_thread = false

local function mark_modified(obj)
	obj_modified_list[obj] = true
	for _, attach in ipairs(obj:GetAttaches() or empty_table) do
		mark_modified(attach)
	end
end

function OnMsg.EditorObjectOperation(op_finished, obj_list)
	if GedObjectEditor and op_finished then
		for _, obj in ipairs(obj_list) do
			mark_modified(obj)
		end
		obj_modified_thread = obj_modified_thread or CreateRealTimeThread(function()
			Sleep(250)
			obj_modified_thread = false
			for obj in pairs(obj_modified_list) do
				ObjModified(obj)
			end
			obj_modified_list = {}
		end)
	end
end

function OnMsg.EditorSelectionChanged(objects)
	if GedObjectEditor and not GedObjectEditor.objects_locked then
		-- no need to call ObjModified if we will rebind everything
		DeleteThread(obj_modified_thread)
		obj_modified_thread = false
		obj_modified_list = {}
		
		-- add 100 ms delay, restart everything if selection is changed again (as in the drag to select case)
		DeleteThread(obj_rebind_thread)
		obj_rebind_thread = CreateRealTimeThread(function()
			Sleep(100)
			if GedObjectEditor then
				local root = GedObjectEditor:ResolveObj("root")
				if objects and #objects == 1 and IsKindOf(objects[1], "PropertyHelper") then
					objects = root -- keep all objects, but filter invalids
				end
				objects = EditorFilterObjList(objects)

				UpdateAnimationTimeFlags(root, objects)
				UpdateForcedLODs(root, objects)
				GedObjectEditor:UnbindObjs("root")
				GedObjectEditor:BindObj("root", objects)
				GedObjectEditor:SelectAll("root")
			end
		end)
	end
end

function OpenGedGameObjectEditor(objects, locked_objs)
	CreateRealTimeThread(function(objects)
		if not GedObjectEditor then
			objects = EditorFilterObjList(objects)

			UpdateAnimationTimeFlags(nil, objects)
			UpdateForcedLODs(nil, objects)
			GedObjectEditor = OpenGedApp("GedObjectEditor", objects, { WarningsUpdateRoot = "root" }) or false
			GedObjectEditor:SelectAll("root")
		else
			GedObjectEditor:Call("rfnApp", "Activate")
		end
		rawset(GedObjectEditor, "objects_locked", locked_objs or false)
	end, objects)
end

function OnMsg.GedClosing(ged_id)
	if GedObjectEditor and GedObjectEditor.ged_id == ged_id then
		local objects = GedObjectEditor:ResolveObj("root")
		table.validate(objects)
		UpdateAnimationTimeFlags(objects, nil)
		UpdateForcedLODs(objects, nil)
		GedObjectEditor = false
	end
end