diff --git a/addons/spectator/$PBOPREFIX$ b/addons/spectator/$PBOPREFIX$ new file mode 100644 index 0000000000..42fe4034e9 --- /dev/null +++ b/addons/spectator/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\spectator \ No newline at end of file diff --git a/addons/spectator/ACE_Settings.hpp b/addons/spectator/ACE_Settings.hpp new file mode 100644 index 0000000000..88f4ebfad6 --- /dev/null +++ b/addons/spectator/ACE_Settings.hpp @@ -0,0 +1,26 @@ +class ACE_Settings { + class GVAR(enabled) { + value = 0; + typeName = "BOOL"; + }; + class GVAR(limitSide) { + value = 0; + typeName = "BOOL"; + }; + class GVAR(AI) { + value = 0; + typeName = "BOOL"; + }; + class GVAR(tracking) { + value = 0; + typeName = "BOOL"; + }; + class GVAR(modulePos) { + value = 0; + typeName = "BOOL"; + }; + class GVAR(endMission) { + value = 0; + typeName = "BOOL"; + }; +}; \ No newline at end of file diff --git a/addons/spectator/CfgEventHandlers.hpp b/addons/spectator/CfgEventHandlers.hpp new file mode 100644 index 0000000000..e75956f440 --- /dev/null +++ b/addons/spectator/CfgEventHandlers.hpp @@ -0,0 +1,11 @@ +class Extended_PreInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_FILE(XEH_preInit)); + }; +}; + +class Extended_PostInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_FILE(XEH_postInit)); + }; +}; diff --git a/addons/spectator/CfgVehicles.hpp b/addons/spectator/CfgVehicles.hpp new file mode 100644 index 0000000000..8faee1dca7 --- /dev/null +++ b/addons/spectator/CfgVehicles.hpp @@ -0,0 +1,53 @@ +class CfgVehicles { + class ACE_Module; + class ACE_ModuleSpectator: ACE_Module { + author = ECSTRING(common,ACETeam); + category = "ACE"; + displayName = CSTRING(Module_DisplayName); + function = QFUNC(moduleSpectator); + scope = 2; + isGlobal = 1; + icon = PATHTOF(UI\Icon_Module_Spectator_ca.paa); + class Arguments { + class SpectatorEnabled { + displayName = CSTRING(Enabled_DisplayName); + description = CSTRING(Enabled_Description); + typeName = "BOOL"; + defaultValue = 0; + }; + class SpectatorPlayerSide { + displayName = CSTRING(PlayerSide_DisplayName); + description = CSTRING(PlayerSide_Description); + typeName = "BOOL"; + defaultValue = 0; + }; + class SpectatorAI { + displayName = CSTRING(AI_DisplayName); + description = CSTRING(AI_Description); + typeName = "BOOL"; + defaultValue = 0; + }; + class SpectatorTracking { + displayName = CSTRING(Tracking_DisplayName); + description = CSTRING(Tracking_Description); + typeName = "BOOL"; + defaultValue = 0; + }; + class SpectatorPos { + displayName = CSTRING(Pos_DisplayName); + description = CSTRING(Pos_Description); + typeName = "BOOL"; + defaultValue = 0; + }; + class SpectatorEnd { + displayName = CSTRING(End_DisplayName); + description = CSTRING(End_Description); + typeName = "BOOL"; + defaultValue = 0; + }; + }; + class ModuleDescription { + description = CSTRING(Module_Description); + }; + }; +}; \ No newline at end of file diff --git a/addons/spectator/README.md b/addons/spectator/README.md new file mode 100644 index 0000000000..180f21c57c --- /dev/null +++ b/addons/spectator/README.md @@ -0,0 +1,10 @@ +ace_spectator +======= + +Spectator. Includes features from Splendid Cam, and much more. + +## Maintainers + +The people responsible for merging changes to this component or answering potential questions. + +- [voiper](https://github.com/voiperr) \ No newline at end of file diff --git a/addons/spectator/UI.hpp b/addons/spectator/UI.hpp new file mode 100644 index 0000000000..eec38066f9 --- /dev/null +++ b/addons/spectator/UI.hpp @@ -0,0 +1,640 @@ +#define PIXEL_X (safeZoneWAbs / (getResolution select 0)) +#define PIXEL_Y (safeZoneH / (getResolution select 1)) +#define XHAIR RESUNITS_X * 4 +#define COMPASS_W RESUNITS_X * 20 +#define COMPASS_H COMPASS_W / 15 +#define COMPASS_X RESCENTRE_X - COMPASS_W / 2 +#define HELP_W RESUNITS_X * 75 +#define HELP_H RESUNITS_Y * 75 + +class ace_spectator_overlay { + + idd = 12200; + enableSimulation = 1; + movingEnable = 1; + enableDisplay = 1; + onLoad = "uiNamespace setVariable ['ace_spectator_overlay', _this select 0]; ['Init', _this] call ace_spectator_fnc_overlay"; + + class controls { + + class Unitlist { + + access = 0; + idc = 0; + type = CT_TREE; + style = ST_LEFT; + default = 0; + blinkingPeriod = 0; + + x = QUOTE(safeZoneX); + y = QUOTE(safeZoneY + RESUNITS_X * 4/3); + w = QUOTE(RESUNITS_X * 30); + h = QUOTE(RESUNITS_Y * 50); + + colorBorder[] = {1,1,1,1}; + + colorBackground[] = {0.1,0.1,0.1,1}; + colorSelect[] = {1,0.5,0,1}; + colorMarked[] = {1,0.5,0,0.5}; + colorMarkedSelected[] = {1,0.5,0,1}; + + sizeEx = QUOTE(RESUNITS_Y * 2); + font = GUI_FONT_NORMAL; + shadow = 1; + colorText[] = {1,1,1,1}; + colorSelectText[] = {1,1,1,1}; + colorMarkedText[] = {1,1,1,1}; + + tooltip = ""; + tooltipColorShade[] = {0,0,0,1}; + tooltipColorText[] = {1,1,1,1}; + tooltipColorBox[] = {1,1,1,1}; + + multiselectEnabled = 0; + expandOnDoubleclick = 0; + hiddenTexture = "A3\ui_f\data\gui\rsccommon\rsctree\hiddenTexture_ca.paa"; + expandedTexture = "A3\ui_f\data\gui\rsccommon\rsctree\expandedTexture_ca.paa"; + maxHistoryDelay = 1; + + class ScrollBar { + width = 0; + height = 0; + scrollSpeed = 0.01; + + arrowEmpty = "\A3\ui_f\data\gui\cfg\scrollbar\arrowEmpty_ca.paa"; + arrowFull = "\A3\ui_f\data\gui\cfg\scrollbar\arrowFull_ca.paa"; + border = "\A3\ui_f\data\gui\cfg\scrollbar\border_ca.paa"; + thumb = "\A3\ui_f\data\gui\cfg\scrollbar\thumb_ca.paa"; + + color[] = {1,1,1,1}; + }; + + colorDisabled[] = {0,0,0,0}; + colorArrow[] = {0,0,0,0}; + + onDestroy = QUOTE(GVAR(mouseBusy) = false; false); + onMouseEnter = QUOTE(GVAR(mouseBusy) = true; false); + onMouseExit = QUOTE(GVAR(mouseBusy) = false; false); + + onTreeDblClick = "['Select', _this] call ace_spectator_fnc_overlay; false"; + }; + }; +}; + +class ace_spectator_vd { + idd = 12201; + enableSimulation = 1; + enableDisplay = 0; + movingEnable = 0; + + onLoad = "uiNamespace setVariable ['ace_spectator_vd', _this select 0]; ['Init', _this] call ace_spectator_fnc_viewDistance"; + + class Controls { + + class BG: vip_rsc_box { + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 30); + y = QUOTE(safeZoneY + COMPASS_H); + w = QUOTE(RESUNITS_X * 30); + h = QUOTE(COMPASS_H); + colorBackground[] = {0.1,0.1,0.1,1}; + onDestroy = QUOTE(GVAR(mouseBusy) = false; false); + }; + + class TitleFrame: vip_rsc_frame { + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 30); + y = QUOTE(safeZoneY + COMPASS_H); + w = QUOTE(RESUNITS_X * 8); + h = QUOTE(COMPASS_H); + shadow = 2; + colorText[]={1,1,1,1}; + }; + + class Title: vip_rsc_text { + style = ST_CENTER; + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 30); + y = QUOTE(safeZoneY + COMPASS_H); + w = QUOTE(RESUNITS_X * 8); + h = QUOTE(COMPASS_H); + sizeEx = QUOTE(RESUNITS_Y * 2); + font = GUI_FONT_NORMAL; + shadow = 2; + colorText[]={1,1,1,1}; + text = CSTRING(VD_Title); + }; + + class DistanceFrame: TitleFrame { + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 10.5); + w = QUOTE(RESUNITS_X * 5); + }; + + class Distance: Title { + idc = 1; + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 10.5); + w = QUOTE(RESUNITS_X * 5); + text = ""; + }; + + class ButtonExit: vip_rsc_button { + idc = 0; + style = ST_CENTER; + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 5); + y = QUOTE(safeZoneY + COMPASS_H); + w = QUOTE(RESUNITS_X * 5); + h = QUOTE(COMPASS_H); + + colorBackground[] = {1,1,1,1}; + sizeEx = QUOTE(RESUNITS_Y * 2); + font = GUI_FONT_NORMAL; + + text = CSTRING(VD_Button); + + onButtonClick = "closeDialog 0; false"; + onMouseButtonDown = QUOTE(GVAR(mouseBusy) = true; false); + onMouseButtonUp = QUOTE(GVAR(mouseBusy) = false; false); + }; + + class Slider { + idc = 2; + + type = CT_XSLIDER; + style = SL_HORZ; + shadow = 2; + + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 21.5); + y = QUOTE(safeZoneY + COMPASS_H); + w = QUOTE(RESUNITS_X * 9.5); + h = QUOTE(COMPASS_H); + color[] = {1,1,1,1}; + colorActive[] = {1,1,1,1}; + colorDisabled[] = {1,1,1,0.2}; + arrowEmpty = "\A3\ui_f\data\gui\cfg\slider\arrowEmpty_ca.paa"; + arrowFull = "\A3\ui_f\data\gui\cfg\slider\arrowFull_ca.paa"; + border = "\A3\ui_f\data\gui\cfg\slider\border_ca.paa"; + thumb = "\A3\ui_f\data\gui\cfg\slider\thumb_ca.paa"; + + text = ""; + onSliderPosChanged = "['Slider', _this] call ace_spectator_fnc_viewDistance"; + onMouseButtonDown = QUOTE(GVAR(mouseBusy) = true; false); + onMouseButtonUp = QUOTE(GVAR(mouseBusy) = false; false); + }; + }; +}; + +class ace_spectator_map { + + idd = 12202; + enableSimulation = 1; + enableDisplay = 0; + onLoad = "uiNameSpace setVariable ['ace_spectator_map', _this select 0]; ['Init', _this select 0] call ace_spectator_fnc_map"; + onUnload = "['Close', _this select 0] call ace_spectator_fnc_map"; + onKeyDown = "['KeyDown', _this] call ace_spectator_fnc_map"; + + class controls { + //changes stolen from ACE_map + class Map { + access = 0; + idc = 1; + type = CT_MAP_MAIN; + style = ST_PICTURE; + default = 0; + blinkingPeriod = 0; + + x = safeZoneXAbs; + y = safeZoneY; + w = safeZoneWAbs; + h = safeZoneH; + + sizeEx = GUI_GRID_CENTER_H; + font = GUI_FONT_NORMAL; + colorText[] = {0,0,0,1}; + text = "#(argb,8,8,3)color(1,1,1,1)"; + + moveOnEdges = 1; + + ptsPerSquareSea = 5; + ptsPerSquareTxt = 20; + ptsPerSquareCLn = 10; + ptsPerSquareExp = 10; + ptsPerSquareCost = 10; + + ptsPerSquareFor = 9; + ptsPerSquareForEdge = 9; + ptsPerSquareRoad = 6; + ptsPerSquareObj = 9; + + scaleMin = 0.001; + scaleMax = 1.0; + scaleDefault = 0.16; + + alphaFadeStartScale = 2; + alphaFadeEndScale = 2; + maxSatelliteAlpha = 0.5; + + colorBackground[] = {0.929412, 0.929412, 0.929412, 1.0}; + colorOutside[] = {0.929412, 0.929412, 0.929412, 1.0}; + colorSea[] = {0.4,0.6,0.8,0.5}; + colorForest[] = {0.6, 0.8, 0.2, 0.25}; + colorForestBorder[] = {0.6,0.8,0.4,1}; + colorRocks[] = {0.50, 0.50, 0.50, 0.50}; + colorRocksBorder[] = {0,0,0,1}; + colorLevels[] = {0.0, 0.0, 0.0, 1.0}; + colorMainCountlines[] = {0.858824, 0, 0,1}; + colorCountlines[] = {0.647059, 0.533333, 0.286275, 1}; + colorMainCountlinesWater[] = {0.5,0.6,0.7,0.6}; + colorCountlinesWater[] = {0.5,0.6,0.7,0.3}; + colorPowerLines[] = {0.1,0.1,0.1,1}; + colorRailWay[] = {0.8,0.2,0,1}; + colorNames[] = {1.1,0.1,1.1,0.9}; + colorInactive[] = {1,1,0,0.5}; + colorTracks[] = {0.2,0.13,0,1}; + colorTracksFill[] = {1,0.88,0.65,0.3}; + colorRoads[] = {0.2,0.13,0,1}; + colorRoadsFill[] = {1,0.88,0.65,1}; + colorMainRoads[] = {0.0,0.0,0.0,1}; + colorMainRoadsFill[] = {0.94,0.69,0.2,1}; + colorGrid[] = {0.05,0.1,0,0.6}; + colorGridMap[] = {0.05,0.1,0,0.4}; + + fontLabel="PuristaMedium"; + sizeExLabel="( ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8)"; + fontGrid="TahomaB"; + sizeExGrid = 0.032; + fontUnits="TahomaB"; + sizeExUnits="( ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8)"; + fontNames="EtelkaNarrowMediumPro"; + sizeExNames="( ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8) * 2"; + fontInfo="PuristaMedium"; + sizeExInfo="( ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8)"; + fontLevel="TahomaB"; + sizeExLevel=0.03; + showCountourInterval = 1; + + class Task { + icon = "#(argb,8,8,3)color(1,1,1,1)"; + color[] = {1,1,0,1}; + + iconCreated = "#(argb,8,8,3)color(1,1,1,1)"; + colorCreated[] = {0,0,0,1}; + + iconCanceled = "#(argb,8,8,3)color(1,1,1,1)"; + colorCanceled[] = {0,0,0,0.5}; + + iconDone = "#(argb,8,8,3)color(1,1,1,1)"; + colorDone[] = {0,1,0,1}; + + iconFailed = "#(argb,8,8,3)color(1,1,1,1)"; + colorFailed[] = {1,0,0,1}; + + size = 8; + importance = 1; + coefMin = 1; + coefMax = 1; + }; + class ActiveMarker { //includes icons spawned by drawIcon + color[] = {0,0,0,1}; + size = 2; + coefMin = 1; //make sure icon doesnt scale + }; + class Waypoint { + coefMax = 1; + coefMin = 4; + color[] = {0,0,0,1}; + icon = "#(argb,8,8,3)color(0,0,0,1)"; + importance = 1; + size = 2; + }; + class WaypointCompleted: Waypoint{}; + class CustomMark: Waypoint{}; + class Command: Waypoint{}; + class Bush { + icon = ""; + color[] = {0.450000, 0.640000, 0.330000, 0.0}; + size = 14; + importance = "0.2 * 14 * 0.05"; + coefMin = 0.250000; + coefMax = 4; + }; + class Rock: Waypoint{color[]={0.45,0.64,0.33,0.4}; importance="0.5 * 12 * 0.05";}; + class SmallTree { + icon = ""; + color[] = {0.450000, 0.640000, 0.330000, 0.0}; + size = 12; + importance = "0.6 * 12 * 0.05"; + coefMin = 0.250000; + coefMax = 4; + }; + class Tree { + icon = ""; + color[] = {0.450000, 0.640000, 0.330000, 0.0}; + size = 12; + importance = "0.9 * 16 * 0.05"; + coefMin = 0.250000; + coefMax = 4; + }; + class Legend { + x = SafeZoneX+SafeZoneW-.340; + y = SafeZoneY+SafeZoneH-.152; + font = "PuristaMedium"; + w = .340; + h = .152; + sizeEx = 0.039210; + colorBackground[] = {0.906000, 0.901000, 0.880000, 0.5}; + color[] = {0, 0, 0, 0.75}; + }; + class BusStop: Waypoint{}; + class FuelStation: Waypoint{}; + class Hospital: Waypoint{}; + class Church: Waypoint{}; + class Lighthouse: Waypoint{}; + class Power: Waypoint{}; + class PowerSolar: Waypoint{}; + class PowerWave: Waypoint{}; + class PowerWind: Waypoint{}; + class Quay: Waypoint{}; + class Transmitter: Waypoint{}; + class Watertower: Waypoint{}; + class Cross: Waypoint{}; + class Chapel: Waypoint{}; + class Shipwreck: Waypoint{}; + class Bunker: Waypoint{}; + class Fortress: Waypoint{}; + class Fountain: Waypoint{}; + class Ruin: Waypoint{}; + class Stack: Waypoint{}; + class Tourism: Waypoint{}; + class ViewTower: Waypoint{}; + }; + }; +}; + +class RscTitles { + class ace_spectator_crosshair { + + onLoad = "uiNamespace setVariable ['ace_spectator_crosshair', _this select 0]"; + + idd=-1; + movingEnable=0; + fadein=0; + fadeout=0; + duration=1e+011; + + class controls { + + class X: vip_rsc_picture { + idc = 0; + x = QUOTE(RESCENTRE_X - XHAIR / 2); + y = QUOTE(RESCENTRE_Y - XHAIR * 4/3 / 2); + w = QUOTE(XHAIR); + h = QUOTE(XHAIR * 4/3); + text = "\a3\ui_f\data\IGUI\Cfg\Cursors\select_target_ca.paa"; + colorText[] = {1,1,1,0.8}; + }; + }; + }; + + class ace_spectator_status { + + onLoad = "uiNamespace setVariable ['ace_spectator_status', _this select 0]; [_this select 0] call ace_spectator_fnc_status"; + idd = -1; + movingEnable=0; + fadein=0; + fadeout=0; + duration=1e+011; + + class controls { + + class BGRight: vip_rsc_box { + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 30); + y = QUOTE(safeZoneY); + w = QUOTE(RESUNITS_X * 30); + h = QUOTE(COMPASS_H); + colorBackground[] = {0.1,0.1,0.1,1}; + }; + + class BGLeft: BGRight { + x = QUOTE(safeZoneX); + }; + + class SpeedFrame: vip_rsc_frame { + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 5); + y = QUOTE(safeZoneY); + w = QUOTE(RESUNITS_X * 5); + h = QUOTE(COMPASS_H); + shadow = 2; + colorText[]={1,1,1,1}; + }; + + class Speed: vip_rsc_text { + idc = 0; + style = ST_CENTER; + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 5); + y = QUOTE(safeZoneY); + w = QUOTE(RESUNITS_X * 5); + h = QUOTE(COMPASS_H); + colorText[]={1,1,1,1}; + sizeEx = QUOTE(RESUNITS_Y * 2); + font = GUI_FONT_NORMAL; + text = ""; + }; + + class FovFrame: SpeedFrame { + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 10.5); + }; + + class Fov: Speed { + idc = 4; + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 10.5); + }; + + class TimeAccFrame: SpeedFrame { + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 21.5); + }; + + class TimeAcc: Speed { + idc = 5; + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 21.5); + }; + + class FocusFrame: SpeedFrame { + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 16); + }; + + class Focus: Speed { + idc = 6; + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 16); + }; + + class NameFrame: SpeedFrame { + x = QUOTE(safeZoneX); + w = QUOTE(RESUNITS_X * 24.5); + }; + + class Name: Speed { + idc = 1; + x = QUOTE(safeZoneX); + w = QUOTE(RESUNITS_X * 24.5); + }; + + class ModeFrame: SpeedFrame { + x = QUOTE(safeZoneX + RESUNITS_X * 25); + }; + + class Mode: Speed { + idc = 2; + x = QUOTE(safeZoneX + RESUNITS_X * 25); + }; + + class TimeFrame: SpeedFrame { + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 30); + w = QUOTE(RESUNITS_X * 8); + }; + + class Time: Speed { + idc = 3; + x = QUOTE(safeZoneX + safeZoneW - RESUNITS_X * 30); + w = QUOTE(RESUNITS_X * 8); + }; + }; + }; + + class ace_spectator_compass { + + onLoad = "uiNamespace setVariable ['ace_spectator_compass', _this select 0]"; + onUnload = ""; + idd=-1; + movingEnable=0; + fadein=0; + fadeout=0; + duration=1e+011; + + class controls { + + class BG: vip_rsc_box { + x = QUOTE(COMPASS_X); + y = QUOTE(safeZoneY); + w = QUOTE(COMPASS_W); + h = QUOTE(COMPASS_H); + colorBackground[] = {0.1,0.1,0.1,1}; + }; + + class 0_90: vip_rsc_picture { + idc = 1; + x = QUOTE(RESCENTRE_X); + y = QUOTE(safeZoneY); + w = QUOTE(COMPASS_W / 2); + h = QUOTE(COMPASS_H); + text = "A3\ui_f_curator\data\cfgIngameUI\compass\texture180_ca.paa"; + }; + + class 90_180: 0_90 { + idc = 2; + x = QUOTE(RESCENTRE_X + COMPASS_W / 2); + text = "A3\ui_f_curator\data\cfgIngameUI\compass\texture270_ca.paa"; + }; + + class 180_270: 0_90 { + idc = 3; + x = QUOTE(RESCENTRE_X + COMPASS_W); + text = "A3\ui_f_curator\data\cfgIngameUI\compass\texture0_ca.paa"; + }; + + class 270_0: 0_90 { + idc = 4; + x = QUOTE(RESCENTRE_X + COMPASS_W * 1.5); + text = "A3\ui_f_curator\data\cfgIngameUI\compass\texture90_ca.paa"; + }; + + class Post: vip_rsc_box { + x = QUOTE(COMPASS_X + COMPASS_W / 2); + y = QUOTE(safeZoneY); + w = QUOTE(PIXEL_X * 2); + h = QUOTE(COMPASS_H); + colorBackground[]={1,0,0,1}; + }; + + class LeftBlocker: vip_rsc_box { + x = QUOTE(COMPASS_X - COMPASS_W / 2); + y = QUOTE(safeZoneY); + w = QUOTE(COMPASS_W / 2); + h = QUOTE(COMPASS_H); + colorBackground[] = {0.1,0.1,0.1,1}; + }; + + class RightBlocker: LeftBlocker { + x = QUOTE(COMPASS_X + COMPASS_W); + }; + + class Frame: vip_rsc_frame { + x = QUOTE(COMPASS_X); + y = QUOTE(safeZoneY); + w = QUOTE(COMPASS_W); + h = QUOTE(COMPASS_H); + shadow=2; + colorText[]={1,1,1,1}; + }; + }; + }; + + class ace_spectator_help { + + onLoad = "uiNamespace setVariable ['ace_spectator_help', _this select 0]; ['Help', _this select 0] call ace_spectator_fnc_camera"; + idd = -1; + movingEnable=0; + fadein=0; + fadeout=0; + duration=1e+011; + + class controls { + + class BG: vip_rsc_box { + idc = -1; + x = QUOTE(RESCENTRE_X - HELP_W / 2); + y = QUOTE(RESCENTRE_Y - HELP_H / 2); + w = QUOTE(HELP_W); + h = QUOTE(HELP_H); + colorBackground[] = {0.1,0.1,0.1,1}; + }; + + class Title: vip_rsc_text { + idc = 0; + style = ST_CENTER; + x = QUOTE(RESCENTRE_X - RESUNITS_X * 25); + y = QUOTE(RESCENTRE_Y - (HELP_H / 2) + RESUNITS_Y * 3); + w = QUOTE(RESUNITS_X * 50); + h = QUOTE(RESUNITS_Y * 4); + colorText[]={1,1,1,1}; + sizeEx = QUOTE(RESUNITS_Y * 4); + font = GUI_FONT_NORMAL; + text = "ACE Spectator Controls"; + }; + + class LeftColumn1 { + idc = 1; + type = CT_STRUCTURED_TEXT; + style = ST_LEFT; + x = QUOTE(RESCENTRE_X - HELP_W / 2 + RESUNITS_X * 3); + y = QUOTE(RESCENTRE_Y - (HELP_H / 2) + RESUNITS_Y * 10); + w = QUOTE(RESUNITS_X * 16.75); + h = QUOTE(RESUNITS_Y * 63); + text = ""; + size = QUOTE(RESUNITS_Y * 2.5); + colorBackground[] = {0,0,0,0}; + }; + + class LeftColumn2: LeftColumn1 { + idc = 2; + x = QUOTE(RESCENTRE_X - HELP_W / 2 + RESUNITS_X * 19.75); + }; + + class RightColumn1: LeftColumn1 { + idc = 3; + x = QUOTE(RESCENTRE_X + HELP_W / 2 - RESUNITS_X * 3 - RESUNITS_X * 29.5); + }; + + class RightColumn2: LeftColumn1 { + idc = 4; + x = QUOTE(RESCENTRE_X + HELP_W / 2 - RESUNITS_X * 3 - RESUNITS_X * 11.75); + }; + }; + }; +}; \ No newline at end of file diff --git a/addons/spectator/UI/Icon_Module_Spectator_ca.paa b/addons/spectator/UI/Icon_Module_Spectator_ca.paa new file mode 100644 index 0000000000..a4d784cea6 Binary files /dev/null and b/addons/spectator/UI/Icon_Module_Spectator_ca.paa differ diff --git a/addons/spectator/XEH_postInit.sqf b/addons/spectator/XEH_postInit.sqf new file mode 100644 index 0000000000..b57d08ac7b --- /dev/null +++ b/addons/spectator/XEH_postInit.sqf @@ -0,0 +1,96 @@ +/* + Author: + voiper +*/ + +#include "script_component.hpp" + +["SettingsInitialized", { + if !GVAR(enabled) exitWith {}; + + //check if respawn is set up properly + _fail = if (getNumber (missionConfigFile >> "respawn") != 3 && getText (missionConfigFile >> "respawn") != "Base") then {true} else {false}; + if (_fail) exitWith { + _text = "[ACE_Spectator] ERROR: This mission does not have respawn set up properly. Add 'respawn=3' or 'respawn=""BASE""' to description.ext."; + systemChat _text; + diag_log text _text; + }; + + if GVAR(endMission) then { + [{ + if (player distance GVAR(penPos) < 200) then { + if ({isPlayer _x && alive _x && (_x distance GVAR(penPos)) > 200} count allUnits == 0) then { + [["endDeath", false], "BIS_fnc_endMission"] call BIS_fnc_MP; + [_this select 1] call CBA_fnc_removePerFrameHandler; + }; + }; + }, 2] call CBA_fnc_addPerFrameHandler; + }; + + if (isDedicated) exitWith {}; + + call FUNC(penPos); + + { + if (getMarkerPos _x isEqualTo [0,0,0]) then { + _marker = createMarkerLocal [_x, [0,0]]; + _marker setMarkerShapeLocal "ICON"; + }; + + _x setMarkerPosLocal GVAR(penPos); + } forEach ["respawn_west", "respawn_east", "respawn_guerrila", "respawn_civilian"]; + + GVAR(playerSide) = side (group player); + + if GVAR(tracking) then { + [FUNC(checkUnits), 2] call CBA_fnc_addPerFrameHandler; + [FUNC(trackUnits), 20] call CBA_fnc_addPerFrameHandler; + }; + + player addEventHandler ["Killed", { + [player] joinSilent grpNull; + if (isClass (configFile >> "CfgPatches" >> "ace_hearing")) then {EGVAR(hearing,disableVolumeUpdate) = true}; + _delay = getNumber (missionConfigFile >> "respawnDelay"); + _delay fadeSound 0; + 999999 cutText ["", "BLACK", _delay]; + }]; + + player addEventHandler ["Respawn", { + if (!isNil QGVAR(cam)) then {["Exit"] call FUNC(camera)}; + if (isClass (configFile >> "CfgPatches" >> "ace_hearing")) then {EGVAR(hearing,disableVolumeUpdate) = true}; + if (isClass (configFile >> "CfgPatches" >> "acre_sys_radio")) then {[true] call acre_api_fnc_setSpectator}; + if (isClass (configFile >> "CfgPatches" >> "task_force_radio")) then {[player, true] call TFAR_fnc_forceSpectator}; + + if !GVAR(modulePos) then { + _corpse = _this select 1; + if (!isNil "_corpse") then { + if (!isNull _corpse) then { + GVAR(startingPos) = getPosATL _corpse; + }; + }; + }; + + player addEventHandler ["HandleDamage", {0}]; + [player] joinSilent grpNull; + removeAllWeapons player; + removeAllItems player; + removeAllAssignedItems player; + removeUniform player; + removeVest player; + player linkItem "ItemMap"; + player linkItem "ItemRadio"; + hideObjectGlobal player; + + if (surfaceisWater GVAR(penPos)) then { + player forceAddUniform "U_B_Wetsuit"; + player addVest "V_RebreatherB"; + }; + + player setPosATL GVAR(penPos); + + 0 fadeSound 0; + 999999 cutText ["", "BLACK FADED", 0]; + ["Init", [true]] call FUNC(camera); + }]; + +}] call EFUNC(common,addEventHandler); \ No newline at end of file diff --git a/addons/spectator/XEH_preInit.sqf b/addons/spectator/XEH_preInit.sqf new file mode 100644 index 0000000000..41b24e8fcc --- /dev/null +++ b/addons/spectator/XEH_preInit.sqf @@ -0,0 +1,31 @@ +#include "script_component.hpp" + +ADDON = false; + +PREP(camera); +PREP(cameraIntro); +PREP(canSpectateUnit); +PREP(checkUnits); +PREP(compass); +PREP(crosshair); +PREP(draw3D); +PREP(drawMines2D); +PREP(drawMines3D); +PREP(drawTracks2D); +PREP(drawUnits2D); +PREP(drawUnits3D); +PREP(killed); +PREP(map); +PREP(moduleSpectator); +PREP(overlay); +PREP(penPos); +PREP(respawn); +PREP(sideColour); +PREP(status); +PREP(trackUnits); +PREP(unitInfo); +PREP(unitSide); +PREP(unitVar); +PREP(viewDistance); + +ADDON = true; \ No newline at end of file diff --git a/addons/spectator/config.cpp b/addons/spectator/config.cpp new file mode 100644 index 0000000000..89b0b814b5 --- /dev/null +++ b/addons/spectator/config.cpp @@ -0,0 +1,19 @@ +#include "script_component.hpp" + +class CfgPatches { + class ADDON { + units[] = {}; + weapons[] = {}; + requiredVersion = REQUIRED_VERSION; + requiredAddons[] = {"ace_common"}; + author[] = {"voiper"}; + authorUrl = "https://github.com/voiperr/"; + VERSION_CONFIG; + }; +}; + +#include "ACE_Settings.hpp" +#include "CfgEventHandlers.hpp" +#include "CfgVehicles.hpp" +#include "rsc_defines.hpp" +#include "UI.hpp" \ No newline at end of file diff --git a/addons/spectator/functions/fnc_camera.sqf b/addons/spectator/functions/fnc_camera.sqf new file mode 100644 index 0000000000..853720893d --- /dev/null +++ b/addons/spectator/functions/fnc_camera.sqf @@ -0,0 +1,1081 @@ +/* + Author: + voiper, derived in part from BIS_fnc_camera by Karel Moricky + + Description: + Spectator camera and UI. + + Arguments: + 0: Mode: "Init" is the only mission relevant one + 1: (optional): + 0: Whether player can escape from camera (true for MP spectator; false for SP photography) + + Example: + ["Init", [false]] call ace_spectator_fnc_camera; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" +#include "\a3\editor_f\Data\Scripts\dikCodes.h" + +disableSerialization; +_mode = _this select 0; +_this = _this select 1; + +switch _mode do { + + case "Init": { + + GVAR(noEscape) = if (count _this > 0) then {_this select 0} else {false}; + + call FUNC(penPos); + + _camPos = if (!isNil QGVAR(startingPos)) then { + GVAR(startingPos) + } else { + getPos cameraOn + }; + + _camDir = if (!isNil QGVAR(startingDir)) then { + GVAR(startingDir) + } else { + 0 + }; + + _camPos set [2, (_camPos select 2) + 2]; + _cam = "camera" camCreate _camPos; + _cam setDir _camDir; + _cam cameraEffect ["internal", "back"]; + _cam camSetFocus [-1, -1]; + _cam camCommit 0; + showCinemaBorder false; + cameraEffectEnableHUD true; + setViewDistance 3000; + + //variables + GVAR(cam) = _cam; + GVAR(LMB) = false; + GVAR(RMB) = false; + GVAR(vector) = [_camDir, 0, 0]; + GVAR(fov) = 0.7; + GVAR(vision) = 0; + GVAR(moveScale) = 0.1; + GVAR(cameraOn) = true; + GVAR(focus) = [-1, -1]; + GVAR(lock) = [-1]; + GVAR(attach) = objNull; + GVAR(unit) = objNull; + GVAR(mouseBusy) = false; + GVAR(markers) = 3; + GVAR(accTime) = 1; + GVAR(third) = false; + + //define only if doesn't exist (to preserve saved spots from a previous camera) + if (isNil QGVAR(savedSpots)) then { + GVAR(savedSpots) = []; + for "_i" from 0 to 11 do {GVAR(savedSpots) set [_i, []]}; + }; + + if (isNil QGVAR(savedUnits)) then { + GVAR(savedUnits) = []; + for "_i" from 0 to 9 do {GVAR(savedUnits) set [_i, objNull]}; + }; + + GVAR(keys) = []; + _DIKcodes = true call BIS_fnc_keyCode; + _DIKlast = _DIKcodes select (count _DIKcodes - 1); + for "_i" from 0 to (_DIKlast - 1) do { + GVAR(keys) set [_i, false]; + }; + + _display = findDisplay 46; + + GVAR(ehDraw3D) = addMissionEventhandler ["Draw3D", {['Draw3D', _this] call FUNC(draw3D)}]; + addMissionEventHandler ["Ended", {if (!isNil QGVAR(cam)) then {["Exit"] call FUNC(camera)}}]; + GVAR(ehKeyDown) = _display displayAddEventHandler ["keyDown", {['KeyDown', _this] call FUNC(camera)}]; + GVAR(ehKeyUp) = _display displayAddEventHandler ["keyUp", {['KeyUp', _this] call FUNC(camera)}]; + GVAR(ehMouseButtonDown) = _display displayAddEventHandler ["mouseButtonDown", {['MouseButtonDown', _this] call FUNC(camera)}]; + GVAR(ehMouseButtonUp) = _display displayAddEventHandler ["mouseButtonUp", {['MouseButtonUp',_this] call FUNC(camera)}]; + GVAR(ehMouseZChanged) = _display displayAddEventHandler ["mouseZChanged", {['MouseZChanged',_this] call FUNC(camera)}]; + GVAR(ehMouseMoving) = _display displayAddEventHandler ["mouseMoving", {['Mouse',_this] call FUNC(camera)}]; + GVAR(ehMouseHolding) =_display displayAddEventHandler ["mouseHolding", {['Mouse',_this] call FUNC(camera)}]; + + //remove mission layer + _displayMission = call (uiNamespace getVariable "BIS_fnc_displayMission"); + _control = _displayMission displayCtrl 11400; + _control ctrlSetFade 1; + _control ctrlCommit 0; + + //kill layers + cutText ["", "Plain"]; + _layers = missionNamespace getVariable ["BIS_fnc_rscLayer_list", []]; + for "_i" from 1 to (count _layers - 1) step 2 do { + (_layers select _i) cutText ["", "Plain"]; + }; + + clearRadio; + + //crosshair + _layer = [QGVAR(crosshair)] call BIS_fnc_rscLayer; + _layer cutRsc [QGVAR(crosshair), "PLAIN", 2, true]; + + //compass + _layer = [QGVAR(compass)] call BIS_fnc_rscLayer; + _layer cutRsc [QGVAR(compass), "PLAIN", 2, true]; + + //status + _layer = [QGVAR(status)] call BIS_fnc_rscLayer; + _layer cutRsc [QGVAR(status), "PLAIN", 2, true]; + + //help + _layer = [QGVAR(help)] call BIS_fnc_rscLayer; + preloadTitleRsc [QGVAR(help), "PLAIN", 0, true]; + + if (isClass (configFile >> "CfgPatches" >> "ace_nametags")) then { + GVAR(tags) = [EGVAR(nametags,showPlayerNames), EGVAR(nametags,showNamesForAI)]; + EGVAR(nametags,showPlayerNames) = 0; + EGVAR(nametags,showNamesForAI) = false; + }; + + if (isClass (configFile >> "CfgPatches" >> "ace_interact_menu")) then { + [QGVAR(interactCondition), {false}] call EFUNC(common,addCanInteractWithCondition); + }; + + //add unit check, since if tracking were on it would already be present + if !GVAR(tracking) then { + [FUNC(checkUnits), 2] call CBA_fnc_addPerFrameHandler + }; + + [FUNC(cameraIntro), 1] call CBA_fnc_addPerFrameHandler; + }; + + ////////////////////////////////////////// + case "Mouse": { + _mapOn = uiNamespace getVariable QGVAR(map); + if (!isNull _mapOn) exitWith {}; + + _keys = GVAR(keys); + _cam = GVAR(cam); + _dir = GVAR(vector) select 0; + _pitch = GVAR(vector) select 1; + _bank = GVAR(vector) select 2; + _camPos = getPosASL _cam; + _coef = (GVAR(moveScale) * (((getPosATL _cam) select 2) / 2)) min 50 max 0.001; + + _move = { + _inPos = _this; + if !GVAR(cameraOn) exitWith {}; + if (_inPos select 2 > 20000) then {_inPos set [2, 20000]}; + _obj = GVAR(attach); + if !(isNull _obj) then { + if ((GVAR(lock) select 0) < 0) then { + _modelPos = _obj worldToModel (ASLtoATL _inPos); + _cam attachTo [_obj, _modelPos]; + }; + } else { + _cam setPosASL _inPos; + }; + }; + + if (GVAR(LMB) || GVAR(RMB)) then { + if GVAR(mouseBusy) exitWith {}; + _mX = (_this select 1) * (GVAR(accTime) max 0.05); + _mY = (_this select 2) * (GVAR(accTime) max 0.05); + + if GVAR(RMB) then { + + _dX = _mX; + _dY = -_mY; + + _camPos = [_camPos, _dY, getDir _cam] call BIS_fnc_relPos; + _camPos = [_camPos, _dX, getDir _cam + 90] call BIS_fnc_relPos; + + _camPos call _move; + + } else { + if (GVAR(lock) select 0 > -1) exitWith {}; + _dX = _mX / 50 * 180 * GVAR(fov); + _dY = -_mY / 50 * 180 * GVAR(fov); + + if (_keys select DIK_LSHIFT) then { + _pitch = (_pitch + _dY) max -180 min 180; + _bank = (_bank + _dX) max -181 min 181; + if (_bank <= -181) then {_bank = 180} else {if (_bank >= 181) then {_bank = -180}}; + } else { + _dir = _dir + _dX; + _pitch = (_pitch + _dY) max -90 min 90; + }; + GVAR(vector) = [_dir, _pitch, _bank]; + [_cam, GVAR(vector)] call BIS_fnc_setObjectRotation; + }; + }; + + _camMove = { + _dX = _this select 0; + _dY = _this select 1; + _dZ = _this select 2; + _pos = getPosASL _cam; + _moveDir = (getDir _cam) + _dX * 90; + _camPos = [ + (_pos select 0) + ((sin (_moveDir)) * _coef * _dY), + (_pos select 1) + ((cos (_moveDir)) * _coef * _dY), + (_pos select 2) + _dZ * _coef / 1.5 + ]; + //for some reason, at visual height = 0, cameras report 10cm higher than they actually are + _camPos set [2, (_camPos select 2) max (getTerrainHeightASL _camPos + 0.1)]; + + _camPos call _move; + }; + + _camRotate = { + if ((GVAR(lock) select 0) > -1) exitWith {}; + _dX = (_this select 0) * GVAR(fov) * _rotMod; + _dY = (_this select 1) * GVAR(fov) * _rotMod; + _pitch = ((GVAR(vector) select 1) + _dY) max -90 min 90; + _bank = GVAR(vector) select 2; + _dir = _dir + _dX; + GVAR(vector) = [_dir, _pitch, _bank]; + [_cam, GVAR(vector)] call BIS_fnc_setObjectRotation; + }; + + _camBank = { + if ((GVAR(lock) select 0) > -1) exitWith {}; + _dZ = (_this select 0) * _rotMod; + _pitch = GVAR(vector) select 1; + _bank = ((GVAR(vector) select 2) + _dZ) max -181 min 181; + if (_bank == -181) then {_bank = 180} else {if (_bank == 181) then {_bank = -180}}; + GVAR(vector) = [_dir, _pitch, _bank]; + [_cam, GVAR(vector)] call BIS_fnc_setObjectRotation; + }; + + _numPad0 = _keys select DIK_NUMPAD0; + _numPadDel = _keys select DIK_DECIMAL; + _rotMod = if (_numPad0 && !_numPadDel) then { + 5 + } else { + if (!_numPad0 && _numPadDel) then {0.1} else {1}; + }; + + if (_keys select DIK_W) then {[0,1,0] call _camMove}; + if (_keys select DIK_S) then {[0,-1,0] call _camMove}; + if (_keys select DIK_A) then {[-1,1,0] call _camMove}; + if (_keys select DIK_D) then {[1,1,0] call _camMove}; + + if (_keys select DIK_Q) then {[0,0,1] call _camMove}; + if (_keys select DIK_Z) then {[0,0,-1] call _camMove}; + + if (_keys select DIK_NUMPAD1) then {[-1,-1,0] call _camRotate}; + if (_keys select DIK_NUMPAD2) then {[+0,-1,0] call _camRotate}; + if (_keys select DIK_NUMPAD3) then {[+1,-1,0] call _camRotate}; + if (_keys select DIK_NUMPAD4) then {[-1,+0,0] call _camRotate}; + if (_keys select DIK_NUMPAD6) then {[+1,+0,0] call _camRotate}; + if (_keys select DIK_NUMPAD7) then {[-1,+1,0] call _camRotate}; + if (_keys select DIK_NUMPAD8) then {[+0,+1,0] call _camRotate}; + if (_keys select DIK_NUMPAD9) then {[+1,+1,0] call _camRotate}; + if (_keys select DIK_DIVIDE) then {[-1] call _camBank}; + if (_keys select DIK_MULTIPLY) then {[+1] call _camBank}; + + if (_keys select DIK_ADD) then { + GVAR(fov) = GVAR(fov) - (GVAR(fov) / 50 * _rotMod) max 0.01; + _cam camPrepareFOV GVAR(fov); + _cam camCommitPrepared 0; + }; + if (_keys select DIK_SUBTRACT) then { + GVAR(fov) = GVAR(fov) + (GVAR(fov) / 50 * _rotMod) min 2; + _cam camPrepareFOV GVAR(fov); + _cam camCommitPrepared 0; + }; + + if (_keys select DIK_NUMPADENTER) then { + GVAR(fov) = 0.7; + _cam camPrepareFOV GVAR(fov); + _cam camCommitPrepared 0; + }; + + if (_keys select DIK_MINUS) then { + _cur = GVAR(focus) select 0; + if (_cur < 0) then {_cur = 1}; + _cur = _cur - (_cur / 25) max 0.25; + GVAR(focus) = [_cur, 1.5]; + _cam camSetFocus GVAR(focus); + _cam camCommit 0; + }; + + if (_keys select DIK_EQUALS) then { + _cur = GVAR(focus) select 0; + if (_cur < 0) then {_cur = 1}; + _cur = _cur + (_cur / 25) min 5000; + GVAR(focus) = [_cur, 1.5]; + _cam camSetFocus GVAR(focus); + _cam camCommit 0; + }; + + if (_keys select DIK_LBRACKET)then { + if (!isMultiplayer) then { + _cur = GVAR(accTime); + _cur = _cur - (_cur / 25) max 0; + GVAR(accTime) = _cur; + setAccTime GVAR(accTime); + }; + }; + + if (_keys select DIK_RBRACKET)then { + if (!isMultiplayer) then { + _cur = GVAR(accTime); + _cur = _cur + (_cur / 25) min 4; + GVAR(accTime) = _cur; + setAccTime GVAR(accTime); + }; + }; + }; + + ////////////////////////////////////////// + case "MouseButtonDown": { + _mapOn = uiNamespace getVariable QGVAR(map); + if (!isNull _mapOn) exitWith {}; + + _button = _this select 1; + _mX = _this select 2; + _mY = _this select 3; + _shift = _this select 4; + _ctrl = _this select 5; + _alt = _this select 6; + + switch (_button) do { + case 0: {GVAR(LMB) = true}; + case 1: {GVAR(RMB) = true}; + }; + }; + + ////////////////////////////////////////// + case "MouseButtonUp": { + _mapOn = uiNamespace getVariable QGVAR(map); + if (!isNull _mapOn) exitWith {}; + + _button = _this select 1; + switch (_button) do { + case 0: {GVAR(LMB) = false}; + case 1: {GVAR(RMB) = false}; + }; + }; + + ////////////////////////////////////////// + case "MouseZChanged": { + _mapOn = uiNamespace getVariable QGVAR(map); + if (!isNull _mapOn) exitWith {}; + + _diff = _this select 1; + if (_diff > 0) then { + GVAR(moveScale) = GVAR(moveScale) + (GVAR(moveScale) / 10) min 1; + } else { + GVAR(moveScale) = GVAR(moveScale) - (GVAR(moveScale) / 10) max 0.001; + }; + }; + + ////////////////////////////////////////// + case "KeyDown": { + _key = _this select 1; + _shift = _this select 2; + _ctrl = _this select 3; + _alt = _this select 4; + _return = false; + + GVAR(keys) set [_key, true]; + + _cam = GVAR(cam); + _camOn = GVAR(cameraOn); + _unit = GVAR(unit); + _lock = GVAR(lock) select 0; + + _camPos = [getPos _cam, GVAR(vector), GVAR(fov), GVAR(focus)]; + + _camSaveSpot = { + _num = _this select 0; + if (!isNull GVAR(attach)) then { + _vector = _camPos select 1; + _dir = _vector select 0; + _vector set [0, _dir - (getDir GVAR(attach))]; + _camPos set [1, _vector]; + }; + GVAR(savedSpots) set [_num, _camPos]; + }; + + _camLoadSpot = { + _num = _this select 0; + _arr = GVAR(savedSpots) select _num; + if (count (_arr) > 0) then { + if (!_camOn) then { + ["Camera", ["Free"]] call FUNC(camera) + }; + call _detach; + _cam setPos (_arr select 0); + _vector = _arr select 1; + [_cam, _vector] call BIS_fnc_setObjectRotation; + _cam camPrepareFOV (_arr select 2); + _cam camPrepareFocus (_arr select 3); + _cam camCommitPrepared 0; + GVAR(vector) = _vector; + }; + }; + + _camSaveUnit = { + _num = _this select 0; + + if (!isNull _unit) then { + _alreadySaved = GVAR(savedUnits) find _unit; + if (_alreadySaved > -1) then { + GVAR(savedUnits) set [_alreadySaved, objNull]; + }; + GVAR(savedUnits) set [_num, _unit] + }; + }; + + _camLoadUnit = { + _num = _this select 0; + _unit = GVAR(savedUnits) select _num; + if (!isNull _unit) then { + if (_lock > -1) then { + ["Camera", ["Lock"]] call FUNC(camera) + }; + if (GVAR(unit) == _unit) then { + call _detach; + if (_camOn) then { + ["Camera", ["Third"]] call FUNC(camera); + } else { + ["Camera", ["SwitchUnit"]] call FUNC(camera); + }; + } else { + GVAR(unit) = _unit; + if ((GVAR(lock) select 0) > -1) then {["Camera", ["Lock"]] call FUNC(camera)}; + if (!_camOn) then { + call _detach; + ["Camera", ["SwitchUnit"]] call FUNC(camera); + }; + }; + }; + }; + + _detach = { + if (!isNull GVAR(attach)) then { + ["Camera", ["Attach"]] call FUNC(camera); + }; + }; + + switch (_key) do { + + case (DIK_F1): {if (_ctrl) then {[0] call _camSaveSpot} else {[0] call _camLoadSpot}; _return = true}; + case (DIK_F2): {if (_ctrl) then {[1] call _camSaveSpot} else {[1] call _camLoadSpot}; _return = true}; + case (DIK_F3): {if (_ctrl) then {[2] call _camSaveSpot} else {[2] call _camLoadSpot}; _return = true}; + case (DIK_F4): {if (_ctrl) then {[3] call _camSaveSpot} else {[3] call _camLoadSpot}; _return = true}; + case (DIK_F5): {if (_ctrl) then {[4] call _camSaveSpot} else {[4] call _camLoadSpot}; _return = true}; + case (DIK_F6): {if (_ctrl) then {[5] call _camSaveSpot} else {[5] call _camLoadSpot}; _return = true}; + case (DIK_F7): {if (_ctrl) then {[6] call _camSaveSpot} else {[6] call _camLoadSpot}; _return = true}; + case (DIK_F8): {if (_ctrl) then {[7] call _camSaveSpot} else {[7] call _camLoadSpot}; _return = true}; + case (DIK_F9): {if (_ctrl) then {[8] call _camSaveSpot} else {[8] call _camLoadSpot}; _return = true}; + case (DIK_F10): {if (_ctrl) then {[9] call _camSaveSpot} else {[9] call _camLoadSpot}; _return = true}; + case (DIK_F11): {if (_ctrl) then {[10] call _camSaveSpot} else {[10] call _camLoadSpot}; _return = true}; + case (DIK_F12): {if (_ctrl) then {[11] call _camSaveSpot} else {[11] call _camLoadSpot}; _return = true}; + + case (DIK_1): {if (_ctrl) then {[0] call _camSaveUnit} else {[0] call _camLoadUnit}; _return = true}; + case (DIK_2): {if (_ctrl) then {[1] call _camSaveUnit} else {[1] call _camLoadUnit}; _return = true}; + case (DIK_3): {if (_ctrl) then {[2] call _camSaveUnit} else {[2] call _camLoadUnit}; _return = true}; + case (DIK_4): {if (_ctrl) then {[3] call _camSaveUnit} else {[3] call _camLoadUnit}; _return = true}; + case (DIK_5): {if (_ctrl) then {[4] call _camSaveUnit} else {[4] call _camLoadUnit}; _return = true}; + case (DIK_6): {if (_ctrl) then {[5] call _camSaveUnit} else {[5] call _camLoadUnit}; _return = true}; + case (DIK_7): {if (_ctrl) then {[6] call _camSaveUnit} else {[6] call _camLoadUnit}; _return = true}; + case (DIK_8): {if (_ctrl) then {[7] call _camSaveUnit} else {[7] call _camLoadUnit}; _return = true}; + case (DIK_9): {if (_ctrl) then {[8] call _camSaveUnit} else {[8] call _camLoadUnit}; _return = true}; + case (DIK_0): {if (_ctrl) then {[9] call _camSaveUnit} else {[9] call _camLoadUnit}; _return = true}; + + case (DIK_NUMPAD5): { + _dir = getDir _cam; + if (!isNull GVAR(attach)) then {_dir = _dir - getDir GVAR(attach)}; + GVAR(vector) = [_dir, 0, 0]; + [_cam, GVAR(vector)] call BIS_fnc_setObjectRotation; + GVAR(fov) = 0.7; + _cam camPrepareFOV GVAR(fov); + _cam camCommitPrepared 0; + }; + + case (DIK_NUMPADENTER): {_return = true}; + + case (DIK_NUMPAD0): {_return = true}; + + case (DIK_DECIMAL): {_return = true}; + + case (DIK_BACKSPACE): { + GVAR(focus) = if (!_shift) then { + [-1, 1]; + } else { + [-1, -1]; + }; + _cam camPrepareFocus GVAR(focus); + _cam camCommitPrepared 0; + _return = true; + }; + + case (DIK_BACKSLASH): { + if (!isMultiplayer) then { + GVAR(accTime) = 1; + setAccTime GVAR(accTime); + }; + }; + + case (DIK_GRAVE): {_return = true}; + + case (DIK_SPACE): { + if (!_camOn) exitWith {}; + if (_ctrl) then { + ["Camera", ["Attach"]] call FUNC(camera); + } else { + ["Camera", ["Lock"]] call FUNC(camera); + }; + }; + + case (DIK_LEFT): { + ["Camera", ["NewUnit", -1]] call FUNC(camera) + }; + + case (DIK_RIGHT): { + ["Camera", ["NewUnit", 1]] call FUNC(camera) + }; + + case (DIK_UP): { + if (isNull GVAR(unit)) exitWith {}; + if (_lock > -1) then {["Camera", ["Lock"]] call FUNC(camera)}; + call _detach; + if (_camOn) then { + ["Camera", ["Third"]] call FUNC(camera); + } else { + if GVAR(third) then { + ["Camera", ["First"]] call FUNC(camera); + }; + }; + }; + + case (DIK_DOWN): { + if (isNull GVAR(unit)) exitWith {}; + if (_lock > -1) then {["Camera", ["Lock"]] call FUNC(camera)}; + call _detach; + if (!_camOn) then { + if !GVAR(third) then { + ["Camera", ["Third"]] call FUNC(camera); + } else { + ["Camera", ["Free"]] call FUNC(camera); + }; + }; + }; + + case (DIK_T): { + GVAR(markers) = GVAR(markers) + 1; + if (GVAR(markers) > 3) then {GVAR(markers) = 0}; + if (GVAR(markers) == 0) then {clearRadio}; + }; + + case (DIK_U): { + _map = uiNameSpace getVariable [QGVAR(map), findDisplay 12202]; + if (!isNull _map) exitWith {}; + + _overlay = uiNamespace getVariable [QGVAR(overlay), findDisplay 12200]; + if (isNull _overlay) then { + createDialog QGVAR(overlay); + } else { + closeDialog 0; + }; + }; + + case (DIK_X): { + _layer = [QGVAR(crosshair)] call BIS_fnc_rscLayer; + _xhair = uiNamespace getVariable QGVAR(crosshair); + if (isNull _xhair) then { + _layer cutRsc [QGVAR(crosshair), "PLAIN", 0, true]; + call FUNC(crosshair); + } else { + _layer cutText ["", "PLAIN"]; + }; + }; + + case (DIK_C): { + _layer = [QGVAR(compass)] call BIS_fnc_rscLayer; + if (isNull (uiNamespace getVariable QGVAR(compass))) then { + _layer cutRsc [QGVAR(compass), "PLAIN", 0, true]; + } else { + _layer cutText ["", "PLAIN"]; + }; + + _layer = [QGVAR(status)] call BIS_fnc_rscLayer; + if (isNull (uiNamespace getVariable QGVAR(status))) then { + _layer cutRsc [QGVAR(status), "PLAIN", 0, true]; + } else { + _layer cutText ["", "PLAIN"]; + }; + }; + + case (DIK_G): { + _vd = uiNamespace getVariable [QGVAR(vd), findDisplay 12201]; + if (isNull _vd) then { + createDialog QGVAR(vd); + } else { + closeDialog 0; + } + }; + + case (DIK_H): { + _layer = [QGVAR(help)] call BIS_fnc_rscLayer; + if (isNull (uiNamespace getVariable QGVAR(help))) then { + _layer cutRsc [QGVAR(help), "PLAIN", 0, true]; + } else { + _layer cutText ["", "PLAIN"]; + }; + }; + + case (DIK_M): { + _map = uiNameSpace getVariable [QGVAR(map), findDisplay 12202]; + if (isNull _map) then { + createDialog QGVAR(map); + } else { + closeDialog 0; + }; + }; + + case (DIK_N): { + GVAR(vision) = GVAR(vision) + 1; + if (GVAR(vision) > 4) then {GVAR(vision) = 0}; + switch GVAR(vision) do { + case 0: { + camUseNVG false; + false SetCamUseTi 0; + }; + case 1: { + camUseNVG true; + false SetCamUseTi 0; + }; + case 2: { + camUseNVG false; + true SetCamUseTi 0; + }; + case 3: { + camUseNVG false; + true SetCamUseTi 1; + }; + + case 4: { + camUseNVG false; + true SetCamUseTi 4; + }; + }; + }; + + case (DIK_ESCAPE): { + if !GVAR(noEscape) then { + _return = true; + _this spawn { + disableSerialization; + _display = _this select 0; + _message = ["Do you want to exit spectator?", "ACE Spectator", nil, true, _display] call BIS_fnc_guiMessage; + if (_message) then {["Exit"] call FUNC(camera)}; + }; + }; + }; + default {}; + }; + + _return + }; + + ////////////////////////////////////////// + case "KeyUp": { + GVAR(keys) set [_this select 1, false]; + }; + + ////////////////////////////////////////// + case "Camera": { + + _mode = _this select 0; + + _cam = GVAR(cam); + _camOn = GVAR(cameraOn); + _unit = GVAR(unit); + _lock = GVAR(lock) select 0; + + _findTarget = { + + _ret = []; + _screenPos = screenToWorld [0.5,0.5]; + _camPosASL = getPosASL _cam; + _camPosReal = getPos _cam; + _endPosASL = [_screenPos select 0, _screenPos select 1, getTerrainHeightASL _screenPos]; + _endPosReal = if (surfaceIsWater _endPosASL) then {_endPosASL} else {ASLtoATL _endPosASL}; + _objs = lineIntersectsWith [_camPosASL, _endPosASL, objNull, objNull, true]; + + if (count _objs > 0) then { //if vehicle/object found + _obj = _objs select (count _objs - 1); + _ret = _obj; + } else { //check for units near endpoint instead + _units = allUnits; + if (count _units > 0) then { + _nearestUnit = _units select 0; + {if (_endPosReal distance _x < _endPosReal distance _nearestUnit) then {_nearestUnit = _x}} forEach _units; + _intersect = [_nearestUnit, "FIRE"] intersect [_camPosReal, _endPosReal]; + if (count (_intersect) > 0) then { + _ret = _nearestUnit; + } else { //check for units near camera instead + _nearestUnit = _units select 0; + {if (_cam distance _x < _cam distance _nearestUnit) then {_nearestUnit = _x}} forEach _units; + _intersect = [_nearestUnit, "FIRE"] intersect [_camPosReal, _endPosReal]; + if (count _intersect > 0) then { + _ret = _nearestUnit; + } else { //if nothing else, point at ground position + _ret = _endPosReal; + }; + }; + }; + }; + _ret + }; + + switch (_mode) do { + + case "Free": { + GVAR(cameraOn) = true; + GVAR(third) = false; + detach _cam; + player switchCamera "Internal"; + _cam cameraEffect ["Internal", "Back"]; + cameraEffectEnableHUD true; + _dir = getDir _unit; + _pos = [_unit, -5, _dir] call BIS_fnc_relPos; + _pos set [2, ((getPos _unit) select 2) + 2]; + _cam setPos _pos; + GVAR(vector) set [0, _dir]; + [_cam, GVAR(vector)] call BIS_fnc_setObjectRotation; + }; + + case "First": { + if (_unit == player) exitWith {}; + GVAR(cameraOn) = false; + GVAR(third) = false; + _cam attachTo [vehicle _unit, [0,0.1,0]]; + _cam cameraEffect ["Terminate", "Back"]; + vehicle _unit switchCamera "Internal"; + }; + + case "Third": { + if (_unit == player) exitWith {}; + GVAR(third) = true; + GVAR(cameraOn) = false; + if (!difficultyEnabled "3rdPersonView") then { + player switchCamera "Internal"; + _cam cameraEffect ["Internal", "Back"]; + cameraEffectEnableHUD true; + if (vehicle _unit == _unit) then { + _cam attachTo [_unit, [0.1, -2.4, 0.6], "head"]; + } else { + _cam attachTo [vehicle _unit, [0, -7, 1.5]]; + }; + } else { + _cam attachTo [vehicle _unit, [0,0.1,0]]; + _cam cameraEffect ["Terminate", "Back"]; + vehicle _unit switchCamera "External"; + }; + }; + + case "NewUnit": { + + _increment = _this select 1; + _units = []; + { + if (alive _x) then {_units pushBack _x}; + } forEach GVAR(units); + + _count = count _units; + + if (_count > 0) then { + + _index = _units find _unit; + _index = _index + _increment; + if (_index < 0) then {_index = _count - 1}; + if (_index > (_count - 1)) then {_index = 0}; + + GVAR(unit) = _units select _index; + if (!_camOn) then {["Camera", ["SwitchUnit"]] call FUNC(camera)}; + }; + }; + + case "SwitchUnit": { + if !GVAR(third) then { + ["Camera", ["First"]] call FUNC(camera); + } else { + ["Camera", ["Third"]] call FUNC(camera); + }; + }; + + case "Lock": { + if (_lock < 0) then { + + _target = call _findTarget; + + if (typeName _target == "OBJECT") then { + GVAR(lock) = [1, _target]; + } else { + if (count _target > 0) then { + GVAR(lock) = [1, _target]; + }; + }; + + _cam camPrepareTarget (GVAR(lock) select 1); + _cam camCommitPrepared 0; + call FUNC(crosshair); + } else { + + _dir = getDir _cam; + _pitchBank = _cam call BIS_fnc_getPitchBank; + GVAR(lock) = [-1]; + _cam cameraEffect ["Terminate", "Back"]; + camDestroy _cam; + _cam = "camera" camCreate (_camPos select 0); + [_cam, _camPos select 1] call BIS_fnc_setObjectRotation; + _cam camPrepareFOV (_camPos select 2); + _cam camPrepareFocus GVAR(focus); + _cam camCommitPrepared 0; + _cam cameraEffect ["Internal", "Back"]; + cameraEffectEnableHUD true; + GVAR(cam) = _cam; + _obj = GVAR(attach); + if !(isNull _obj) then { + _modelPos = _obj worldToModel (_camPos select 0); + _cam attachTo [_obj, _modelPos]; + _dir = _dir - getDir _obj; + }; + GVAR(vector) = [_dir, _pitchBank select 0, 0]; + [_cam, GVAR(vector)] call BIS_fnc_setObjectRotation; + call FUNC(crosshair); + }; + }; + + case "Attach": { + _dir = getDir _cam; + _pitchBank = _cam call BIS_fnc_getPitchBank; + if (isNull GVAR(attach)) then { + _target = call _findTarget; + if (typeName _target == "OBJECT") then { + if (_target isKindOf "AllVehicles") then { + _dir = _dir - getDir _target; + _cam attachTo [_target]; + GVAR(attach) = _target; + call FUNC(crosshair); + }; + }; + } else { + detach _cam; + GVAR(attach) = objNull; + call FUNC(crosshair); + }; + GVAR(vector) = [_dir, _pitchBank select 0, _pitchBank select 1]; + [_cam, GVAR(vector)] call BIS_fnc_setObjectRotation; + }; + }; + + call FUNC(crosshair); + }; + + ////////////////////////////////////////// + case "Help": { + + _dialog = _this; + +_c1Action = parseText " +Camera:
+
+Move
+Pitch, Yaw
+Roll
+Slide
+Speed Multiplier
+Camera Mode
+Track Pos or Object
+Lock to Object
+Save Pos
+Recall Pos
+Optic Mode
+Focus
+Autofocus
+Disable Focus
+Pitch and Yaw
+Roll
+Pitch/Roll Reset
+Zoom
+Reset Zoom
+Pitch/Yaw/Roll/Zoom Fast
+Pitch/Yaw/Roll/Zoom Slow
+
+"; + +_c1Control = parseText " +
+
+W, A, S, D
+LMB + Mouse
+Shift + LMB + Mouse
+RMB + Mouse
+MouseWheel Up, Down
+Arrow Up, Down
+Space
+Ctrl + Space
+Ctrl + F1...F12
+F1...F12
+N
+Keyboard - and +
+Backspace
+Shift + Backspace
+Numpad 1...9
+Numpad / and *
+Numpad 5
+Numpad - and +
+Numpad Enter
+Numpad 0
+Numpad Decimal
+
+"; + +_c2Action = parseText " +Units:
+
+Cycle Unit
+Save Unit
+Recall Unit
+Unit List
+
+Display:
+
+Toggle Crosshair
+Toggle Status Bar
+View Distance Dialog
+Cycle Marker Mode
+Toggle Help
+
+"; + +_c2Control = parseText " +
+
+Arrow Left, Right
+Ctrl + 1...10
+1...10
+U
+
+
+
+X
+C
+G
+T
+H
+
+"; + +if (!isMultiplayer) then { + +_add1 = parseText " +
+Time:
+
+Faster, Slower
+Reset +
+"; + +_add2 = parseText " +
+
+
+[ and ]
+\
+
+"; + + _c2Action = composeText [_c2Action, _add1]; + _c2Control = composeText [_c2Control, _add2]; +}; + + (_dialog displayCtrl 1) ctrlSetStructuredText _c1Action; + (_dialog displayCtrl 2) ctrlSetStructuredText _c1Control; + (_dialog displayCtrl 3) ctrlSetStructuredText _c2Action; + (_dialog displayCtrl 4) ctrlSetStructuredText _c2Control; + }; + + ////////////////////////////////////////// + case "Exit": { + + if (isClass (configFile >> "CfgPatches" >> "ace_nametags")) then { + EGVAR(nametags,showPlayerNames) = GVAR(tags) select 0; + EGVAR(nametags,showNamesForAI) = GVAR(tags) select 1; + GVAR(tags) = nil; + }; + + if (isClass (configFile >> "CfgPatches" >> "ace_hearing")) then { + EGVAR(hearing,disableVolumeUpdate) = false; + }; + + if (isClass (configFile >> "CfgPatches" >> "ace_interact_menu")) then { + [QGVAR(interactCondition)] call EFUNC(common,removeCanInteractWithCondition); + }; + + GVAR(cam) cameraEffect ["terminate", "back"]; + camUseNVG false; + false SetCamUseTi 0; + camDestroy GVAR(cam); + clearRadio; + + GVAR(noEscape) = nil; + GVAR(cam) = nil; + GVAR(LMB) = nil; + GVAR(RMB) = nil; + GVAR(vector) = nil; + GVAR(fov) = nil; + GVAR(vision) = nil; + GVAR(moveScale) = nil; + GVAR(cameraOn) = nil; + GVAR(focus) = nil; + GVAR(lock) = nil; + GVAR(attach) = nil; + GVAR(unit) = nil; + GVAR(mouseBusy) = nil; + GVAR(markers) = nil; + GVAR(keys) = nil; + GVAR(accTime) = nil; + + _display = findDisplay 46; + + removeMissionEventHandler ["Draw3D", GVAR(ehDraw3D)]; + _display displayRemoveEventHandler ["keyDown", GVAR(ehKeyDown)]; + _display displayRemoveEventHandler ["keyUp", GVAR(ehKeyUp)]; + _display displayRemoveEventHandler ["mouseButtonDown", GVAR(ehMouseButtonDown)]; + _display displayRemoveEventHandler ["mouseButtonUp", GVAR(ehMouseButtonUp)]; + _display displayRemoveEventHandler ["mouseZChanged", GVAR(ehMouseZChanged)]; + _display displayRemoveEventHandler ["mouseMoving", GVAR(ehMouseMoving)]; + _display displayRemoveEventHandler ["mouseHolding", GVAR(ehMouseHolding)]; + GVAR(ehDraw3D) = nil; + GVAR(ehKeyDown) = nil; + GVAR(ehKeyUp) = nil; + GVAR(ehMouseButtonDown) = nil; + GVAR(ehMouseButtonUp) = nil; + GVAR(ehMouseZChanged) = nil; + GVAR(ehMouseMoving) = nil; + GVAR(ehMouseHolding) = nil; + + _layers = missionNamespace getVariable ["BIS_fnc_rscLayer_list", []]; + + for "_i" from 1 to (count _layers - 1) step 2 do { + (_layers select _i) cutText ["", "Plain"]; + }; + + if (!isMultiplayer) then {setAccTime 1}; + ACE_player switchCamera "Internal"; + }; +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_cameraIntro.sqf b/addons/spectator/functions/fnc_cameraIntro.sqf new file mode 100644 index 0000000000..22bfa27d57 --- /dev/null +++ b/addons/spectator/functions/fnc_cameraIntro.sqf @@ -0,0 +1,28 @@ +/* + Author: + voiper + + Description: + Curtain, sound, and text intro when camera starts. + + Arguments: + None + + Example: + call ace_spectator_fnc_camIntro; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +999999 cutText ["", "BLACK IN", 2]; +2 fadeSound 1; + +["Press H for spectator controls", 2] call EFUNC(common,displaytextstructured); + +[_this select 1] call CBA_fnc_removePerFrameHandler; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_canSpectateUnit.sqf b/addons/spectator/functions/fnc_canSpectateUnit.sqf new file mode 100644 index 0000000000..1ce9f32d97 --- /dev/null +++ b/addons/spectator/functions/fnc_canSpectateUnit.sqf @@ -0,0 +1,30 @@ +/* + Author: + voiper + + Description: + Check if a unit is suitable to spectate. + + Arguments: + 0: Unit to check + + Example: + [unit] call ace_spectator_canSpectateUnit; + + Return Value: + Whether is suitable to spectate + + Public: + No +*/ + +#include "script_component.hpp" + +_unit = _this select 0; + +if (_unit distance GVAR(penPos) < 200) exitWith {false}; +if (_unit distance [0,0,0] < 100) exitWith {false}; +if (!GVAR(AI) && !isPlayer _unit) exitWith {false}; +if (GVAR(limitSide) && (([_unit] call FUNC(unitSide)) != GVAR(playerSide))) exitWith {false}; + +true \ No newline at end of file diff --git a/addons/spectator/functions/fnc_checkUnits.sqf b/addons/spectator/functions/fnc_checkUnits.sqf new file mode 100644 index 0000000000..5d62aeed12 --- /dev/null +++ b/addons/spectator/functions/fnc_checkUnits.sqf @@ -0,0 +1,37 @@ +/* + Author: + voiper + + Description: + Compile array of units to spectate. + + Arguments: + None. + + Example: + call ace_spectator_fnc_checkUnits; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_units = allUnits; +_units append allDead; +GVAR(units) = []; + +{ + _listed = _x getVariable [QGVAR(listed), false]; + if (!_listed) then { + [_x] call FUNC(unitVar); + _x addEventHandler ["Killed", {_this call FUNC(killed)}]; + _x addEventHandler ["Respawn", {_this call FUNC(respawn)}]; + _x setVariable [QGVAR(listed), true]; + }; + + if ([_x] call FUNC(canSpectateUnit)) then {GVAR(units) pushback _x}; +} forEach _units; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_compass.sqf b/addons/spectator/functions/fnc_compass.sqf new file mode 100644 index 0000000000..4120cd996f --- /dev/null +++ b/addons/spectator/functions/fnc_compass.sqf @@ -0,0 +1,59 @@ +/* + Author: + voiper + + Description: + Orient and set compass. + + Arguments: + 0: Compass + + Example: + [display] call ace_spectator_fnc_compass; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +#define RESUNITS_X safeZoneW / 100 +#define CENTRE safeZoneX + safeZoneW / 2 +#define COMPASS_W RESUNITS_X * 20 +#define COMPASS_X CENTRE - COMPASS_W / 2 +#define ARC_W COMPASS_W / 2 +#define DEGUNIT COMPASS_W / 180 + +_dialog = _this select 0; + +_Q1 = _dialog displayCtrl 1; +_Q2 = _dialog displayCtrl 2; +_Q3 = _dialog displayCtrl 3; +_Q4 = _dialog displayCtrl 4; +_qOrder = []; + +_dir = if GVAR(cameraOn) then {getDir GVAR(cam)} else {getDir GVAR(unit)}; +_angleFromCentre = _dir - floor(_dir / 90) * 90; +_leftEdgePos = _angleFromCentre * DEGUNIT; + +_positions = [ + [CENTRE - _leftEdgePos - ARC_W, safeZoneY], + [CENTRE - _leftEdgePos, safeZoneY], + [CENTRE - _leftEdgePos + ARC_W, safeZoneY], + [0, safeZoneY - 1] +]; + +_qOrder = switch (true) do { + case ((_dir >= 0) && (_dir < 90)): {[_Q4, _Q1, _Q2, _Q3]}; + case ((_dir >= 90) && (_dir < 180)): {[_Q1, _Q2, _Q3, _Q4]}; + case ((_dir >= 180) && (_dir < 270)): {[_Q2, _Q3, _Q4, _Q1]}; + case (_dir >= 270): {[_Q3, _Q4, _Q1, _Q2]}; +}; + +{ + _x ctrlSetPosition (_positions select _forEachIndex); + _x ctrlCommit 0; +} forEach _qOrder; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_crosshair.sqf b/addons/spectator/functions/fnc_crosshair.sqf new file mode 100644 index 0000000000..80a12cd668 --- /dev/null +++ b/addons/spectator/functions/fnc_crosshair.sqf @@ -0,0 +1,29 @@ +/* + Author: + voiper + + Description: + Automatically set crosshair colour. + + Arguments: + None + + Example: + call ace_spectator_fnc_crosshair; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_xhair = uiNamespace getVariable QGVAR(crosshair); +if (!isNull _xhair) then { + _colour = if ((GVAR(lock) select 0) > -1) then {[1,0,0,0.8]} else { + if (!isNull GVAR(attach)) then {[1,1,0,0.8]} else {[1,1,1,0.8]}; + }; + (_xhair displayCtrl 0) ctrlSetTextColor _colour; +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_draw3D.sqf b/addons/spectator/functions/fnc_draw3D.sqf new file mode 100644 index 0000000000..26eb319f73 --- /dev/null +++ b/addons/spectator/functions/fnc_draw3D.sqf @@ -0,0 +1,34 @@ +/* + Author: + voiper + + Description: + Draw3D EH for spectator. + + Arguments: + None + + Example: + call ace_spectator_fnc_draw3D; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_map = uiNameSpace getVariable QGVAR(map); +if (!isNull _map) exitWith {}; + +_compass = uiNamespace getVariable QGVAR(compass); +_status = uiNamespace getVariable QGVAR(status); +if (!isNull _compass) then {[_compass] call FUNC(compass)}; +if (!isNull _status) then {[_status] call FUNC(status)}; + +if (GVAR(markers) > 0) then { + call FUNC(drawMines3D); + call FUNC(drawUnits3D); +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_drawMines2D.sqf b/addons/spectator/functions/fnc_drawMines2D.sqf new file mode 100644 index 0000000000..c77c589529 --- /dev/null +++ b/addons/spectator/functions/fnc_drawMines2D.sqf @@ -0,0 +1,43 @@ +/* + Author: + voiper + + Description: + Draw mines on spectator map. + + Arguments: + 0: Map control + 1: Zoom level + + Example: + [map, zoomlevel] call ace_spectator_fnc_drawMines2D; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +#define SCALE 5 * safeZoneH / 100 +#define MIN 300 * SCALE +#define TEXTURE "\A3\ui_f\data\map\markers\military\triangle_CA.paa" + +_map = _this select 0; +_zoom = _this select 1; + +_size = SCALE / _zoom; +_textSize = ((1/4 * _size) max (SCALE / 2) min (SCALE / 1.5)) / 2; +_iconSize = (20 * _size) max SCALE min MIN; +_showText = (GVAR(markers) > 1); + +{ + _name = ""; + if (_showText) then { + _magName = getText (configFile >> "CfgAmmo" >> (typeOf _x) >> "defaultMagazine"); + _name = getText (configFile >> "CfgMagazines" >> _magName >> "displayName"); + }; + _map drawIcon [TEXTURE, [1,0.5,0,1], getPos _x, _iconSize, _iconSize, getDir _x, _name, 1, _textSize, "PuristaMedium"]; +} forEach allMines; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_drawMines3D.sqf b/addons/spectator/functions/fnc_drawMines3D.sqf new file mode 100644 index 0000000000..7183db574f --- /dev/null +++ b/addons/spectator/functions/fnc_drawMines3D.sqf @@ -0,0 +1,55 @@ +/* + Author: + voiper + + Description: + Draw mines in 3D. + + Arguments: + None + + Example: + call ace_spectator_fnc_drawMines3D; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +#define SCALE safeZoneH / 100 +#define TEXTMAX (1.5 * SCALE) +#define TEXTMIN 6 * SCALE +#define ICONMAX (30 * SCALE) +#define ICONMIN (120 * SCALE) +#define TEXTURE "\A3\ui_f\data\map\markers\military\triangle_CA.paa" + +_cam = GVAR(cam); +_showText = (GVAR(markers) > 1); + +{ + _pos = getPos _x; + _dist = (_cam distance _pos) + 0.1; + + if (_dist < 2000) then { + + _distScaled = SCALE / sqrt(_dist); + _iconScale = 300 * _distScaled; + //_iconSize = (20 * _size) max _scale min _min; + _iconSize = _iconScale max ICONMAX min ICONMIN; + _textSize = 0; + + if (_showText) then { + _textScale = 10 * _distScaled; + _textSize = _textScale max TEXTMAX min TEXTMIN; + }; + + _magName = getText (configFile >> "CfgAmmo" >> (typeOf _x) >> "defaultMagazine"); + _name = getText (configFile >> "CfgMagazines" >> _magName >> "displayName"); + + drawIcon3D [TEXTURE, [1,0.5,0,1], _pos, _iconSize, _iconSize, 0, _name, 1, _textSize, "PuristaMedium"]; + }; +} forEach allMines; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_drawTracks2D.sqf b/addons/spectator/functions/fnc_drawTracks2D.sqf new file mode 100644 index 0000000000..32b6d58ae8 --- /dev/null +++ b/addons/spectator/functions/fnc_drawTracks2D.sqf @@ -0,0 +1,56 @@ +/* + Author: + voiper + + Description: + Draw unit tracks in spectator map. + + Arguments: + 0: Map control + 1: Zoom level + + Example: + [map, zoomlevel] call ace_spectator_fnc_drawTracks2D; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_map = _this select 0; +_zoom = _this select 1; + +{ + _unit = _x select 0; + + _info = [_unit] call FUNC(unitInfo); + _colour = _info select 2; + + if (_unit == GVAR(unit)) then {_colour = [1,1,0,_colour select 3]}; + + _positions = _x select 1; + _count = count _positions; + _step = floor (10 * _zoom) min 3 max 1; + _lastIndex = 0; + + if (_count > 1) then { + for "_i" from 0 to (_count - 1) step _step do {; + if (_i > 0 && _i < _count) then { + _pos1 = _positions select _i; + _pos2 = _positions select (_i - _step); + _lastIndex = _i; + _map drawLine [_pos1, _pos2, _colour]; + }; + }; + }; + + //((_count - _step + _count mod _step) max 0) + if (alive _unit) then { + _map drawLine [_positions select _lastIndex, getPosVisual _unit, _colour]; + }; + +} forEach GVAR(trackingArray); \ No newline at end of file diff --git a/addons/spectator/functions/fnc_drawUnits2D.sqf b/addons/spectator/functions/fnc_drawUnits2D.sqf new file mode 100644 index 0000000000..858b418c2f --- /dev/null +++ b/addons/spectator/functions/fnc_drawUnits2D.sqf @@ -0,0 +1,81 @@ +/* + Author: + voiper + + Description: + Draw unit icons on spectator map. + + Arguments: + 0: Map control + 1: Zoom level + + Example: + [map, zoomlevel] call ace_spectator_fnc_drawUnits2D; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +#define SCALE 5 * safeZoneH / 100 +#define MIN 300 * SCALE + +_map = _this select 0; +_zoom = _this select 1; + +_topIcon = []; + +_size = SCALE / _zoom; +_showText = (GVAR(markers) > 1); +_textSize = (0.25 * _size) max (SCALE / 2) min (SCALE / 1.5); + +{ + _unit = _x; + _isTarget = (_unit == GVAR(unit)); + + if (true) then { + + _veh = vehicle _unit; + _inVeh = (_veh != _unit); + if (!_isTarget && _inVeh && !(_unit == ((crew _veh) select 0))) exitWith {}; + if (!_isTarget && _inVeh && (GVAR(unit) in (crew _veh))) exitWith {}; + + _obj = if (_inVeh) then {_veh} else {_unit}; + _pos = getPosVisual _obj; + _dir = getDir _obj; + + _info = [_unit] call FUNC(unitInfo); + _name = _info select 0; + _colour = _info select 2; + + _icon = getText (configFile >> "CfgVehicles" >> (typeOf _veh) >> "Icon"); + _iconSize = 0; + _iconText = ""; + + if (_inVeh) then { + _iconSize = (50 * _size) max SCALE min (MIN * 2); + if (_showText) then { + _iconText = "[" + (getText (configFile >> "CfgVehicles" >> (typeOf _veh) >> "displayName")) + "] " + _name; + }; + } else { + _iconSize = (50 * _size) max SCALE min MIN; + if (_showText) then { + _iconText = _name; + }; + }; + + if (_isTarget) exitWith { + _topIcon = [_icon, [1,1,0,1], _pos, _iconSize, _iconSize, _dir, _iconText, 1, _textSize, "PuristaBold", "RIGHT"]; + }; + + _map drawIcon [_icon, _colour, _pos, _iconSize, _iconSize, _dir, _iconText, 1, _textSize, "PuristaMedium", "RIGHT"] + }; +} forEach GVAR(units); + +if (count _topIcon > 0) then { + _map drawIcon _topIcon; +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_drawUnits3D.sqf b/addons/spectator/functions/fnc_drawUnits3D.sqf new file mode 100644 index 0000000000..709e7d8fff --- /dev/null +++ b/addons/spectator/functions/fnc_drawUnits3D.sqf @@ -0,0 +1,97 @@ +/* + Author: + voiper + + Description: + Draw unit icons in 3D. + + Arguments: + None + + Example: + call ace_spectator_fnc_drawUnits3D; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +#define SCALE safeZoneH / 100 +#define TEXTMAX (1.5 * SCALE) +#define TEXTMIN 6 * SCALE +#define ICONMAX (30 * SCALE) +#define ICONMIN (120 * SCALE) + +_cam = GVAR(cam); +_showText = (GVAR(markers) > 1); +_topIcon = []; + +{ + _unit = _x; + + if (true) then { + _veh = vehicle _unit; + _inVeh = (_veh != _unit); + _cmdr = if (_inVeh && (_unit == ((crew _veh) select 0))) then {true} else {false}; + + _obj = if (_inVeh && _cmdr) then {_veh} else {_unit}; + _pos = if (surfaceIsWater getPos _obj) then {getPosASLVisual _obj} else {getPosATLVisual _obj}; + _dist = (_cam distance _pos) + 0.1; + + _isTarget = (_unit == GVAR(unit)); + + //exit if too far + if (_dist > 2000 && !_isTarget) exitWith {}; + + //exit if target not on screen + if ((count (worldToScreen _pos) < 1) && !_isTarget) exitWith {}; + + _info = [_unit] call FUNC(unitInfo); + _name = _info select 0; + _colour = _info select 2; + + _pos set [2, (_pos select 2) + 3]; + _distScaled = SCALE / sqrt(_dist); + + _icon = ""; + _iconScale = 300 * _distScaled; + _iconSize = _iconScale max ICONMAX min ICONMIN; + + _text = if (_showText) then {_name} else {""}; + _textScale = 10 * _distScaled; + _textSize = _textScale max TEXTMAX min TEXTMIN; + + if (_inVeh) then { + if (_cmdr) then { + _icon = getText (configFile >> "CfgVehicles" >> (typeOf _veh) >> "Icon"); + _text = if (_showText) then { + "[" + (getText (configFile >> "CfgVehicles" >> (typeOf _veh) >> "displayName")) + "] " + _text + } else {""}; + _pos set [2, (_pos select 2) + 3]; + } else { + _iconSize = 0; + _textSize = if (_dist < 25) then {_textSize / 1.5} else {0}; + }; + } else { + _icon = getText (configFile >> "CfgVehicles" >> (typeOf _unit) >> "Icon"); + }; + + if (GVAR(markers) > 2) then { + _text = _text + " [" + str ceil(_dist) + "]"; + }; + + if (_isTarget) exitWith { + _topIcon = [_icon, [1,1,0,1], _pos, _iconSize, _iconSize, 0, _text, 2, _textSize, "PuristaBold", "CENTER", true]; + }; + + drawIcon3D [_icon, _colour, _pos, _iconSize, _iconSize, 0, _text, 2, _textSize, "PuristaMedium"]; + }; +} forEach GVAR(units); + +if ((count _topIcon > 0) && GVAR(cameraOn)) then { + drawIcon3D _topIcon; +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_killed.sqf b/addons/spectator/functions/fnc_killed.sqf new file mode 100644 index 0000000000..4774476be4 --- /dev/null +++ b/addons/spectator/functions/fnc_killed.sqf @@ -0,0 +1,65 @@ +/* + Author: + voiper + + Description: + Killed EH for remote units. + + Arguments: + 0: Unit + 1: Killer + + Example: + [unit, killer] call ace_spectator_fnc_killed; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_unit = _this select 0; +_killer = _this select 1; + +if (isNil "_unit") exitWith {}; +if (isNil QGVAR(cam)) exitWith {}; +if (isNull _unit) exitWith {}; + +if (!isNull _killer) then { + if (GVAR(markers) > 2 && !difficultyEnabled "deathMessages") then { + _nameUnit = name _unit; + _nameKiller = name _killer; + + _text = if (_killer == _unit) then { + format ["%1 died", _nameUnit] + } else { + format ["%2 killed by %1", _nameUnit, _nameKiller] + }; + systemChat _text; + }; +}; + +if (_unit == GVAR(unit) && !GVAR(cameraOn)) then { + ["Camera", ["Free"]] call FUNC(camera); + GVAR(unit) = objNull; +}; + +_savedUnit = GVAR(savedUnits) find _unit; +if (_savedUnit > -1) then { + GVAR(savedUnits) set [_savedUnit, objNull]; +}; + +if (!isNil QGVAR(trackingArray)) then { + _pos = getPos _unit; + _pos resize 2; + _index = -1; + {if ((_x select 0) == _unit) then {_index = _forEachIndex}} forEach GVAR(trackingArray); + _unitArray = GVAR(trackingArray) select _index; + _tracks = _unitArray select 1; + _tracks pushBack _pos; + _unitArray set [1, _tracks]; + GVAR(trackingArray) set [_index, _unitArray]; +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_map.sqf b/addons/spectator/functions/fnc_map.sqf new file mode 100644 index 0000000000..b58fa8ce2b --- /dev/null +++ b/addons/spectator/functions/fnc_map.sqf @@ -0,0 +1,156 @@ +/* + Author: + voiper + + Description: + Manage spectator map. + + Arguments: + 0: Mode + 1: Elements (depending on situation) + + Example: + ["Init", [map]] call ace_spectator_fnc_map; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" +#include "\a3\editor_f\Data\Scripts\dikCodes.h" + +_mode = _this select 0; +_this = _this select 1; + +switch _mode do { + + case "Init": { + _map = _this displayCtrl 1; + + if (isNil QGVAR(mapPos)) then { + GVAR(mapPos) = [(GVAR(penPos) select 0) / 4, (GVAR(penPos) select 1) / 4]; + }; + + if (isNil QGVAR(mapZoom)) then { + GVAR(mapZoom) = 0.75; + }; + + _map ctrlMapAnimAdd [0, GVAR(mapZoom), GVAR(mapPos)]; + ctrlMapAnimCommit _map; + setMousePosition [0.5, 0.5]; + + _map ctrlAddEventHandler ["Draw", {['Draw', _this] call FUNC(map)}]; + _map ctrlAddEventHandler ["MouseButtonDblClick", {['Click', _this] call FUNC(map)}]; + }; + + case "Close": { + _map = _this displayCtrl 1; + GVAR(mapPos) = _map ctrlMapScreenToWorld [0.5,0.5]; + GVAR(mapZoom) = ctrlMapScale _map; + }; + + case "Draw": { + _map = _this select 0; + _zoom = ctrlMapScale _map; + + if (GVAR(markers) > 0) then { + if ((GVAR(markers) > 2) && GVAR(tracking)) then { + [_map, _zoom] call FUNC(drawTracks2D); + }; + [_map, _zoom] call FUNC(drawMines2D); + [_map, _zoom] call FUNC(drawUnits2D); + }; + + if (GVAR(cameraOn)) then { + _scale = 5 * safeZoneH / 100; + _map drawIcon ["\A3\ui_f\data\gui\Rsc\RscDisplayMissionEditor\iconcamera_ca.paa", [1,1,1,1], getPos GVAR(cam), 500 * _scale, 500 * _scale, getDir GVAR(cam), "", 0, 0, "PuristaMedium"]; + }; + }; + + case "Click": { + _map = _this select 0; + _button = _this select 1; + _shift = _this select 4; + _mapPos = _map ctrlMapScreenToWorld [_this select 2, _this select 3]; + + if (_shift) then { + if (GVAR(cameraOn)) then { + _dir = [getPos GVAR(cam), _mapPos] call BIS_fnc_dirTo; + GVAR(vector) set [0, _dir]; + [GVAR(cam), GVAR(vector)] call BIS_fnc_setObjectRotation; + }; + } else { + + _newUnit = objNull; + + _scale = ctrlMapScale _map; + _radius = _scale * 250; + _units = []; + + //find units near spot, ignoring height (necessary since nearestObjects takes height into account) + { + if (alive _x) then { + _pos = getPos _x; + _pos set [2, 0]; + if (_pos distance _mapPos <= _radius) then { + _units pushBack _x; + }; + }; + } forEach GVAR(units); + + //find closest unit to spot + if (count _units > 0) then { + _nearest = 0; + for "_i" from 1 to (count _units - 1) do { + if (((_units select _i) distance _mapPos) < ((_units select _nearest) distance _mapPos)) then { + _nearest = _i; + }; + }; + _newUnit = _units select _nearest; + }; + + if (!isNull _newUnit) then { + + if (vehicle _newUnit != _newUnit) then { + _crew = crew (vehicle _newUnit); + _newUnit = _crew select 0; + }; + + GVAR(unit) = _newUnit; + if (GVAR(cameraOn)) then { + ["Camera", ["Third"]] call FUNC(camera); + } else { + if (GVAR(third)) then { + ["Camera", ["Third"]] call FUNC(camera); + } else { + ["Camera", ["First"]] call FUNC(camera); + }; + }; + } else { + + if (!GVAR(cameraOn)) then { + ["Camera", ["Free"]] call FUNC(camera); + }; + _mapPos set [2, 10]; + GVAR(cam) setPosATL _mapPos; + }; + }; + }; + + case "KeyDown": { + _key = _this select 1; + _shift = _this select 2; + _ctrl = _this select 3; + _alt = _this select 4; + _return = false; + + switch (_key) do { + case (DIK_DELETE): {_return = true}; + }; + + _return + }; +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_moduleSpectator.sqf b/addons/spectator/functions/fnc_moduleSpectator.sqf new file mode 100644 index 0000000000..a371327b23 --- /dev/null +++ b/addons/spectator/functions/fnc_moduleSpectator.sqf @@ -0,0 +1,28 @@ +/* + * Author: esteldunedain + * Initializes the addon module. + * + * Arguments: + * Whatever the module provides. + * + * Return Value: + * None + */ + +#include "script_component.hpp" + +PARAMS_3(_logic,_units,_activated); + +if !(_activated) exitWith {}; + +[_logic, QGVAR(enabled), "SpectatorEnabled"] call EFUNC(common,readSettingFromModule); +[_logic, QGVAR(limitSide), "SpectatorPlayerSide"] call EFUNC(common,readSettingFromModule); +[_logic, QGVAR(AI), "SpectatorAI"] call EFUNC(common,readSettingFromModule); +[_logic, QGVAR(tracking), "SpectatorTracking"] call EFUNC(common,readSettingFromModule); +[_logic, QGVAR(modulePos), "SpectatorPos"] call EFUNC(common,readSettingFromModule); +[_logic, QGVAR(endMission), "SpectatorEnd"] call EFUNC(common,readSettingFromModule); + +if GVAR(modulePos) then { + GVAR(startingPos) = getPosATL _logic; + GVAR(startingDir) = getDir _logic; +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_overlay.sqf b/addons/spectator/functions/fnc_overlay.sqf new file mode 100644 index 0000000000..d998b9a3b2 --- /dev/null +++ b/addons/spectator/functions/fnc_overlay.sqf @@ -0,0 +1,106 @@ +/* + Author: + voiper + + Description: + Spectator unit list. + + Arguments: + 0: Specific function to use + 1: Function params + + Example: + ["Init", [display]] call ace_spectator_fnc_overlay; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_mode = _this select 0; +_this = _this select 1; + +switch _mode do { + + case "Init": { + + _display = _this select 0; + _ctrl = _display displayCtrl 0; + _count = _ctrl tvCount []; + for "_i" from 0 to _count do { + _ctrl tvDelete [_x]; + }; + + _ctrl tvAdd [[], "Blufor"]; + _ctrl tvAdd [[], "Opfor"]; + _ctrl tvAdd [[], "Independent"]; + _ctrl tvAdd [[], "Civilian"]; + + _unitList = []; + + { + _units = units _x; + private ["_groupNum"]; + { + if ((GVAR(units) find _x > -1) && alive _x) then { + _info = [_x] call FUNC(unitInfo); + _text = _info select 0; + _team = _info select 1; + _side = switch (_team) do { + case BLUFOR: {0}; + case OPFOR: {1}; + case INDEPENDENT: {2}; + case CIVILIAN: {3}; + }; + + _icon = getText (configFile >> "CfgVehicles" >> (typeOf _x) >> "Icon"); + _picture = "\a3\ui_f\data\map\VehicleIcons\" + _icon + "_ca.paa"; + _treeIndex = []; + _unitList pushBack _x; + + _savedUnit = GVAR(savedUnits) find _x; + if (_savedUnit > -1) then {_text = _text + " (#" + str (_savedUnit + 1) + ")"}; + + if (_forEachIndex == 0) then { + _groupNum = _ctrl tvAdd [[_side], _text]; + _treeIndex = [_side, _groupNum]; + } else { + _num = _ctrl tvAdd [[_side, _groupNum], _text]; + _treeIndex = [_side, _groupNum, _num]; + }; + + _ctrl tvSetPicture [_treeIndex, _picture]; + _ctrl tvSetData [_treeIndex, [_x] call FUNC(unitVar)]; + _unitList pushBack _treeIndex; + }; + } forEach _units; + } forEach allGroups; + + if (!isNull GVAR(unit)) then { + if (alive GVAR(unit)) then { + _treeIndex = _unitList select ((_unitList find GVAR(unit)) + 1); + _ctrl tvSetCurSel _treeIndex; + }; + }; + }; + + case "Select": { + + _ctrl = _this select 0; + _selection = _this select 1; + if (count _selection < 2) exitWith {}; + + _str = _ctrl tvData _selection; + _unit = missionNamespace getVariable _str; + GVAR(unit) = _unit; + if (GVAR(cameraOn)) then { + ["Camera", ["Third"]] call FUNC(camera); + } else { + ["Camera", ["SwitchUnit"]] call FUNC(camera); + }; + }; +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_penPos.sqf b/addons/spectator/functions/fnc_penPos.sqf new file mode 100644 index 0000000000..a176d3e811 --- /dev/null +++ b/addons/spectator/functions/fnc_penPos.sqf @@ -0,0 +1,33 @@ +/* + Author: + voiper + + Description: + Determine spec pen position. + + Arguments: + None + + Example: + call ace_spectator_fnc_penPos; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_mapSize = (configFile >> "CfgWorlds" >> worldName >> "mapSize"); +_worldEdge = if (isNumber _mapSize) then {getNumber _mapSize} else {32768}; +_pos = [_worldEdge * 2, _worldEdge * 2]; + +if (surfaceisWater _pos) then { + _pos set [2, -1.4]; + GVAR(penPos) = ASLtoATL _pos; +} else { + _pos set [2, 0]; + GVAR(penPos) = _pos; +}; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_respawn.sqf b/addons/spectator/functions/fnc_respawn.sqf new file mode 100644 index 0000000000..dba2f6a850 --- /dev/null +++ b/addons/spectator/functions/fnc_respawn.sqf @@ -0,0 +1,25 @@ +/* + Author: + voiper + + Description: + Respawn EH for remote clients. + + Arguments: + 0: Unit + + Example: + ["Init", [false]] call ace_spectator_fnc_camera; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_unit = _this select 0; +_unit setVariable [QGVAR(name), name _unit, true]; +//_unit setVariable [QGVAR(listed), false]; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_sideColour.sqf b/addons/spectator/functions/fnc_sideColour.sqf new file mode 100644 index 0000000000..1262ae5b07 --- /dev/null +++ b/addons/spectator/functions/fnc_sideColour.sqf @@ -0,0 +1,66 @@ +/* + Author: + voiper + + Description: + Determine side colour for spectator GUI. + + Arguments: + 0: Side + + Example: + [0 call ace_spectator_fnc_sideColour; + + Return Value: + Colour + + Public: + No +*/ + +#include "script_component.hpp" +#define FACTOR 1.3 +#define GETCOLOUR(a,b) (profileNameSpace getVariable [QUOTE(a), b]) + +PARAMS_1(_side); + +_colour = switch _side do { + case BLUFOR: { + [ + GETCOLOUR('Map_BLUFOR_R', 0), + GETCOLOUR('Map_BLUFOR_G', 0.3), + GETCOLOUR('Map_BLUFOR_B', 0.6) + ] + }; + + case OPFOR: { + [ + GETCOLOUR('Map_OPFOR_R', 0.5), + GETCOLOUR('Map_OPFOR_G', 0), + GETCOLOUR('Map_OPFOR_B', 0) + ] + }; + + case INDEPENDENT: { + [ + GETCOLOUR('Map_Independent_R', 0), + GETCOLOUR('Map_Independent_G', 0.5), + GETCOLOUR('Map_Independent_B', 0) + ] + }; + + case CIVILIAN: { + [ + GETCOLOUR('Map_Civilian_R', 0.4), + GETCOLOUR('Map_Civilian_G', 0), + GETCOLOUR('Map_Civilian_B', 0.5) + ] + }; +}; + +_colour set [0, (_colour select 0) * FACTOR]; +_colour set [1, (_colour select 1) * FACTOR]; +_colour set [2, (_colour select 2) * FACTOR]; +_colour set [3, 1]; + +_colour \ No newline at end of file diff --git a/addons/spectator/functions/fnc_status.sqf b/addons/spectator/functions/fnc_status.sqf new file mode 100644 index 0000000000..4207ef9c00 --- /dev/null +++ b/addons/spectator/functions/fnc_status.sqf @@ -0,0 +1,60 @@ +/* + Author: + voiper + + Description: + Render status. + + Arguments: + 0: Status element + + Example: + [display] call ace_spectator_fnc_status; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_display = _this select 0; + +_speedText = (str ([GVAR(moveScale), 4] call BIS_fnc_cutDecimals)) + "v"; +(_display displayCtrl 0) ctrlSetText _speedText; + +_name = ""; +_colour = [1,1,1,1]; + +if (!isNull GVAR(unit)) then { + _info = [GVAR(unit)] call FUNC(unitInfo); + _name = _info select 0; + _side = _info select 1; + _colour = _info select 2; + _colour set [3, 1]; +}; +(_display displayCtrl 1) ctrlSetText _name; +(_display displayCtrl 1) ctrlSetTextColor _colour; + +_mode = if (GVAR(cameraOn)) then { + if (isNull GVAR(attach)) then {"FREE"} else {"ATTACH"}; +} else { + if (GVAR(third)) then {"THIRD"} else {"FIRST"}; +}; +(_display displayCtrl 2) ctrlSetText _mode; + +_timeText = [dayTime] call BIS_fnc_timeToString; +(_display displayCtrl 3) ctrlSetText _timeText; + +_fovText = (str ([GVAR(fov), 3] call BIS_fnc_cutDecimals)) + "a"; +(_display displayCtrl 4) ctrlSetText _fovText; + +_timeAccText = (str ([GVAR(accTime), 4] call BIS_fnc_cutDecimals)) + "x"; +(_display displayCtrl 5) ctrlSetText _timeAccText; + +_focusDist = [GVAR(focus) select 0, 1] call BIS_fnc_cutDecimals; +_focusBlur = GVAR(focus) select 1; +_focusText = if (_focusDist == -1 && _focusBlur == 1) then {"Auto"} else {if (_focusDist < 0) then {toString [8734]} else {str _focusDist + "m"}}; +(_display displayCtrl 6) ctrlSetText _focusText; \ No newline at end of file diff --git a/addons/spectator/functions/fnc_trackUnits.sqf b/addons/spectator/functions/fnc_trackUnits.sqf new file mode 100644 index 0000000000..28da75a554 --- /dev/null +++ b/addons/spectator/functions/fnc_trackUnits.sqf @@ -0,0 +1,54 @@ +/* + Author: + voiper + + Description: + Check and record tracking for all units. + + Arguments: + None + + Example: + call ace_spectator_fnc_trackUnits; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +if (isNil QGVAR(trackingArray)) then { + GVAR(trackingArray) = []; +}; + +if (count GVAR(units) < 1) exitWith {}; + +{ + _unit = _x; + _pos = getPos _unit; + _pos resize 2; + + _index = -1; + + {if ((_x select 0) == _unit) then {_index = _forEachIndex}} forEach GVAR(trackingArray); + if (_index == -1) then { + GVAR(trackingArray) pushBack [_unit, [_pos]] + } else { + _unitArray = GVAR(trackingArray) select _index; + _trackingArray = _unitArray select 1; + _latestIndex = (count _trackingArray) - 1; + _latestPos = _trackingArray select _latestIndex; + _diffX = abs((_latestPos select 0) - (_pos select 0)); + _diffY = abs((_latestPos select 1) - (_pos select 1)); + + if !((_diffX < 20) && (_diffY < 20)) then { + _trackingArray pushBack _pos; + _unitArray set [1, _trackingArray]; + GVAR(trackingArray) set [_index, _unitArray]; + }; + }; + +} forEach GVAR(units); \ No newline at end of file diff --git a/addons/spectator/functions/fnc_unitInfo.sqf b/addons/spectator/functions/fnc_unitInfo.sqf new file mode 100644 index 0000000000..a377cabf31 --- /dev/null +++ b/addons/spectator/functions/fnc_unitInfo.sqf @@ -0,0 +1,34 @@ +/* + Author: + voiper + + Description: + Retrieve unit name, side, and colour. + + Arguments: + 0: Unit + + Example: + [player] call ace_spectator_fnc_unitInfo; + + Return Value: + Name , Side , Colour + + Public: + No +*/ + +#include "script_component.hpp" + +PARAMS_1(_unit); + +_name = [_unit] call EFUNC(common,getName); +_side = [_unit] call FUNC(unitSide); +_colour = [_side] call FUNC(sideColour); + +if (!alive _unit) then { + {_colour set [_forEachIndex, _x / 2.5]} forEach _colour; + _colour set [3, 0.8]; +}; + +[_name, _side, _colour] \ No newline at end of file diff --git a/addons/spectator/functions/fnc_unitSide.sqf b/addons/spectator/functions/fnc_unitSide.sqf new file mode 100644 index 0000000000..d33520d5ec --- /dev/null +++ b/addons/spectator/functions/fnc_unitSide.sqf @@ -0,0 +1,39 @@ +/* + Author: + voiper + + Description: + Retrieve unit's side, even after they join grpNull. + + Arguments: + 0: Unit to check + + Example: + [unit] call ace_spectator_unitSide; + + Return Value: + Side + + Public: + No +*/ + +#include "script_component.hpp" + +PARAMS_1(_unit); + +private ["_side"]; + +if (alive _unit) then { + _side = side (group _unit); +} else { + _sideNum = getNumber (configFile >> "CfgVehicles" >> (typeOf _unit) >> "side"); + _side = switch _sideNum do { + case 0: {OPFOR}; + case 1: {BLUFOR}; + case 2: {INDEPENDENT}; + case 3: {CIVILIAN}; + }; +}; + +_side \ No newline at end of file diff --git a/addons/spectator/functions/fnc_unitVar.sqf b/addons/spectator/functions/fnc_unitVar.sqf new file mode 100644 index 0000000000..6d072ae171 --- /dev/null +++ b/addons/spectator/functions/fnc_unitVar.sqf @@ -0,0 +1,38 @@ +/* + Author: Karel Moricky, modified by voiper + + Description: + Set or return an unique object variable (client only). + + Arguments: + 0: Unit + + Returns: + Variable +*/ + +#include "script_component.hpp" + +private ["_object","_var"]; + +_object = [_this, 0, objnull, [objnull]] call BIS_fnc_param; + +if (isNull _object) exitwith {""}; + +_var = _object getVariable [QGVAR(objectVar), ""]; + +if (_var == "") then { + _var = vehicleVarname _object; + + if (_var == "") then { + _var = [_this, 1, QGVAR(obj), [""]] call BIS_fnc_param; + _varID = [_var, 1] call BIS_fnc_counter; + _var = _var + str _varID; + }; + + _object setVariable [QGVAR(objectVar), _var]; + missionNamespace setVariable [_var, _object]; + _object setVehicleVarname _var; +}; + +_var \ No newline at end of file diff --git a/addons/spectator/functions/fnc_viewDistance.sqf b/addons/spectator/functions/fnc_viewDistance.sqf new file mode 100644 index 0000000000..4112ed2a62 --- /dev/null +++ b/addons/spectator/functions/fnc_viewDistance.sqf @@ -0,0 +1,47 @@ +/* + Author: + voiper + + Description: + View distance dialog. + + Arguments: + 0: Mode + 1: Arguemnts + + Example: + ["Init", [dialog]] call ace_spectator_fnc_viewDistance; + + Return Value: + None + + Public: + No +*/ + +#include "script_component.hpp" + +_mode = _this select 0; +_this = _this select 1; + +switch _mode do { + + case "Init": { + _dialog = _this select 0; + _dist = -1; + _text = _dialog displayCtrl 1; + _slider = _dialog displayCtrl 2; + _slider slidersetRange [1000,20000]; + _slider sliderSetSpeed [1000,1000,1000]; + _slider sliderSetPosition viewDistance; + _text ctrlSetText str viewDistance; + }; + + case "Slider": { + _dialog = ctrlParent (_this select 0); + _dist = _this select 1; + _text = _dialog displayCtrl 1; + setViewDistance _dist; + _text ctrlSetText str viewDistance; + }; +}; \ No newline at end of file diff --git a/addons/spectator/functions/script_component.hpp b/addons/spectator/functions/script_component.hpp new file mode 100644 index 0000000000..d5034cb39c --- /dev/null +++ b/addons/spectator/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\z\ace\addons\spectator\script_component.hpp" \ No newline at end of file diff --git a/addons/spectator/rsc_defines.hpp b/addons/spectator/rsc_defines.hpp new file mode 100644 index 0000000000..e7cb7178c3 --- /dev/null +++ b/addons/spectator/rsc_defines.hpp @@ -0,0 +1,646 @@ +// Control types +#define CT_STATIC 0 +#define CT_BUTTON 1 +#define CT_EDIT 2 +#define CT_SLIDER 3 +#define CT_COMBO 4 +#define CT_LISTBOX 5 +#define CT_TOOLBOX 6 +#define CT_CHECKBOXES 7 +#define CT_PROGRESS 8 +#define CT_HTML 9 +#define CT_STATIC_SKEW 10 +#define CT_ACTIVETEXT 11 +#define CT_TREE 12 +#define CT_STRUCTURED_TEXT 13 +#define CT_CONTEXT_MENU 14 +#define CT_CONTROLS_GROUP 15 +#define CT_SHORTCUTBUTTON 16 +#define CT_HITZONES 17 +#define CT_XKEYDESC 40 +#define CT_XBUTTON 41 +#define CT_XLISTBOX 42 +#define CT_XSLIDER 43 +#define CT_XCOMBO 44 +#define CT_ANIMATED_TEXTURE 45 +#define CT_OBJECT 80 +#define CT_OBJECT_ZOOM 81 +#define CT_OBJECT_CONTAINER 82 +#define CT_OBJECT_CONT_ANIM 83 +#define CT_LINEBREAK 98 +#define CT_USER 99 +#define CT_MAP 100 +#define CT_MAP_MAIN 101 +#define CT_LISTNBOX 102 +#define CT_ITEMSLOT 103 +#define CT_CHECKBOX 77 + +// Static styles +#define ST_POS 0x0F +#define ST_HPOS 0x03 +#define ST_VPOS 0x0C +#define ST_LEFT 0x00 +#define ST_RIGHT 0x01 +#define ST_CENTER 0x02 +#define ST_DOWN 0x04 +#define ST_UP 0x08 +#define ST_VCENTER 0x0C + +#define ST_TYPE 0xF0 +#define ST_SINGLE 0x00 +#define ST_MULTI 0x10 +#define ST_TITLE_BAR 0x20 +#define ST_PICTURE 0x30 +#define ST_FRAME 0x40 +#define ST_BACKGROUND 0x50 +#define ST_GROUP_BOX 0x60 +#define ST_GROUP_BOX2 0x70 +#define ST_HUD_BACKGROUND 0x80 +#define ST_TILE_PICTURE 0x90 +#define ST_WITH_RECT 0xA0 +#define ST_LINE 0xB0 +#define ST_UPPERCASE 0xC0 +#define ST_LOWERCASE 0xD0 + +#define ST_SHADOW 0x100 +#define ST_NO_RECT 0x200 +#define ST_KEEP_ASPECT_RATIO 0x800 + +#define ST_TITLE ST_TITLE_BAR + ST_CENTER + +// Slider styles +#define SL_DIR 0x400 +#define SL_VERT 0 +#define SL_HORZ 0x400 + +#define SL_TEXTURES 0x10 + +// progress bar +#define ST_VERTICAL 0x01 +#define ST_HORIZONTAL 0 + +// Listbox styles +#define LB_TEXTURES 0x10 +#define LB_MULTI 0x20 + +// Tree styles +#define TR_SHOWROOT 1 +#define TR_AUTOCOLLAPSE 2 + +// MessageBox styles +#define MB_BUTTON_OK 1 +#define MB_BUTTON_CANCEL 2 +#define MB_BUTTON_USER 4 +#define MB_ERROR_DIALOG 8 + +// Fonts +#define GUI_FONT_NORMAL PuristaMedium +#define GUI_FONT_BOLD PuristaSemibold +#define GUI_FONT_THIN PuristaLight +#define GUI_FONT_MONO EtelkaMonospacePro +#define GUI_FONT_NARROW EtelkaNarrowMediumPro +#define GUI_FONT_CODE LucidaConsoleB +#define GUI_FONT_SYSTEM TahomaB + +//colours + +#define COLOUR_GUI_TEXT {"profilenamespace getvariable ['GUI_TITLETEXT_RGB_R',1]", "profilenamespace getvariable ['GUI_TITLETEXT_RGB_G',1]", "profilenamespace getvariable ['GUI_TITLETEXT_RGB_B',1]", "profilenamespace getvariable ['GUI_TITLETEXT_RGB_A',1]"} + +#define COLOUR_GUI_BG {"profilenamespace getvariable ['GUI_BCG_RGB_R',0.8]", "profilenamespace getvariable ['GUI_BCG_RGB_G',0.8]","profilenamespace getvariable ['GUI_BCG_RGB_B',0.8]","profilenamespace getvariable ['GUI_BCG_RGB_A',0.8]"} + +#define COLOUR_IGUI_TEXT {"profilenamespace getvariable ['IGUI_TEXT_RGB_R',1]", "profilenamespace getvariable ['IGUI_TEXT_RGB_G',1]", "profilenamespace getvariable ['IGUI_TEXT_RGB_B',1]", "profilenamespace getvariable ['IGUI_TEXT_RGB_A',1]"} + +#define COLOUR_IGUI_BG {"profilenamespace getvariable ['IGUI_BCG_RGB_R',0.8]", "profilenamespace getvariable ['IGUI_BCG_RGB_G',0.5]","profilenamespace getvariable ['IGUI_BCG_RGB_B',0]","profilenamespace getvariable ['IGUI_BCG_RGB_A',0.8]"} + +#define COLOUR_IGUI_WARN {"profilenamespace getvariable ['IGUI_TEXT_WARNING_R',0.8]", "profilenamespace getvariable ['IGUI_TEXT_WARNING_G',0.5]", "profilenamespace getvariable ['IGUI_TEXT_WARNING_B',0]", "profilenamespace getvariable ['IGUI_TEXT_WARNING_A',0.8]"} + +// Grids +#define GUI_GRID_CENTER_WAbs ((safezoneW / safezoneH) min 1.2) +#define GUI_GRID_CENTER_HAbs (GUI_GRID_CENTER_WAbs / 1.2) +#define GUI_GRID_CENTER_W (GUI_GRID_CENTER_WAbs / 40) +#define GUI_GRID_CENTER_H (GUI_GRID_CENTER_HAbs / 25) +#define GUI_GRID_CENTER_X (safezoneX + (safezoneW - GUI_GRID_CENTER_WAbs)/2) +#define GUI_GRID_CENTER_Y (safezoneY + (safezoneH - GUI_GRID_CENTER_HAbs)/2) + +#define RESUNITS_X (safeZoneW / 100) +#define RESUNITS_Y (safeZoneH / 100) +#define RESCENTRE_X safeZoneX + safeZoneW / 2 +#define RESCENTRE_Y safeZoneY + safeZoneH / 2 + +#define Q(s) #s + +class vip_rsc_picture { + + access = 0; + idc = -1; + type = CT_STATIC; + style = ST_PICTURE; + colorBackground[] = {0,0,0,0}; + colorText[] = {1,1,1,1}; + font = "TahomaB"; + sizeEx = 0; + lineSpacing = 0; + text = ""; + fixedWidth = 0; + shadow = 0; +}; + +class vip_rsc_text { + + access = 0; + idc = -1; + type = CT_STATIC; + style = ST_LEFT; + colorBackground[] = {0,0,0,0}; + colorText[] = {1,1,1,1}; + font = "PuristaMedium"; + sizeEx = 0.08; + lineSpacing = 1; + text = ""; + fixedWidth = 0; + moving = 0; +}; + +class vip_rsc_box { + + idc=-1; + type = CT_STATIC; + style = ST_CENTER; + text = ""; + font = "TahomaB"; + sizeEx = 0.04; + + colorBackground[] = {0,0,0,1}; + colorText[] = {1,1,1,1}; +}; + +class vip_rsc_button { + + access = 0; + type = CT_BUTTON; + style = ST_LEFT; + default = 0; + blinkingPeriod = 0; + + x = 0 * GUI_GRID_CENTER_W + GUI_GRID_CENTER_X; + y = 0 * GUI_GRID_CENTER_H + GUI_GRID_CENTER_Y; + w = 6 * GUI_GRID_CENTER_W; + h = 1 * GUI_GRID_CENTER_H; + + colorBackground[] = {0.8,0.8,0.8,1}; + colorBackgroundDisabled[] = {0.8,0.8,0.8, 0.25}; + colorBackgroundActive[] = {1,1,1,1}; + colorFocused[] = {0.8,0.8,0.8,1}; + + text = ""; + sizeEx = 1 * GUI_GRID_CENTER_H; + font = GUI_FONT_NORMAL; + shadow = 1; + colorText[] = {0,0,0,1}; + colorDisabled[] = {0,0,0,0.25}; + + borderSize = 0.0; + colorBorder[] = {1,1,1,1}; + + colorShadow[] = {0,0,0,0.0}; + offsetX = 0.0075; + offsetY = 0.01; + offsetPressedX = 0.000; + offsetPressedY = 0.00; + + period = 0; + periodFocus = 0; + periodOver = 0.5; + + soundClick[] = {"\A3\ui_f\data\sound\RscButton\soundClick",0.09,1}; + soundEnter[] = {"\A3\ui_f\data\sound\RscButton\soundEnter",0.09,1}; + soundPush[] = {"\A3\ui_f\data\sound\RscButton\soundPush",0.09,1}; + soundEscape[] = {"\A3\ui_f\data\sound\RscButton\soundEscape",0.09,1}; + + //onButtonClick = "false"; +}; + +class vip_rsc_listbox { + + access = 0; + idc = 25; + type = CT_COMBO; + style = ST_LEFT + LB_TEXTURES; + default = 0; + blinkingPeriod = 0; + + x = 12 * GUI_GRID_CENTER_W + GUI_GRID_CENTER_X; + y = 4 * GUI_GRID_CENTER_H + GUI_GRID_CENTER_Y; + w = 9 * GUI_GRID_CENTER_W; + h = 1 * GUI_GRID_CENTER_H; + + colorBackground[] = {0.2,0.2,0.2,1}; + colorSelectBackground[] = {1,0.5,0,1}; + + sizeEx = GUI_GRID_CENTER_H; + font = GUI_FONT_NORMAL; + shadow = 0; + colorText[] = {1,1,1,1}; + colorDisabled[] = {1,1,1,0.5}; + colorSelect[] = {1,1,1,1}; + + pictureColor[] = {1,0.5,0,1}; + pictureColorSelect[] = {1,1,1,1}; + pictureColorDisabled[] = {1,1,1,0.5}; + + arrowEmpty = "\A3\ui_f\data\GUI\RscCommon\rsccombo\arrow_combo_ca.paa"; + arrowFull = "\A3\ui_f\data\GUI\RscCommon\rsccombo\arrow_combo_active_ca.paa"; + + wholeHeight = 8 * GUI_GRID_CENTER_H; + maxHistoryDelay = 1; + + soundExpand[] = {"\A3\ui_f\data\sound\RscCombo\soundExpand",0.1,1}; + soundCollapse[] = {"\A3\ui_f\data\sound\RscCombo\soundCollapse",0.1,1}; + soundSelect[] = {"\A3\ui_f\data\sound\RscCombo\soundSelect",0.1,1}; + + class ComboScrollBar + { + width = 0; + height = 0; + scrollSpeed = 0.01; + + arrowEmpty = "\A3\ui_f\data\gui\cfg\scrollbar\arrowEmpty_ca.paa"; + arrowFull = "\A3\ui_f\data\gui\cfg\scrollbar\arrowFull_ca.paa"; + border = "\A3\ui_f\data\gui\cfg\scrollbar\border_ca.paa"; + thumb = "\A3\ui_f\data\gui\cfg\scrollbar\thumb_ca.paa"; + + color[] = {1,1,1,1}; + }; + + onLBSelChanged = ""; +}; + +class vip_rsc_frame { + type = 0; + idc = -1; + style=ST_FRAME; + shadow=2; + colorBackground[]={0,0,0,0}; + colorText[]={1,1,1,1}; + font="PuristaMedium"; + sizeEx=0.02; + text=""; +}; + +class vip_rsc_map { + moveOnEdges=1; + x="SafeZoneXAbs"; + y="SafeZoneY + 1.5 * ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25)"; + w="SafeZoneWAbs"; + h="SafeZoneH - 1.5 * ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25)"; + shadow=0; + ptsPerSquareSea=5; + ptsPerSquareTxt=20; + ptsPerSquareCLn=10; + ptsPerSquareExp=10; + ptsPerSquareCost=10; + ptsPerSquareFor=9; + ptsPerSquareForEdge=9; + ptsPerSquareRoad=6; + ptsPerSquareObj=9; + showCountourInterval=0; + scaleMin=0.001; + scaleMax=1; + scaleDefault=0.16; + maxSatelliteAlpha=0.85000002; + alphaFadeStartScale=2; + alphaFadeEndScale=2; + colorBackground[]={0.96899998,0.95700002,0.949,1}; + colorSea[]={0.46700001,0.63099998,0.85100001,0.5}; + colorForest[]={0.62400001,0.77999997,0.38800001,0.5}; + colorForestBorder[]={0,0,0,0}; + colorRocks[]={0,0,0,0.30000001}; + colorRocksBorder[]={0,0,0,0}; + colorLevels[]={0.28600001,0.177,0.093999997,0.5}; + colorMainCountlines[]={0.57200003,0.354,0.18799999,0.5}; + colorCountlines[]={0.57200003,0.354,0.18799999,0.25}; + colorMainCountlinesWater[]={0.491,0.57700002,0.70200002,0.60000002}; + colorCountlinesWater[]={0.491,0.57700002,0.70200002,0.30000001}; + colorPowerLines[]={0.1,0.1,0.1,1}; + colorRailWay[]={0.80000001,0.2,0,1}; + colorNames[]={0.1,0.1,0.1,0.89999998}; + colorInactive[]={1,1,1,0.5}; + colorOutside[]={0,0,0,1}; + colorTracks[]={0.83999997,0.75999999,0.64999998,0.15000001}; + colorTracksFill[]={0.83999997,0.75999999,0.64999998,1}; + colorRoads[]={0.69999999,0.69999999,0.69999999,1}; + colorRoadsFill[]={1,1,1,1}; + colorMainRoads[]={0.89999998,0.5,0.30000001,1}; + colorMainRoadsFill[]={1,0.60000002,0.40000001,1}; + colorGrid[]={0.1,0.1,0.1,0.60000002}; + colorGridMap[]={0.1,0.1,0.1,0.60000002}; + fontLabel="PuristaMedium"; + sizeExLabel="( ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8)"; + fontGrid="TahomaB"; + sizeExGrid=0.02; + fontUnits="TahomaB"; + sizeExUnits="( ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8)"; + fontNames="EtelkaNarrowMediumPro"; + sizeExNames="( ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8) * 2"; + fontInfo="PuristaMedium"; + sizeExInfo="( ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8)"; + fontLevel="TahomaB"; + sizeExLevel=0.02; + text="#(argb,8,8,3)color(1,1,1,1)"; + class Legend + { + x="SafeZoneX + ( ((safezoneW / safezoneH) min 1.2) / 40)"; + y="SafeZoneY + safezoneH - 4.5 * ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25)"; + w="10 * ( ((safezoneW / safezoneH) min 1.2) / 40)"; + h="3.5 * ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25)"; + font="PuristaMedium"; + sizeEx="( ( ( ((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8)"; + colorBackground[]={1,1,1,0.5}; + color[]={0,0,0,1}; + }; + class Task + { + icon="\A3\ui_f\data\map\mapcontrol\taskIcon_CA.paa"; + iconCreated="\A3\ui_f\data\map\mapcontrol\taskIconCreated_CA.paa"; + iconCanceled="\A3\ui_f\data\map\mapcontrol\taskIconCanceled_CA.paa"; + iconDone="\A3\ui_f\data\map\mapcontrol\taskIconDone_CA.paa"; + iconFailed="\A3\ui_f\data\map\mapcontrol\taskIconFailed_CA.paa"; + color[]= + { + "(profilenamespace getvariable ['IGUI_TEXT_RGB_R',0])", + "(profilenamespace getvariable ['IGUI_TEXT_RGB_G',1])", + "(profilenamespace getvariable ['IGUI_TEXT_RGB_B',1])", + "(profilenamespace getvariable ['IGUI_TEXT_RGB_A',0.8])" + }; + colorCreated[]={1,1,1,1}; + colorCanceled[]={0.69999999,0.69999999,0.69999999,1}; + colorDone[]={0.69999999,1,0.30000001,1}; + colorFailed[]={1,0.30000001,0.2,1}; + size=27; + importance=1; + coefMin=1; + coefMax=1; + }; + class Waypoint + { + icon="\A3\ui_f\data\map\mapcontrol\waypoint_ca.paa"; + color[]={0,0,0,1}; + }; + class WaypointCompleted + { + icon="\A3\ui_f\data\map\mapcontrol\waypointCompleted_ca.paa"; + color[]={0,0,0,1}; + }; + class CustomMark + { + icon="\A3\ui_f\data\map\mapcontrol\custommark_ca.paa"; + size=24; + importance=1; + coefMin=1; + coefMax=1; + color[]={0,0,0,1}; + }; + class Command + { + icon="\A3\ui_f\data\map\mapcontrol\waypoint_ca.paa"; + size=18; + importance=1; + coefMin=1; + coefMax=1; + color[]={1,1,1,1}; + }; + class Bush + { + icon="\A3\ui_f\data\map\mapcontrol\bush_ca.paa"; + color[]={0.44999999,0.63999999,0.33000001,0.40000001}; + size="14/2"; + importance="0.2 * 14 * 0.05 * 0.05"; + coefMin=0.25; + coefMax=4; + }; + class Rock + { + icon="\A3\ui_f\data\map\mapcontrol\rock_ca.paa"; + color[]={0.1,0.1,0.1,0.80000001}; + size=12; + importance="0.5 * 12 * 0.05"; + coefMin=0.25; + coefMax=4; + }; + class SmallTree + { + icon="\A3\ui_f\data\map\mapcontrol\bush_ca.paa"; + color[]={0.44999999,0.63999999,0.33000001,0.40000001}; + size=12; + importance="0.6 * 12 * 0.05"; + coefMin=0.25; + coefMax=4; + }; + class Tree + { + icon="\A3\ui_f\data\map\mapcontrol\bush_ca.paa"; + color[]={0.44999999,0.63999999,0.33000001,0.40000001}; + size=12; + importance="0.9 * 16 * 0.05"; + coefMin=0.25; + coefMax=4; + }; + class busstop + { + icon="\A3\ui_f\data\map\mapcontrol\busstop_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class fuelstation + { + icon="\A3\ui_f\data\map\mapcontrol\fuelstation_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class hospital + { + icon="\A3\ui_f\data\map\mapcontrol\hospital_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class church + { + icon="\A3\ui_f\data\map\mapcontrol\church_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class lighthouse + { + icon="\A3\ui_f\data\map\mapcontrol\lighthouse_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class power + { + icon="\A3\ui_f\data\map\mapcontrol\power_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class powersolar + { + icon="\A3\ui_f\data\map\mapcontrol\powersolar_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class powerwave + { + icon="\A3\ui_f\data\map\mapcontrol\powerwave_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class powerwind + { + icon="\A3\ui_f\data\map\mapcontrol\powerwind_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class quay + { + icon="\A3\ui_f\data\map\mapcontrol\quay_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class transmitter + { + icon="\A3\ui_f\data\map\mapcontrol\transmitter_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class watertower + { + icon="\A3\ui_f\data\map\mapcontrol\watertower_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={1,1,1,1}; + }; + class Cross + { + icon="\A3\ui_f\data\map\mapcontrol\Cross_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={0,0,0,1}; + }; + class Chapel + { + icon="\A3\ui_f\data\map\mapcontrol\Chapel_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={0,0,0,1}; + }; + class Shipwreck + { + icon="\A3\ui_f\data\map\mapcontrol\Shipwreck_CA.paa"; + size=24; + importance=1; + coefMin=0.85000002; + coefMax=1; + color[]={0,0,0,1}; + }; + class Bunker + { + icon="\A3\ui_f\data\map\mapcontrol\bunker_ca.paa"; + size=14; + importance="1.5 * 14 * 0.05"; + coefMin=0.25; + coefMax=4; + color[]={0,0,0,1}; + }; + class Fortress + { + icon="\A3\ui_f\data\map\mapcontrol\bunker_ca.paa"; + size=16; + importance="2 * 16 * 0.05"; + coefMin=0.25; + coefMax=4; + color[]={0,0,0,1}; + }; + class Fountain + { + icon="\A3\ui_f\data\map\mapcontrol\fountain_ca.paa"; + size=11; + importance="1 * 12 * 0.05"; + coefMin=0.25; + coefMax=4; + color[]={0,0,0,1}; + }; + class Ruin + { + icon="\A3\ui_f\data\map\mapcontrol\ruin_ca.paa"; + size=16; + importance="1.2 * 16 * 0.05"; + coefMin=1; + coefMax=4; + color[]={0,0,0,1}; + }; + class Stack + { + icon="\A3\ui_f\data\map\mapcontrol\stack_ca.paa"; + size=20; + importance="2 * 16 * 0.05"; + coefMin=0.89999998; + coefMax=4; + color[]={0,0,0,1}; + }; + class Tourism + { + icon="\A3\ui_f\data\map\mapcontrol\tourism_ca.paa"; + size=16; + importance="1 * 16 * 0.05"; + coefMin=0.69999999; + coefMax=4; + color[]={0,0,0,1}; + }; + class ViewTower + { + icon="\A3\ui_f\data\map\mapcontrol\viewtower_ca.paa"; + size=16; + importance="2.5 * 16 * 0.05"; + coefMin=0.5; + coefMax=4; + color[]={0,0,0,1}; + }; +}; \ No newline at end of file diff --git a/addons/spectator/script_component.hpp b/addons/spectator/script_component.hpp new file mode 100644 index 0000000000..c445dc4cdd --- /dev/null +++ b/addons/spectator/script_component.hpp @@ -0,0 +1,12 @@ +#define COMPONENT spectator +#include "\z\ace\addons\main\script_mod.hpp" + +#ifdef DEBUG_ENABLED_SPECTATOR + #define DEBUG_MODE_FULL +#endif + +#ifdef DEBUG_SETTINGS_SPECTATOR + #define DEBUG_SETTINGS DEBUG_SETTINGS_SPECTATOR +#endif + +#include "\z\ace\addons\main\script_macros.hpp" \ No newline at end of file diff --git a/addons/spectator/stringtable.xml b/addons/spectator/stringtable.xml new file mode 100644 index 0000000000..72a29abe9c --- /dev/null +++ b/addons/spectator/stringtable.xml @@ -0,0 +1,110 @@ + + + + + Spectator + + + + + + + Enable Spectator + + + + + + + Begin spectating on player death? + + + + + + + Player Side Only + + + + + + + Only spectate units belonging to player's side? + + + + + + + Spectate AI + + + + + + + Allow spectating of AI units? + + + + + + + Track Units + + + + + + + Track units' movements throughout mission? + + + + + + + Start Position + + + + + + + Use this module as a starting position for spectator camera? + + + + + + + End Mission + + + + + + + End mission when all players dead (default BIS behaviour)? + + + + + + + View Distance + + + + + + + OK + + + + + + + \ No newline at end of file