File size: 8,159 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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
if FirstLoad then
	CCMT = true
	C_CCMT = true
	C_CMT_Async = false
end
MapVar("CMT_trigger_target_pairs", false)

function SetCCMT(val)
	if CCMT == val then
		return
	end
	
	if val then
		CMT_trigger_target_pairs = false
	end
	
	SetC_CCMT(val)
	CCMT = val
	ReloadTriggerTargetPairs()
end

function OnMsg.GameEnterEditor()
	StopAllHiding("Editor")
end

function OnMsg.GameExitEditor()
	ResumeAllHiding("Editor")
end

function CObject:SetShadowOnly(bSet)
	if g_CMTPaused then return end
	CMT(self, bSet)
end

function CObject:SetShadowOnlyImmediate(bSet)
	if bSet then
		self:SetHierarchyGameFlags(const.gofSolidShadow)
	else
		self:ClearHierarchyGameFlags(const.gofSolidShadow)
	end
	self:SetOpacity(bSet and 0 or 100)
end

function Decal:SetShadowOnlyImmediate(bSet)
	if bSet then
		self:SetHierarchyGameFlags(const.gofSolidShadow)
	else
		self:ClearHierarchyGameFlags(const.gofSolidShadow)
	end
end

function OnMsg.ChangeMap()
	CMT_SetPause(true, "ChangeMap")
	C_CMT_Reset()
end

function OnMsg.ChangeMapDone()
	ReloadTriggerTargetPairs()
	CMT_SetPause(false, "ChangeMap")
end

function OnMsg.GameExitEditor()
	ReloadTriggerTargetPairs()
end

function ReloadTriggerTargetPairs()
	if GetMap() == "" then
		return
	end
	if CCMT then
		--CCMTMode
		--0 dist check (UseDistCheckForNonTops)
		--1 collision check (UseCollisionForNonTops)
		ReloadCMTTargets((EngineOptions.ObjectDetail == "Low") and 0 or 1)
	else
		local border = GetBorderAreaLimits()
		CMT_trigger_target_pairs = {}
		for _, col in pairs(CollectionsByName) do
			if not IsCollectionLinkedToRooms(col) then
				local objs = MapGet("map", "collection", col.Index, true)
				if col.HideFromCamera then
					CMT_trigger_target_pairs[col] = objs
				elseif not col.DontHideFromCamera then
					local ht
					for _, o in ipairs(objs) do
						if IsKindOf(o, "HideTop") and o:GetGameFlags(const.gofOnRoof) == 0 and (not border or border:Point2DInside(o)) then
							ht = ht or {}
							table.insert(ht, o)
						end
					end
					if ht then
						CMT_trigger_target_pairs[col] = ht
					end
				end
			elseif col.HideFromCamera then
				print("Collection " .. col.Name .. " with index " .. tostring(col.Index) .. " is marked as HideFromCamera but is also linked to rooms, HideFromCamera is ignored!")
			end
		end
		MapForEach("map", "HideTop", function(o)
			local col = o:GetCollection()
			if col or o:GetGameFlags(const.gofOnRoof) ~= 0 then
				return
			end
			if not o.Top then return end
			CMT_trigger_target_pairs[o] = true
		end)
	end
end

local sleep_time = CMT_OpacitySleep*4
MapRealTimeRepeat("CMT_Trigger_Thread", 0, function()
	assert(sleep_time % CMT_OpacitySleep == 0)
	Sleep(sleep_time)
	-- local startTs = GetPreciseTicks(1000)
	if g_CMTPaused then 
		return 
	end
	local camera_pos, lookAt = cameraTac.GetZoomedPosLookAt()
	local hiding_pt = camera_pos + (lookAt - camera_pos)/2

	if CCMT then
		if C_CMT_Async then
			AsyncC_CMT_Thread_Func(SelectedObj)
		else
			async.AsyncC_CMT_Thread_Func(nil, SelectedObj)
		end
	else
		local hide_collections = CMT_GetCollectionsToHide()
		for trigger, objs in next, CMT_trigger_target_pairs do
			trigger:HandleCMTTrigger(camera_pos, lookAt, hiding_pt, objs, hide_collections)
		end
	end
	-- print("CCMT", CCMT, "CMT_Trigger_Thread time", GetPreciseTicks(1000) - startTs)
end)

local col_mask_any = 2^32-1
local col_mask_all = 0
local cam_pos, lookat
function CMT_GetCollectionsToHide()
	local collided = CMTCollisionDbg and {}
	if CMTCollisionDbg then
		local c, l = GetCamera()
		cam_pos = cam_pos or c
		lookat = lookat or l
		if c ~= cam_pos or l ~= lookat then
			cam_pos = c
			lookat = l
		end
	end
	
	local collections = {}
	local ptCamera, ptCameraLookAt = GetCamera()
	local bbox = box(-2000, -2000, -2000, 2000, 2000, 2000)
	collision.Collide(bbox + ptCamera, ptCameraLookAt - ptCamera, 0, col_mask_all, col_mask_any,
	function(o)
		if not IsValid(o) then return end --happens when editing rooms in editor
		if IsKindOf(o, "HideTop") then return end
		local col = o:GetRootCollection()
		if not col or not col.HideFromCamera or collections[col.Index] then
			return
		end
		
		if CMTCollisionDbg then
			o:SetHierarchyGameFlags(const.gofEditorSelection)
			CMTCollisionDbgShown[o] = true
			collided[o] = true
		end
		
		collections[col.Index] = true
	end)
	
	if CMTCollisionDbg then
		for o, _ in pairs(CMTCollisionDbgShown) do
			if not collided[o] then
				o:ClearHierarchyGameFlags(const.gofEditorSelection)
				CMTCollisionDbgShown[o] = nil
			end
		end
	end
	return collections
end

function Collection:HandleCMTTrigger(camera_pos, lookAt, hiding_pt, objs_to_hide, hide_collections)
	local hide
	if hide_collections[self.Index] then
		hide = true
	else
		for _, obj in ipairs(objs_to_hide) do
			if IsKindOf(obj, "HideTop") and obj:TopHidingCondition(camera_pos, lookAt, hiding_pt) then
				hide = true
				break
			end
		end
	end
	for _, obj in ipairs(objs_to_hide) do
		obj:SetShadowOnly(hide)
	end
end

if FirstLoad then
	CMTCollisionDbg = false
end

MapVar("CMTCollisionDbgShown", {})

function ToggleCMTCollisionDbg()
	for o, _ in pairs(CMTCollisionDbgShown) do
		o:ClearHierarchyGameFlags(const.gofEditorSelection)
		CMTCollisionDbgShown[o] = nil
	end
	CMTCollisionDbg = not CMTCollisionDbg
end

local visualized_cube_count = 3
function VisualizeCMTCube()
	local ptCamera, ptCameraLookAt = GetCamera()
	local bbox = box(-2000, -2000, -2000, 2000, 2000, 2000)
	for i = 1, visualized_cube_count do
		DbgAddBox(bbox + (ptCamera + (ptCameraLookAt - ptCamera)*i/visualized_cube_count), const.clrRed)
	end
end

function IsContourObjectClassAndEntityCheck(obj)
	--used by CCMT
	if IsKindOf(obj, "Slab") then
		if obj.room then
			if obj.room:IsRoofOnly() then 
				return false 
			end
		end
		
		if IsKindOf(obj, "SlabWallObject") then
			if next(obj.decorations) then
				for _, plank in ipairs(obj.decorations) do
					plank:SetHierarchyGameFlags(const.gofContourInner)
				end
			end
			local s = obj.main_wall
			if IsKindOf(s, "RoofWallSlab") then
				return false --no contours for windows on roofs
			end
		end
		
		local entity = obj:GetEntity()
		return (not IsKindOfClasses(obj, "RoofSlab", "RoofWallSlab", "FloorSlab", "CeilingSlab", "RoofCornerWallSlab") and not entity:find("ence"))
	end
	
	return false
end

function IsContourObject(obj)
	if IsContourObjectClassAndEntityCheck(obj) then
		local flr = cameraTac.GetFloor() + 1
		if obj.floor > flr then return false end
		
		return true
	end
	if g_AdditionalContourObjects[obj] then 
		return true
	end
	
	return false
end

------------------------------------------------------------------------------------
--CMTPlane
------------------------------------------------------------------------------------
local mask = const.CMTPlaneFlags
DefineClass.CMTPlane = {
	__parents = { "CObject", "EditorVisibleObject" },
	entity = "CMTPlane",
}

function SetupCMTPlaneCollections(map)
	if map == "" then return end
	local cols = {}
	local collectionlessPlanes = {}
	local allPlanes = {}
	MapForEach("map", "CMTPlane", function(o, cols, allPlanes, collectionlessPlanes)
		allPlanes[o] = true
		local id = o:GetCollectionIndex()
		if id and id ~= 0 then
			cols[id] = true
		else
			table.insert(collectionlessPlanes, o)
		end
	end, cols, allPlanes, collectionlessPlanes)
	
	if next(cols) then
		MapForEach("map", "CObject", function(o, cols, allPlanes)
			if allPlanes[o] then
				collision.SetAllowedMask(o, const.cmSeenByCMT)
				return 
			end
			
			local id = o:GetCollectionIndex()
			if cols[id] then
				local m = collision.GetAllowedMask(o)
				m = m & ~mask
				collision.SetAllowedMask(o, m)
			end
		end, cols, allPlanes)
	end
	
	if #collectionlessPlanes > 0 then
		print("Found " .. #collectionlessPlanes .. " CMTPlane(s) without collections!")
		--should probably kill those?
	end
end

OnMsg.GameExitEditor = SetupCMTPlaneCollections
OnMsg.ChangeMapDone = SetupCMTPlaneCollections

function ToggleVisibilitySystems(reason)
	local turnOn = g_CMTPaused
	if not turnOn then
		StopWallInvisibilityThread()
	end
	CMT_SetPause(not turnOn, reason or "BecauseReasons")
	C_CCMT_ShowAllAndReset()
	if turnOn then
		StartWallInvisibilityThreadWithChecks()
	end
end