myspace / CommonLua /Camera.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
37.5 kB
---
--- Calculates the power of a camera shake effect based on the position of the camera and the position of the shake.
--- @param pos point The position of the shake.
--- @param radius_insight number The radius within which the shake is considered to be in sight of the camera.
--- @param radius_outofsight number The radius beyond which the shake is considered to be out of sight of the camera.
--- @return number The power of the camera shake effect, as a percentage.
---
function CameraShake_GetEffectPower(pos, radius_insight, radius_outofsight)
local cam_pos, cam_look = GetCamera()
local camera_orientation = CalcOrientation(cam_pos, cam_look)
local shake_orientation = CalcOrientation(cam_pos, pos)
local dist = DistSegmentToPt(cam_pos, cam_look, pos)
if dist < 0 then
assert(false)
return 0
end
local radius
if abs(AngleDiff(shake_orientation, camera_orientation)) < const.CameraShakeFOV/2 then
radius = radius_insight or const.ShakeRadiusInSight
else
radius = radius_outofsight or const.ShakeRadiusOutOfSight
end
return dist < radius and 100 * (radius - dist) / radius or 0
end
--- Starts a camera shake effect with the specified position and power.
-- @cstyle void CameraShake(point pos, int power).
-- @param pos point.
-- @param power int.
-- @return void.
function CameraShake(pos, power)
power = power * CameraShake_GetEffectPower(pos) / 100
if power == 0 then return end
local total_duration = const.MinShakeDuration + power*(const.MaxShakeDuration-const.MinShakeDuration)/const.MaxShakePower
local shake_offset = power*const.MaxShakeOffset/const.MaxShakePower
local shake_roll = power*const.MaxShakeRoll/const.MaxShakePower
camera.Shake(total_duration, const.ShakeTick, shake_offset, shake_roll)
end
---
--- Stores the current camera shake thread and the maximum offset for the camera shake effect.
---
--- @field camera_shake_thread thread The current camera shake thread.
--- @field camera_shake_max_offset number The maximum offset for the camera shake effect.
---
MapVar("camera_shake_thread", false)
MapVar("camera_shake_max_offset", 0)
---
--- Performs a camera shake effect with the specified parameters.
---
--- @param total_duration number The total duration of the camera shake effect, in seconds.
--- @param shake_tick number The interval between each shake, in seconds.
--- @param max_offset number The maximum offset of the camera shake, in meters.
--- @param max_roll_offset number The maximum roll offset of the camera shake, in degrees.
---
local function DoShakeCamera(total_duration, shake_tick, max_offset, max_roll_offset)
local time_left = total_duration
while true do
local LookAtOffset = RandPoint(1500, 500, 500)
local EyePtOffset = RandPoint(1500, 500, 500)
local len = Max(1, 2 * time_left * max_offset / total_duration)
local angle = 60 * time_left * max_roll_offset / total_duration
if LookAtOffset:Len2() > 0 then
LookAtOffset = SetLen(LookAtOffset, len)
end
if EyePtOffset:Len2() > 0 then
EyePtOffset = SetLen(EyePtOffset, len)
end
camera.SetLookAtOffset(LookAtOffset, shake_tick)
camera.SetEyeOffset(EyePtOffset, shake_tick)
camera.SetRollOffset(AsyncRand(2 * angle + 1) - angle, shake_tick)
if total_duration > 0 then
time_left = time_left - shake_tick
if time_left <= shake_tick then
Sleep(time_left)
break
end
end
Sleep(shake_tick)
end
camera.ShakeStop(shake_tick)
end
---
--- Performs a camera shake effect with the specified parameters.
---
--- @param total_duration number The total duration of the camera shake effect, in seconds.
--- @param shake_tick number The interval between each shake, in seconds.
--- @param shake_max_offset number The maximum offset of the camera shake, in meters. This value is clamped to the range [0, 10m].
--- @param shake_max_roll number The maximum roll offset of the camera shake, in degrees. This value is clamped to the range [0, 180].
---
function camera.Shake(total_duration, shake_tick, shake_max_offset, shake_max_roll)
local max_offset = Clamp(shake_max_offset, 0, 10 * guim)
assert(max_offset == shake_max_offset, "camera.Shake() max_offset should be [0-10m]!")
local max_roll = Clamp(shake_max_roll, 0, 180)
assert(max_roll == shake_max_roll, "camera.Shake() max_roll should be [0-180]!")
if total_duration == 0 or shake_tick <= 0 then
return
end
if IsValidThread(camera_shake_thread) then
if camera_shake_max_offset > shake_max_offset then
return
end
DeleteThread(camera_shake_thread)
end
camera_shake_max_offset = max_offset
camera_shake_thread = CreateRealTimeThread(DoShakeCamera, total_duration, shake_tick, max_offset, max_roll)
MakeThreadPersistable(camera_shake_thread)
end
---
--- Stops the camera shake effect.
---
--- @param shake_tick number The interval between each shake, in seconds.
---
function camera.ShakeStop(shake_tick)
camera.SetRollOffset(0, 0)
camera.SetLookAtOffset(point30, shake_tick or 0)
camera.SetEyeOffset(point30, shake_tick or 0)
camera_shake_max_offset = 0
if IsValidThread(camera_shake_thread) and CurrentThread() ~= camera_shake_thread then
DeleteThread(camera_shake_thread)
end
camera_shake_thread = false
end
function OnMsg.ChangeMap()
camera.ShakeStop()
end
---
--- Sets the camera to the specified position, look-at point, camera type, zoom, and field of view.
---
--- @param ptCamera vec3 The position of the camera.
--- @param ptCameraLookAt vec3 The point the camera is looking at.
--- @param camType string The type of camera to use. Can be "3p", "RTS", "Max", or "Tac".
--- @param zoom number The zoom level of the camera.
--- @param properties table A table of camera properties to set.
--- @param fovX number The field of view of the camera in degrees.
--- @param time number The duration of the camera transition in seconds.
---
--- @return nil
---
function SetCamera(ptCamera, ptCameraLookAt, camType, zoom, properties, fovX, time)
if type(ptCamera) == "table" then
return SetCamera(unpack_params(ptCamera))
end
time = time or 0
if camType then
if camType == "Max" or camType == "3p" or camType == "RTS" or camType == "Tac" then
camType = "camera" .. camType
end
_G[camType].Activate(1)
end
if not ptCamera then
return
end
if camera3p.IsActive() then
camera3p.SetEye(ptCamera, time)
camera3p.SetLookAt(ptCameraLookAt, time)
elseif cameraRTS.IsActive() then
if properties then
cameraRTS.SetProperties(1, properties)
end
cameraRTS.SetCamera(ptCamera, ptCameraLookAt, time)
if zoom then
cameraRTS.SetZoom(zoom)
end
elseif cameraMax.IsActive() then
-- cameraMax can't look straight down
local diff = ptCameraLookAt - ptCamera
if diff:x() == 0 and diff:y() == 0 then
ptCamera = ptCamera:SetX(ptCamera:x()-5)
end
cameraMax.SetCamera(ptCamera, ptCameraLookAt, time)
elseif cameraTac.IsActive() then
cameraTac.SetCamera(ptCamera, ptCameraLookAt, time)
if properties then
local floor = properties.floor
local overview = properties.overview
if floor then
cameraTac.SetFloor(floor)
end
if overview ~= nil then
cameraTac.SetOverview(overview, true)
end
end
if zoom then
cameraTac.SetZoom(zoom)
end
end
SetCameraFov(fovX)
end
---
--- Sets the camera field of view (FOV) to the specified value.
---
--- @param fovX number The horizontal field of view in degrees. If not provided, defaults to 70 degrees.
---
function SetCameraFov(fovX)
camera.SetFovX(fovX or 70 * 60)
end
---
--- Sets the camera field of view (FOV) to the specified value, with optional easing.
---
--- @param properties table A table containing the following properties:
--- - FovX: number The horizontal field of view in degrees.
--- - FovXNarrow: number The horizontal field of view in degrees for 3:4 screens.
--- - FovXWide: number The horizontal field of view in degrees for 21:9 screens.
--- @param duration number (optional) The duration of the FOV change in seconds.
--- @param easing string (optional) The easing function to use for the FOV change.
---
function SetRTSCameraFov(properties, duration, easing)
local FovX = properties.FovX
local minFovX = properties.FovXNarrow -- FovX for 3:4 screens
local minX, minY = 4, 3
local maxFovX = properties.FovXWide -- FovX for 21:9 screens
local maxX, maxY = 21, 9
if FovX and minFovX and maxFovX then
-- when both FovXNarrow and FovXWide are supplied,
-- FovX at 16:9 is computed and equals FovXNarrow * 5 / 9 + FovXWide * 4 / 9
assert(abs(minFovX * 5 / 9 + maxFovX * 4 / 9 - FovX) < 60)
end
FovX = FovX or 90 * 60
if not minFovX then
minFovX = FovX
minX, minY = 16, 9
end
if not maxFovX then
maxFovX = FovX
maxX, maxY = 16, 9
end
hr.CameraFovEasing = easing or "Linear"
camera.SetAutoFovX(1, duration or 0, minFovX, minX, minY, maxFovX, maxX, maxY)
end
-- init from last camera with reasonable settings ( at editor exit, ... )
-- or set to map center if first run
---
--- Sets the default camera to the RTS (Real-Time Strategy) camera type and configures its properties.
---
--- If the Libs.Sim module is available, it retrieves the RTS camera properties from the account storage and applies them.
--- Otherwise, it uses the default RTS camera properties defined in const.DefaultCameraRTS.
---
--- The function also sets the camera's field of view using the SetRTSCameraFov function, and positions the camera to the center of the map if the current look-at position is at (0, 0).
---
--- Finally, it calls the ViewObjectRTS function to set the camera's position and look-at to the center of the map.
---
function SetDefaultCameraRTS()
cameraRTS.Activate(1)
cameraRTS.SetProperties(1, const.DefaultCameraRTS)
if Libs.Sim then
cameraRTS.SetProperties(1, GetRTSCamPropsFromAccountStorage())
end
SetRTSCameraFov(const.DefaultCameraRTS)
local lookat = cameraRTS.GetLookAt()
if lookat:x() == 0 and lookat:y() == 0 then
lookat = point(terrain.GetMapSize()) / 2
end
ViewObjectRTS(lookat, 0)
end
---
--- Returns a table of available camera types.
---
--- @return table Camera types
---
function GetCameraTypesItems()
return {"3p", "RTS", "Max", "Tac"}
end
---
--- Returns the current camera position, look-at position, camera type, zoom level, and camera properties.
---
--- @return point, point, string, number, table, number Camera position, look-at position, camera type, zoom level, camera properties, and field of view angle
---
function GetCamera()
local ptCamera, ptCameraLookAt, camType, zoom, properties, fovX
if camera3p.IsActive() then
ptCamera, ptCameraLookAt = camera.GetEye(), camera3p.GetLookAt()
camType = "3p"
elseif cameraRTS.IsActive() then
ptCamera, ptCameraLookAt = cameraRTS.GetPosLookAt()
camType = "RTS"
zoom = cameraRTS.GetZoom()
properties = cameraRTS.GetProperties(1)
elseif cameraMax.IsActive() then
ptCamera, ptCameraLookAt = cameraMax.GetPosLookAt()
camType = "Max"
elseif cameraTac.IsActive() then
ptCamera, ptCameraLookAt = cameraTac.GetPosLookAt()
camType = "Tac"
zoom = cameraTac.GetZoom()
properties = {floor=cameraTac.GetFloor(), overview=cameraTac.GetIsInOverview()}
else
ptCamera, ptCameraLookAt = camera.GetEye(), camera.GetEye() + SetLen(camera.GetDirection(), 3 * guim)
end
fovX = camera.GetFovX()
return ptCamera, ptCameraLookAt, camType, zoom, properties, fovX
end
if FirstLoad then
ptLastCameraPos = false
ptLastCameraLookAt = false
cameraMax3DView = {
toggle = false,
old_pos = false,
old_lookat = false,
}
end
---
--- Cleans up the state of the cameraMax3DView object.
--- Resets the toggle, old_pos, and old_lookat properties to their default values.
---
function cameraMax3DView:Clean()
self.toggle = false
self.old_pos = false
self.old_lookat = false
end
-- returns the new camera pos and the look pos of the selection
---
--- Rotates the camera in the 3D Max view to the specified view direction.
---
--- @param view_direction point The new view direction for the camera.
---
local function cameraMax3DView_Rotate(view_direction)
local sel = editor.GetSel()
local cnt = #sel
if cnt == 0 then
print("You need to select object(s) for this operation")
return
end
local center = point30
for i = 1, cnt do
local bsc = sel[i]:GetBSphere()
center = center + bsc
end
if cnt > 0 then
-- find center of the selection
center = point(center:x() / cnt, center:y() / cnt, center:z() / cnt)
-- find the radius of the bounding sphere of the selection
local selSize = 0
for i = 1, cnt do
local bsc, bsr = sel[i]:GetBSphere()
local dist = bsc:Dist(center) + bsr
if selSize < dist then
selSize = dist
end
end
selSize = 2 * selSize -- get the diameter of the selection
-- move the camera position to look in the center of the selection
local half_fovY = MulDivRound(camera.GetFovY(), 1, 2)
local fov_sin, fov_cos = sin(half_fovY), cos(half_fovY)
local dist_from_camera = (fov_sin > 0) and MulDivRound((selSize / 2), fov_cos, fov_sin) or (selSize / 2)
view_direction = SetLen(view_direction, dist_from_camera * 130 / 100)
local pos = center + view_direction
cameraMax.SetCamera(pos, center, 0)
end
end
---
--- Rotates the camera in the 3D Max view to the up direction.
---
function cameraMax3DView:SetViewUp()
cameraMax3DView_Rotate(point(0, 0, 1))
end
---
--- Rotates the camera in the 3D Max view to the down direction.
---
function cameraMax3DView:SetViewDown()
cameraMax3DView_Rotate(point(0, 0, -1))
end
---
--- Sets the camera to the old position and look-at point.
---
function cameraMax3DView:SetViewOld()
cameraMax.SetCamera(cameraMax3DView.old_pos, cameraMax3DView.old_lookat, 0)
end
---
--- Rotates the camera in the 3D Max view around the Z axis.
---
--- @param dir string The direction to rotate the camera, either "east" or "west".
---
function cameraMax3DView:RotateZ(dir)
local pos, look_at = cameraMax.GetPosLookAt()
local cam_angle = (camera.GetYaw() / 60) + 180
local cam_quadrant = (cam_angle / 90) % 4 + 1
local correction = 0
local z_axis = point(0, 0, 1)
if cam_angle % 90 ~= 0 then
if cam_angle - 90 * (cam_quadrant - 1) < 90 * cam_quadrant - cam_angle then
correction = -(cam_angle - 90 * (cam_quadrant - 1))
else
correction = 90 * cam_quadrant - cam_angle
end
cam_angle = cam_angle + correction
end
local view_dir = false
if dir == "east" then
view_dir = RotateAxis(pos, z_axis, (cam_angle - 90) * 60)
else
view_dir = RotateAxis(pos, z_axis, (cam_angle + 90) * 60)
end
if view_dir then
cameraMax3DView_Rotate(Normalize(view_dir))
end
end
---
--- Sets the camera position and look-at point.
---
--- @param pos table The position of the camera, represented as a point.
--- @param dist number (optional) The distance from the camera to the look-at point.
--- @param cam_type number (optional) The type of camera to use.
---
--- If `pos` is `InvalidPos()`, the camera will be reset to the last known position and look-at point.
--- If `pos` does not have a valid Z coordinate, it will be set to the terrain Z coordinate.
--- The camera vector is calculated based on the `pos` and the look-at point, and the camera is set to this position.
---
function ViewPos(pos, dist, cam_type)
local ptCamera, ptCameraLookAt = GetCamera()
if not ptCamera then
return
end
if pos == InvalidPos() then
pos = nil
end
if not pos then
if ptLastCameraPos then
SetCamera(ptLastCameraPos, ptLastCameraLookAt, cam_type)
end
return
end
ptLastCameraPos, ptLastCameraLookAt = ptCamera, ptCameraLookAt
if not pos:z() then
pos = pos:SetTerrainZ()
end
local cameraVector = ptCameraLookAt - ptCamera
if dist then
cameraVector = SetLen(cameraVector, dist)
end
ptCamera = pos - cameraVector
ptCameraLookAt = pos
SetCamera(ptCamera, ptCameraLookAt, cam_type)
end
---
--- Sets the camera to view the specified object.
---
--- @param obj MapObject|number The object to view, or its handle.
--- @param dist number (optional) The distance from the camera to the object.
---
--- If `obj` is a number, it is assumed to be the handle of a `MapObject` and looked up in `HandleToObject`.
--- If `pos` is `InvalidPos()`, the camera will be reset to the last known position and look-at point.
--- If `pos` does not have a valid Z coordinate, it will be set to the terrain Z coordinate.
--- The camera vector is calculated based on the `pos` and the look-at point, and the camera is set to this position.
---
ViewObject = function(obj, dist)
if type(obj) == "number" and HandleToObject[obj] then
obj = HandleToObject[obj]
end
local pos = IsValid(obj) and obj:GetPos()
if not pos or pos == InvalidPos() then
return
end
if dist then
ViewPos(pos, dist)
else
local center, radius = obj:GetBSphere()
ViewPos(center, Max(guim, radius * 10))
end
end
---
--- Caches the last object viewed by `ViewNextObject`.
---
--- This cache is used to keep track of the last object that was viewed, so that `ViewNextObject` can cycle through the objects in the order they were viewed.
---
local ViewNextObjectCache
function OnMsg.ChangeMap()
ViewNextObjectCache = nil
end
-- Cycles ViewObject in the array objs, viewing the next object every time it is called for the same set of parameters
---
--- Cycles through the next object in the given list of objects and views it.
---
--- @param name string (optional) The class name of the objects to cycle through. If not provided, the class name of the last selected object is used.
--- @param objs table (optional) The list of objects to cycle through. If not provided, all objects of the given class name are used.
--- @param select_obj boolean (optional) Whether to select the object after viewing it.
---
--- If `name` is not provided and there is no last selected object, this function does nothing.
--- If `objs` is not provided, all objects of the given class name are used.
--- The function keeps track of the last object viewed and cycles to the next one in the list.
--- If the end of the list is reached, it cycles back to the beginning.
--- If `select_obj` is true, the viewed object is also selected.
---
function ViewNextObject(name, objs, select_obj)
name = name or ""
local last
if not objs then
if name == "" then
last = SelectedObj
name = last and last.class
select_obj = true
end
if not IsKindOf(g_Classes[name], "MapObject") then
return
end
objs = MapGet("map", name)
end
ViewNextObjectCache = ViewNextObjectCache or setmetatable({}, weak_values_meta)
last = last or ViewNextObjectCache[name]
local idx = last and table.find(objs, last) or 0
last = objs[idx + 1] or objs[1]
ViewNextObjectCache[name] = last
ViewObject(last)
SelectObj(last)
end
---
--- Cycles through the next object in the given list of objects and views it.
---
--- @param name string (optional) The class name of the objects to cycle through. If not provided, the class name of the last selected object is used.
--- @param objs table (optional) The list of objects to cycle through. If not provided, all objects of the given class name are used.
--- @param select_obj boolean (optional) Whether to select the object after viewing it.
---
--- If `name` is not provided and there is no last selected object, this function does nothing.
--- If `objs` is not provided, all objects of the given class name are used.
--- The function keeps track of the last object viewed and cycles to the next one in the list.
--- If the end of the list is reached, it cycles back to the beginning.
--- If `select_obj` is true, the viewed object is also selected.
---
function ViewObjects(objects)
objects = objects or {}
local dgs = XEditorSelectSingleObjects
XEditorSelectSingleObjects = 1
editor.ChangeSelWithUndoRedo(objects)
XEditorSelectSingleObjects = dgs
if #objects == 0 then
return
end
local bbox = GetObjectsBBox(objects)
local center, radius = bbox:GetBSphere()
local cam_pos = camera.GetEye()
local h = cam_pos:z() - terrain.GetSurfaceHeight(cam_pos)
local eye = center:SetZ(0) + SetLen((cam_pos - center):SetZ(0), h)
eye = eye:SetZ(terrain.GetSurfaceHeight(eye) + h)
local dist = (eye - center):Len()
local new_dist = Clamp(Max(dist, 2*radius), 10*guim, 100*guim)
eye = center + MulDivRound(eye - center, new_dist, dist)
local steps = 18
local angle = 360 * 60 / steps
local max_radius = 2 * guim
local success = true
local objects_map = {}
for i=1,#objects do
objects_map[objects[i]] = true
end
while true do
local objs = IntersectSegmentWithObjects(eye, center, const.efVisible)
if not objs then
break
end
local objects_too_big = false
for i=1,#objs do
local obj = objs[i]
if not objects_map[obj] then
local center, radius = obj:GetBSphere()
if radius > max_radius then
objects_too_big = true
break
end
end
end
if not objects_too_big then
break
end
steps = steps - 1
if steps <= 1 then
success = false
break
end
eye = RotateAroundCenter(center, eye, angle)
eye = eye:SetZ(terrain.GetSurfaceHeight(eye) + h)
end
if success then
SetCamera(eye, center)
end
end
if FirstLoad then
SplitScreenType = false
SplitScreenEnabled = true
SecondViewEnabled = false
SecondViewViewport = false
end
-- call this after every resolution/scene size change to recalc and setup appropriate views for single or split screen
---
--- Sets up the camera views for single or split screen.
---
--- @param size number|nil The size of the screen, if provided.
---
--- This function is responsible for configuring the camera views based on the current split screen settings.
--- If split screen is enabled, it sets up two views - one for each player. The views can be either horizontal or vertical.
--- If split screen is disabled, it sets up a single view that covers the entire screen.
--- The function adjusts the viewport settings of the camera accordingly.
---
function SetupViews(size)
local w, h = 1000000, 1000000
if SecondViewEnabled and SecondViewViewport then
camera.SetViewCount(2)
camera.SetViewport(box(0, 0, w, h), 1)
camera.SetViewport(SecondViewViewport, 2)
elseif SplitScreenEnabled then
if SplitScreenType == "horizontal" then
camera.SetViewCount(2)
camera.SetViewport(box(0, 0, w, h / 16 * 8), 1)
camera.SetViewport(box(0, (h + 15) / 16 * 8, w, h), 2)
elseif SplitScreenType == "vertical" then
camera.SetViewCount(2)
camera.SetViewport(box(0, 0, w / 16 * 8, h), 1)
camera.SetViewport(box((w + 15) / 16 * 8, 0, w, h), 2)
else
camera.SetViewCount(1)
camera.SetViewport(box(0, 0, w, h), 1)
end
else
if not SplitScreenType then
camera.SetViewCount(1)
camera.SetViewport(box(0, 0, w, h), 1)
else
camera.SetViewport(box(0, 0, w, h), 1)
end
end
end
if FirstLoad then
SplitScreenDisableReasons = {}
end
---
--- Enables or disables split screen mode based on the provided reason.
---
--- @param on boolean Whether to enable or disable split screen mode.
--- @param reason string The reason for enabling or disabling split screen mode.
---
--- This function is responsible for managing the state of split screen mode. It updates the `SplitScreenDisableReasons` table to track the reasons for enabling or disabling split screen mode. If there are no more reasons to disable split screen mode, it enables it. Otherwise, it disables it. The function also calls `SetupViews()` to reconfigure the camera views and sends a "SplitScreenChange" message.
---
function SetSplitScreenEnabled(on, reason)
assert(reason)
SplitScreenDisableReasons[reason] = (on == false) or nil
on = not next(SplitScreenDisableReasons)
if SplitScreenEnabled ~= on then
SplitScreenEnabled = on
SetupViews()
Msg("SplitScreenChange", true)
end
end
---
--- Enables the second view for the camera and sets the viewport for it.
---
--- @param viewport table The viewport for the second view.
---
--- This function enables the second view for the camera and sets the viewport for it. It updates the `SecondViewEnabled` and `SecondViewViewport` variables and then calls the `SetupViews()` function to reconfigure the camera views.
---
function EnableSecondView(viewport)
SecondViewEnabled = true
SecondViewViewport = viewport
SetupViews()
end
---
--- Disables the second view for the camera.
---
--- This function disables the second view for the camera by setting the `SecondViewEnabled` variable to `false` and calling the `SetupViews()` function to reconfigure the camera views.
---
function DisableSecondView()
SecondViewEnabled = false
SetupViews()
end
---
--- Sets the split screen type.
---
--- @param type string The type of split screen to use, or an empty string to disable split screen.
---
--- This function sets the `SplitScreenType` variable to the provided `type` parameter. If the `type` is an empty string, split screen is disabled. The function then calls `SetupViews()` to reconfigure the camera views, and sends a "SplitScreenChange" message if the split screen type has changed.
function SetSplitScreenType(type)
if type == "" then
type = false
end
local bChange = SplitScreenType ~= type
SplitScreenType = type
if not CameraControlScene then
SetupViews()
end
if bChange then
Msg("SplitScreenChange")
end
end
---
--- Checks if split screen is enabled.
---
--- @return boolean true if split screen is enabled, false otherwise
---
function IsSplitScreenEnabled()
return SplitScreenEnabled and SplitScreenType and true
end
---
--- Checks if split screen is in horizontal mode.
---
--- @return boolean true if split screen is in horizontal mode, false otherwise
---
function IsSplitScreenHorizontal()
return SplitScreenEnabled and SplitScreenType == "horizontal"
end
---
--- Checks if split screen is in vertical mode.
---
--- @return boolean true if split screen is in vertical mode, false otherwise
---
function IsSplitScreenVertical()
return SplitScreenEnabled and SplitScreenType == "vertical"
end
---
--- Loads a map and camera location from a saved state.
---
--- @param map string The name of the map to load.
--- @param cam_params table A table containing the camera parameters to set.
--- @param editor_mode boolean Whether to activate the editor mode after loading.
--- @param map_rand number The random seed to use for the map.
---
--- This function loads a map and camera location from a saved state. It first checks if the map exists, and if not, prints an error message. It then creates a real-time thread to perform the following steps:
---
--- 1. Deactivate the editor.
--- 2. If the map or random seed is different from the current map, change the map and restore the configuration.
--- 3. If the editor mode is enabled, activate the editor.
--- 4. Set the camera parameters, activating the fly camera if necessary.
--- 5. Close any open menu dialogs.
--- 6. Send a "OnDbgLoadLocation" message.
---
--- This function is typically used for debugging purposes, to quickly load a specific map and camera location.
function DbgLoadLocation(map, cam_params, editor_mode, map_rand)
if not MapData[map] then
print("No such map:", map)
return
end
CreateRealTimeThread(function()
EditorDeactivate()
if map ~= GetMapName() or map_rand and map_rand ~= MapLoadRandom then
if map_rand then
table.change(config, "DbgLoadLocation", {FixedMapLoadRandom=map_rand})
end
ChangeMap(map)
table.restore(config, "DbgLoadLocation", true)
end
if editor_mode then
EditorActivate()
end
if cam_params then
if cam_params[3] == "Fly" then
cam_params[3] = "Max"
SetCamera(table.unpack(cam_params))
cameraFly.Activate()
else
SetCamera(table.unpack(cam_params))
end
end
CloseMenuDialogs()
Msg("OnDbgLoadLocation")
end)
end
---
--- Gets a string representation of the current camera location that can be used to restore the camera state.
---
--- @return string A string that can be passed to `DbgLoadLocation` to restore the camera state.
---
function GetCameraLocationString()
local cam_params
if cameraFly.IsActive() then
-- Fly camera doesn't expose its parameters, but it can be saved as Max and forced to Fly again on load
cameraMax.Activate()
cam_params = {GetCamera()}
cam_params[3] = "Fly"
cameraFly.Activate()
else
cam_params = {GetCamera()}
end
return string.format("DbgLoadLocation( \"%s\", %s, %s, %s)\n", GetMapName(), TableToLuaCode(cam_params, ' '),
IsEditorActive() and "true" or "false", tostring(MapLoadRandom))
end
function OnMsg.BugReportStart(print_func)
print_func(string.format("\nLocation: (paste in the console)\n%s", GetCameraLocationString()))
end
if FirstLoad then
g_ResetSceneCameraViewportThread = false
end
function OnMsg.SystemSize(pt)
--if FullscreenMode() == 0 then
DeleteThread(g_ResetSceneCameraViewportThread)
g_ResetSceneCameraViewportThread = CreateRealTimeThread(function()
WaitNextFrame(1)
SetupViews(pt)
end)
--end
end
---
--- Checks if the given position is a valid camera position.
---
--- @param pos point The position to check.
--- @return boolean True if the position is valid, false otherwise.
---
local function IsValidCameraPos(pos)
return pos and pos ~= point30 and pos ~= InvalidPos()
end
---
--- Checks if the camera can move between two positions without intersecting terrain.
---
--- @param pos0 point The starting position for the camera movement.
--- @param pos1 point The ending position for the camera movement.
--- @return boolean True if the camera can move between the two positions without intersecting terrain, false otherwise.
---
local function CanMoveCamBetween(pos0, pos1)
local max_move_dist = const.MaxMoveCamDist or max_int
if max_move_dist >= max_int or IsCloser(pos0, pos1, max_move_dist) then
return true
end
return not terrain.IntersectSegment(pos0, pos1)
end
---
--- Moves the camera to view the specified object, optionally with a zoom level.
---
--- @param obj table|point The object or position to view
--- @param time number The time in seconds for the camera to move to the new position
--- @param pos point The position to move the camera to
--- @param zoom number The zoom level to set the camera to
---
function ViewObjectRTS(obj, time, pos, zoom)
if not obj then
return
end
local la = IsPoint(obj) and obj or IsValid(obj)
and (obj:HasMember("GetLogicalPos") and obj:GetLogicalPos() or obj:GetVisualPos())
if not la or la == InvalidPos() then
return
end
la = la:SetTerrainZ()
local cur_pos, cur_la = cameraRTS.GetPosLookAt()
if not pos then
local cur_off = cur_pos - cur_la
if not IsValidCameraPos(cur_pos) or cur_pos == cur_la then
local lookatDist = const.DefaultCameraRTS.LookatDistZoomIn
+ (const.DefaultCameraRTS.LookatDistZoomOut - const.DefaultCameraRTS.LookatDistZoomIn)
* cameraRTS.GetZoom()
cur_off = SetLen(point(1, 1, 0), lookatDist * guim) + point(0, 0, cameraRTS.GetHeight() * guim)
zoom = zoom or 0.5
end
pos = la + cur_off
end
pos, la = cameraRTS.Normalize(pos, la)
if not IsValidCameraPos(cur_pos) or not CanMoveCamBetween(cur_pos, pos) then
time = 0
elseif not time then
local min_dist, max_dist = 200 * guim, 1000 * guim
local min_time, max_time = 200, 500
local dist_factor = Clamp(pos:Dist2D(cur_pos) - min_dist, 0, max_dist) * 100 / (max_dist - min_dist)
time = min_time + (max_time - min_time) * dist_factor / 100
end
cameraRTS.SetCamera(pos, la, time or 0, "Sin in/out")
if zoom then
cameraRTS.SetZoom(zoom, time or 0)
end
end
---
--- Defines the available types of camera interpolation.
---
--- @class CameraInterpolationTypes
--- @field linear integer Linear interpolation.
--- @field spherical integer Spherical interpolation.
--- @field polar integer Polar interpolation.
CameraInterpolationTypes = {linear=0, spherical=1, polar=2}
---
--- Defines the available types of camera movement.
---
--- @class CameraMovementTypes
--- @field linear integer Linear movement.
--- @field harmonic integer Harmonic movement.
--- @field accelerated integer Accelerated movement.
--- @field decelerated integer Decelerated movement.
CameraMovementTypes = {linear=0, harmonic=1, accelerated=2, decelerated=3}
---
--- Sets the camera position and lookat point, taking into account the base offset and angle.
---
--- @param pos table The camera position.
--- @param lookat table The camera lookat point.
--- @param base_offset table The base offset to apply to the position and lookat.
--- @param base_angle number The base angle to apply to the position and lookat.
--- @param camera_view integer The camera view to use.
---
function SetCameraPosMaxLookAt(pos, lookat, base_offset, base_angle, camera_view)
cameraMax.SetPositionLookatAndRoll(base_offset + Rotate(pos, base_angle), base_offset + Rotate(lookat, base_angle),
0)
end
---
--- Interpolates the camera position and lookat point between two camera states over a given duration, relative to a reference object.
---
--- @param camera1 table The initial camera state, with `pos` and `lookat` fields.
--- @param camera2 table The final camera state, with `pos` and `lookat` fields.
--- @param duration number The duration of the interpolation in frames.
--- @param relative_to Entity The entity to use as the reference for the camera position and lookat.
--- @param interpolation string The type of interpolation to use, one of "linear", "spherical", or "polar".
--- @param movement string The type of camera movement to use, one of "linear", "harmonic", "accelerated", or "decelerated".
--- @param camera_view integer The camera view to use.
---
function InterpolateCameraMaxWakeup(camera1, camera2, duration, relative_to, interpolation, movement, camera_view)
camera_view = camera_view or 1
local base_offset = IsValid(relative_to) and relative_to:GetVisualPosPrecise(1000) or point30
local base_angle = IsValid(relative_to) and relative_to:GetVisualAngle() or 0
local camera2_pos = Rotate(camera2.pos * 1000 - base_offset, 360 * 60 - base_angle)
local camera2_lookat = Rotate(camera2.lookat * 1000 - base_offset, 360 * 60 - base_angle)
if duration > 1 then
local camera1_pos = Rotate(camera1.pos * 1000 - base_offset, 360 * 60 - base_angle)
local camera1_lookat = Rotate(camera1.lookat * 1000 - base_offset, 360 * 60 - base_angle)
SetCameraPosMaxLookAt(camera1_pos, camera1_lookat, base_offset, base_angle, camera_view)
for t = 1, duration do
if WaitWakeup(1) then
break
end
base_offset = IsValid(relative_to) and relative_to:GetVisualPosPrecise(1000) or point30
base_angle = IsValid(relative_to) and relative_to:GetVisualAngle() or 0
local p, l = CameraLerp(camera1_pos, camera1_lookat, camera2_pos, camera2_lookat, t, duration,
CameraInterpolationTypes[interpolation] or 0, CameraMovementTypes[movement] or 0)
SetCameraPosMaxLookAt(p, l, base_offset, base_angle, camera_view)
end
end
SetCameraPosMaxLookAt(camera2_pos, camera2_lookat, base_offset, base_angle, camera_view)
end
---
--- Toggles the fly camera mode.
---
--- If the fly camera is active, it deactivates the fly camera and applies the camera and controllers.
--- If the fly camera is not active, it activates the fly camera and recalculates the active player control.
--- It also sets the mouse delta mode accordingly.
---
function CheatToggleFlyCamera()
if cameraFly.IsActive() then
SetMouseDeltaMode(false)
if rawget(_G, "GetPlayerControlObj") and GetPlayerControlObj() then
ApplyCameraAndControllers()
else
SetupInitialCamera()
end
else
print("Camera Fly")
cameraFly.Activate(1)
if rawget(_G, "GetPlayerControlObj") and GetPlayerControlObj() then
PlayerControl_RecalcActive(true)
end
SetMouseDeltaMode(true)
end
end