From 1ab7a886e71d445d96895b78b1a8a702915aaa0e Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Wed, 30 Aug 2017 11:40:12 +0100 Subject: [PATCH] Improve spectator drawing and selection (#5457) * Optimise cursor object updates * Restrict distance units can be selected from * Fix potential for no icons to render in FPP * Limit projectile drawing distance * Optimise icon updates * Fix group names not being drawn in vehicles * Fix selection nameplate not drawing for vehicles * Fix selection of non-spectatable entities --- addons/spectator/functions/fnc_ui_draw3D.sqf | 23 +++++--- .../fnc_ui_handleMouseButtonDown.sqf | 10 ++-- .../functions/fnc_ui_updateIconsToDraw.sqf | 56 +++++++++---------- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/addons/spectator/functions/fnc_ui_draw3D.sqf b/addons/spectator/functions/fnc_ui_draw3D.sqf index 02a1d02837..fc0d39ea93 100644 --- a/addons/spectator/functions/fnc_ui_draw3D.sqf +++ b/addons/spectator/functions/fnc_ui_draw3D.sqf @@ -19,17 +19,21 @@ BEGIN_COUNTER(updateCursor); private _camTarget = GVAR(camTarget); +private _camTargetVeh = vehicle _camTarget; private _cursorObject = objNull; -// This function doesn't work for units underwater, due to use of screenToWorld. Would be complicated to work around this. -private _intersections = [getMousePosition select 0, getMousePosition select 1, _camTarget, vehicle _camTarget] call BIS_fnc_getIntersectionsUnderCursor; +// This doesn't work for units underwater due to use of screenToWorld +// Would be hard to work around due to parallax +private _start = AGLToASL positionCameraToWorld [0,0,0]; +private _end = AGLToASL screenToWorld getMousePosition; -if !(_intersections isEqualTo []) then { - _cursorObject = (_intersections select 0) select 3; -}; +// Can only select units within name drawing distance +if ((_start distanceSqr _end) <= DISTANCE_NAMES_SQR) then { + private _intersections = lineIntersectsSurfaces [_start, _end, _camTarget, _camTargetVeh]; -if !(_cursorObject isKindOf "Man") then { - _cursorObject = effectiveCommander _cursorObject; + if !(_intersections isEqualTo []) then { + _cursorObject = effectiveCommander ((_intersections select 0) select 3); + }; }; GVAR(cursorObject) = _cursorObject; @@ -43,10 +47,11 @@ if !(GVAR(uiMapVisible)) then { _x params ["_unit", "_type", "_icon"]; private _position = (_unit modelToWorldVisual (_unit selectionPosition "Head")) vectorAdd [0,0,HEIGHT_OFFSET]; - if (_type == 2 && { _unit distanceSqr GVAR(camera) < DISTANCE_NAMES_SQR } && {_unit in _camTarget || _unit in _cursorObject}) then { + // Cursor object is always effectiveCommander so no need to check `in` + if (_type == 2 && {_unit in _camTargetVeh || _unit == _cursorObject}) then { drawIcon3D [ ICON_BACKGROUND_UNIT, - [0, 0, 0, [0.4, 0.8] select (_unit in _camTarget)], + [0, 0, 0, [0.4, 0.8] select (_unit in _camTargetVeh)], _position, 5, 4, diff --git a/addons/spectator/functions/fnc_ui_handleMouseButtonDown.sqf b/addons/spectator/functions/fnc_ui_handleMouseButtonDown.sqf index 52c094f174..365c85bd16 100644 --- a/addons/spectator/functions/fnc_ui_handleMouseButtonDown.sqf +++ b/addons/spectator/functions/fnc_ui_handleMouseButtonDown.sqf @@ -33,11 +33,13 @@ if (_button == 0) exitWith { [objNull] call FUNC(setFocus); }; } else { - playSound "ReadoutClick"; + if (GVAR(cursorObject) in ([] call FUNC(getTargetEntities))) then { + playSound "ReadoutClick"; - // Focus will be at screen center - [GVAR(cursorObject)] call FUNC(setFocus); - setMousePosition [0.5, 0.5]; + // Focus will be at screen center + [GVAR(cursorObject)] call FUNC(setFocus); + setMousePosition [0.5, 0.5]; + }; }; }; diff --git a/addons/spectator/functions/fnc_ui_updateIconsToDraw.sqf b/addons/spectator/functions/fnc_ui_updateIconsToDraw.sqf index 1114a2d392..12dcb11af9 100644 --- a/addons/spectator/functions/fnc_ui_updateIconsToDraw.sqf +++ b/addons/spectator/functions/fnc_ui_updateIconsToDraw.sqf @@ -19,17 +19,17 @@ private _iconsToDraw = []; private _entitiesToDraw = []; +// camToWorld is used instead of camera object to account for FPP +private _camPos = AGLToASL positionCameraToWorld [0,0,0]; { private _vehicle = vehicle _x; private _inVehicle = (_vehicle != _x); - private _distanceToCameraSqr = GVAR(camera) distanceSqr _x; + private _distanceToCameraSqr = _camPos distanceSqr _x; if (_distanceToCameraSqr <= DISTANCE_ICONS_SQR && { !_inVehicle || { _x == effectiveCommander _vehicle } }) then { private _group = group _x; - private _groupSide = side _group; - private _groupName = groupId _group; - private _groupLeader = leader _group; - private _groupColor = [_groupSide] call BIS_fnc_sideColor; + private _isLeader = _x == leader _group; + private _groupColor = [side _group] call BIS_fnc_sideColor; // Calculate distance fade (_distanceToCameraSqr call { @@ -54,19 +54,19 @@ private _entitiesToDraw = []; // Apply color fade _groupColor set [3, _fadeByDistance]; - private _name = ([_x] call EFUNC(common,getName)) select [0, NAME_MAX_CHARACTERS]; - if !(isPlayer _x) then { _name = format ["%1: %2", localize "str_player_ai", _name]; }; - - if (_inVehicle) then { - private _crewCount = (({alive _x} count (crew _vehicle)) - 1); - if (_crewCount > 0) then { - _name = format ["%1 (+%2)", _name, _crewCount]; - }; - }; - // Show unit name only if camera is near enough if (_distanceToCameraSqr < DISTANCE_NAMES_SQR) then { - // Unit name + private _name = ([_x] call EFUNC(common,getName)) select [0, NAME_MAX_CHARACTERS]; + if !(isPlayer _x) then { _name = format ["%1: %2", localize "str_player_ai", _name]; }; + + if (_inVehicle) then { + private _crewCount = (({alive _x} count (crew _vehicle)) - 1); + if (_crewCount > 0) then { + _name = format ["%1 (+%2)", _name, _crewCount]; + }; + }; + + // Draw unit name for effective commander or all units on foot _iconsToDraw pushBack [_x, 2, [ "", [1,1,1,1], @@ -81,8 +81,8 @@ private _entitiesToDraw = []; "center" ]]; } else { - if (_x == _groupLeader) then { - // Group name + // Draw group name for effective commander or leader on foot + if (_inVehicle || _isLeader) then { _iconsToDraw pushBack [_x, 0, [ "", [1,1,1,_fadeByDistance], @@ -90,7 +90,7 @@ private _entitiesToDraw = []; 0, _heightByDistance, 0, - _groupName, + groupID _group, 2, _fontSizeByDistance, "PuristaMedium", @@ -99,8 +99,8 @@ private _entitiesToDraw = []; }; }; - if (_x == _groupLeader || { _inVehicle && { _x == effectiveCommander _vehicle } }) then { - // Group icon + // Draw group icon for effective commander or leader on foot + if (_inVehicle || _isLeader) then { _iconsToDraw pushBack [_x, 0, [ [_group, true] call FUNC(getGroupIcon), _groupColor, @@ -116,7 +116,7 @@ private _entitiesToDraw = []; ]]; }; - // Draw unit icon + // Draw unit icon for effective commander or all units on foot _iconsToDraw pushBack [_x, 1, [ [ICON_UNIT, ICON_REVIVE] select (NEEDS_REVIVE(_x)), _groupColor, @@ -130,14 +130,14 @@ private _entitiesToDraw = []; "PuristaMedium", "center" ]]; - }; - // Track entities themselves for use with fired EH - _entitiesToDraw pushBack _vehicle; + // Track entities themselves for use with fired EH + _entitiesToDraw pushBack _vehicle; - // Add fired EH for drawing and icon highlighting - if (GETVAR(_vehicle,GVAR(firedEH),-1) == -1) then { - SETVAR(_vehicle,GVAR(firedEH),_vehicle addEventHandler [ARR_2("Fired",{_this call FUNC(handleFired)})]); + // Add fired EH for drawing and icon highlighting + if (GETVAR(_vehicle,GVAR(firedEH),-1) == -1) then { + SETVAR(_vehicle,GVAR(firedEH),_vehicle addEventHandler [ARR_2("Fired",{_this call FUNC(handleFired)})]); + }; }; nil // Speed loop