File size: 3,854 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
DefineClass.FloatingDummy = {
	__parents = {"Object", "InvisibleObject", "ComponentAnim", },
}

function FloatingDummy:GameInit()
	self:SetAnimPhase(1, self:Random(self:GetAnimDuration()))
end

DefineClass.FloatingDummyCollision = {
	__parents = {"Object" },
	flags = { efWalkable = false, efCollision = false, efApplyToGrids = false	},
	clone_of = false,
}

local floating_dummy_attach_clear_enum_flags = const.efWalkable + const.efCollision + const.efApplyToGrids

function AttachObjectToFloatingDummy(obj, dummy, parent)
	local o = PlaceObject("FloatingDummyCollision")
	NetTempObject(o)
	o.clone_of = obj
	TargetDummies[obj] = o
	o:ChangeEntity(obj:GetEntity())
	local enum_flags = obj:GetEnumFlags(floating_dummy_attach_clear_enum_flags)
	if enum_flags ~= 0 then
		o:SetEnumFlags(enum_flags)
		obj:ClearEnumFlags(enum_flags)
	end
	o:SetState(obj:GetState())
	o:SetMirrored(obj:GetMirrored())
	o:SetScale(obj:GetScale())
	o:SetOpacity(0)
	if parent then
		parent:Attach(o, obj:GetAttachSpot())
		o:SetAttachAxis(obj:GetAttachAxis())
		o:SetAttachAngle(obj:GetAttachAngle())
		o:SetAttachOffset(obj:GetAttachOffset())
	else
		local obj_axis = obj:GetAxis()
		local obj_angle = obj:GetAngle()
		local obj_pos = obj:GetPos()
		if not obj_pos:IsValidZ() then obj_pos = obj_pos:SetTerrainZ() end

		o:SetAxis(obj_axis)
		o:SetAngle(obj_angle)
		o:SetPos(obj:GetPosXYZ())

		local attach_spot = dummy:GetSpotBeginIndex("Spot")
		local spot_pos, spot_angle, spot_axis = dummy:GetSpotLoc(attach_spot, dummy:GetState(), 0)
		if not spot_pos:IsValidZ() then spot_pos = spot_pos:SetTerrainZ() end
		local attach_offset = RotateAxis(obj_pos - spot_pos, spot_axis, -spot_angle)
		local attach_axis, attach_angle = ComposeRotation(obj_axis, obj_angle, spot_axis, -spot_angle)
		dummy:Attach(obj, attach_spot)
		obj:SetAttachAxis(attach_axis)
		obj:SetAttachAngle(attach_angle)
		obj:SetAttachOffset(attach_offset)
		local phase = InteractionRand(obj:GetAnimDuration(), "FloatingDummy")
		obj:SetAnimPhase(1, phase)
	end
	obj:ForEachAttach(AttachObjectToFloatingDummy, dummy, o)
end

function AttachObjectsToFloatingDummies()
	if IsEditorActive() then return end
	local collection = {}
	MapForEach("map", "FloatingDummy", function(dummy, collection)
		local col_index = dummy:GetCollectionIndex()
		if col_index ~= 0 then
			collection[col_index] = dummy
		end
	end, collection)
	if not next(collection) then
		return
	end
	SuspendPassEdits("AttachObjectsToFloatingDummies")
	MapForEach("map", "CObject", function(obj, collection)
		local dummy = collection[obj:GetCollectionIndex()]
		if not dummy or obj == dummy or obj:GetParent() then
			return
		end
		AttachObjectToFloatingDummy(obj, dummy)
	end, collection)
	ResumePassEdits("AttachObjectsToFloatingDummies")
end

local function RestoreFloatingDummyAttachFlags(o)
	local obj = o.clone_of
	if IsValid(obj) then
		obj:SetEnumFlags(o:GetEnumFlags(floating_dummy_attach_clear_enum_flags))
		obj:ClearHierarchyGameFlags(const.gofSolidShadow)
	end
	o:ForEachAttach(RestoreFloatingDummyAttachFlags)
end

function RestoreFloatingDummyAttach(o)
	local obj = o.clone_of
	if IsValid(obj) then
		if IsKindOf(obj:GetParent(), "FloatingDummy") then
			obj:Detach()
			obj:SetPos(o:GetPosXYZ())
			obj:SetAxis(o:GetAxis())
			obj:SetAngle(o:GetAngle())
		end
		RestoreFloatingDummyAttachFlags(o)
	end
	DoneObject(o)
	TargetDummies[obj] = nil
end

function DetachObjectsFromFloatingDummies()
	SuspendPassEdits("DetachObjectsFromFloatingDummies")
	MapForEach("map", "FloatingDummyCollision", RestoreFloatingDummyAttach)
	ResumePassEdits("DetachObjectsFromFloatingDummies")
end

OnMsg.NewMapLoaded = AttachObjectsToFloatingDummies
OnMsg.GameEnteringEditor = DetachObjectsFromFloatingDummies -- called before GameEnterEditor, allowing XEditorFilters to catch these objects
OnMsg.GameExitEditor = AttachObjectsToFloatingDummies