File size: 8,209 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
MapVar("g_SnapCameraEnabled", true)

local lCameraCollisionMask = const.cmCameraMask | const.cmTerrain
local lCameraCollisionQueryFlags = const.cqfSingleResult | const.cqfSorted

-- Returns the camera pos and lookat snapped to any collisions and rotated to avoid
-- the original ptCameraLookAt from being offscreen once the camera clamps to the terrain
function GetCameraSnapToObjectParams(pos, floor)
	local ptCamera, ptCameraLookAt = cameraTac.GetPosLookAtEnd()
	if not pos then return ptCamera, ptCameraLookAt end
	pos = not pos:IsValidZ() and pos:SetTerrainZ() or pos
	
	-- Used for restoring when camera messes up.
	ptLastCameraPos, ptLastCameraLookAt = ptCamera, ptCameraLookAt

	local cameraVector = ptCameraLookAt - ptCamera
	ptCamera = pos - cameraVector
	ptCameraLookAt = pos

	-- Snap camera to camera collisions between pos and lookat.
	-- This matters when the collision is low, like in the underground mines.
	local first = true
	collision.Collide(ptCamera, ptCameraLookAt - ptCamera, lCameraCollisionQueryFlags, 0, lCameraCollisionMask,
		function(o, _, hitX, hitY, hitZ)
			if not first then return end
			first = false
			ptCameraLookAt = point(hitX, hitY, hitZ)
			ptCamera = ptCameraLookAt - cameraVector
	end)
	
	return ptCamera, ptCameraLookAt, floor or GetFloorOfPos(pos:xyz())
end

--Similar to GetCameraSnapToObjectParams, but it is only used for determining camera pos/lookat (with zoom taken into account) for DoPointsFitScreen
function GetCameraPosLookAtOnPos(pos)
	local ptCamera, ptCameraLookAt = cameraTac.GetZoomedPosLookAtEnd()
	if not pos then return ptCamera, ptCameraLookAt end
	pos = not pos:IsValidZ() and pos:SetTerrainZ() or pos

	local cameraVector = ptCameraLookAt - ptCamera
	ptCamera = pos - cameraVector
	ptCameraLookAt = pos

	-- Snap camera to camera collisions between pos and lookat.
	-- This matters when the collision is low, like in the underground mines.
	local first = true
	collision.Collide(ptCamera, ptCameraLookAt - ptCamera, lCameraCollisionQueryFlags, 0, lCameraCollisionMask,
		function(o, _, hitX, hitY, hitZ)
			if not first then return end
			first = false
			ptCameraLookAt = point(hitX, hitY, hitZ)
			ptCamera = ptCameraLookAt - cameraVector
	end)
	
	return ptCamera, ptCameraLookAt
end

SnapCameraToObjInterpolationTimeDefault = 1000
function SnapCameraToObj(obj, force, floor, time, easingType)
	if not g_SnapCameraEnabled then return end
	if not cameraTac.IsActive() then return end
	
	-- Dont snap the camera if requested by player input, and
	-- other player input is already moving the camera. (217661)
	if force == "player-input" then
		force = false
		if cameraTac.IsInputMovingCamera() then
			return
		end
	end
	
	local pos = IsValid(obj) and obj:GetPos() or obj
	if IsPoint(pos) and ((not IsCameraLocked() and not gv_Deployment) or force) then
		assert(not CurrentActionCamera)
		local ptCamera, ptCameraLookAt, floor = GetCameraSnapToObjectParams(pos, floor)
		local easing
		if easingType and easingType ~= "none" then
			easing = GetEasingIndex(easingType)
		else
			easing = hr.CameraTacPosEasing
		end
		time = time or SnapCameraToObjInterpolationTimeDefault
		cameraTac.SetPosLookAtAndFloor(ptCamera, ptCameraLookAt, floor, time, easing)
		return time, ptCamera, ptCameraLookAt
	end
	return 0
end

function SnapCameraToObjFloor(obj, force)
	if not g_SnapCameraEnabled or cameraTac.GetIsInOverview() then return end
	if not cameraTac.IsActive() then return end
	if IsValid(obj) and (not IsCameraLocked() or force) then 
		local floor = GetFloorOfPos(obj:GetPosXYZ())
		cameraTac.SetFloor(floor, hr.CameraTacInterpolatedMovementTime * 10, hr.CameraTacInterpolatedVerticalMovementTime * 10)
	end
end

function DoesTargetFitOnScreen(self, target)
	if GetUIStyleGamepad() then return false end
	
	local paddingX, paddingY = const.Camera.CrosshairPaddingX, const.Camera.CrosshairPaddingY
	
	local _, sx, sy = GameToScreenXY(target)
	local crosshair_dimX, crosshair_dimY = ScaleXY(self.scale, paddingX, paddingY)
	if sx - crosshair_dimX/2 < 0 or sy - crosshair_dimY/2 < 0 then
		return false
	end
	local screen_size = UIL.GetScreenSize()
	if sx + crosshair_dimX/2 >= screen_size:x() or sy + crosshair_dimY/2 >= screen_size:y() then
		return false
	end
	return true
end

function IsOnScreen(target)
	local screen_width, screen_height = UIL.GetScreenSize():xy()
	local front, screen_x, screen_y = GameToScreenXY(target)
	return front and screen_x > 0 and screen_y > 0 and screen_x < screen_width and screen_y < screen_height
end

function CameraPositionFromUnitOrientation(unit, time)
	local ptCamera, ptCameraLookAt = GetCamera()
	local cameraVector = ptCameraLookAt - ptCamera
	if unit.entrance_marker then
		local pos = unit.entrance_marker:GetPos()
		if not pos:IsValidZ() then
			pos = pos:SetTerrainZ()
		end
		local axis, marker_orient = unit.entrance_marker:GetOrientation()
		local cam_orient = CalcOrientation(ptCamera, ptCameraLookAt)
		local cameraVector = RotateAxis(cameraVector, axis, marker_orient - cam_orient)
		ptCamera = pos - cameraVector
		ptCameraLookAt = pos
		local floor = GetFloorOfPos(pos:xyz())
		cameraTac.SetPosLookAtAndFloor(ptCamera, ptCameraLookAt, floor, time or 1)
	elseif time then -- use interpolation
		local ptCamera, ptCameraLookAt = GetCamera()
		if not ptCamera then
			return
		end
		local pos = unit:GetPos()
		if not pos:IsValidZ() then
			pos = pos:SetTerrainZ()
		end
		ptCamera = pos - cameraVector
		ptCameraLookAt = pos
		local floor = GetFloorOfPos(ptCameraLookAt:xyz())
		cameraTac.SetPosLookAtAndFloor(ptCamera, ptCameraLookAt, floor, time or 1)
	else
		ViewPos(unit:GetPos())
		cameraTac.Rotate(-mapdata.MapOrientation * 60)
	end
end

local max_trans_len = 15*guim
function HandleCameraTargetFixed(src, tar)
	if not cameraTac.GetForceMaxZoom() or src == tar then return end
	local t_pos = IsPoint(tar) and tar or tar:GetPos()
	t_pos = not t_pos:IsValidZ() and t_pos:SetTerrainZ() or t_pos
	local front, t_pos_sc = GameToScreen(t_pos)
	local shrink = 300
	local box = g_DesktopBox:grow(shrink, shrink, -shrink, -shrink)
	if t_pos_sc:InBox(box) then return end
	local ptCamera, ptCameraLookAt = GetCamera()
	local s_pos = src:GetPos()
	local trans_vector_tar = t_pos:SetZ(0) - s_pos:SetZ(0)
	local len = s_pos:Dist2D(t_pos)/2
	len = len > max_trans_len and max_trans_len or len
	trans_vector_tar = SetLen(trans_vector_tar, len)
	local trans_vector_src = s_pos:SetZ(0) - ptCameraLookAt:SetZ(0)
	local trans_vector = trans_vector_src + trans_vector_tar
	cameraTac.SetCamera(ptCamera + trans_vector , ptCameraLookAt + trans_vector, 500, "Sin out")
end

function ResetTacticalCamera()
	local mapOrient = (mapdata.MapOrientation - 90) * 60
	local ptCamera, ptCameraLookAt = GetCamera()
	local cameraVector = ptCameraLookAt - ptCamera
	local cam_orient = CalcOrientation(ptCamera, ptCameraLookAt)
	if SelectedObj then
		local pos = SelectedObj:GetPos():SetTerrainZ()
		local cameraVector = RotateAxis(cameraVector, axis_z, mapOrient - cam_orient)
		ptCamera = pos - cameraVector
		ptCameraLookAt = pos
		cameraTac.SetCamera(ptCamera, ptCameraLookAt)
	else
		cameraTac.Rotate(cam_orient - mapOrient)
	end
	cameraTac.SetFloor(0)
end

----
-- Camera changing floor when unit does
----

MapVar("floorFollowData", false)

local function lClearFollowRecord()
	floorFollowData = false
end

OnMsg.ChangeMapDone = lClearFollowRecord
OnMsg.SelectedObjChange = lClearFollowRecord
OnMsg.TacCamFloorChanged = lClearFollowRecord

local function lFloorFollowRecord(unit)
	if not table.find(Selection, unit) then return end
	local currentFloor = cameraTac.GetFloor()
	local movementStartFloor = GetFloorOfPos(unit:GetPosXYZ())
	if movementStartFloor ~= currentFloor then return end
	floorFollowData = {
		unit = unit
	}
end

OnMsg.UnitMovementStart = lFloorFollowRecord -- combat
OnMsg.UnitGoToStart = lFloorFollowRecord -- exploration

local function lFloorFollowCheckAndApply(unit)
	if not floorFollowData then return end
	if floorFollowData.unit ~= unit then return end
	SnapCameraToObjFloor(unit)
	lClearFollowRecord()
end

OnMsg.UnitMovementDone = lFloorFollowCheckAndApply
OnMsg.UnitGoTo = lFloorFollowCheckAndApply

--overwrite func to do SetAutoFovX
function SetCameraFov(fovX)
	SetAutoFovX()
end