From 89659ec65a0057d5521532d5280691361d0d9ab9 Mon Sep 17 00:00:00 2001 From: IndigoFox Date: Tue, 29 Mar 2022 17:40:01 -0400 Subject: [PATCH] ready for playtesting? --- x/ocap2/addons/extension/config.cpp | 2 + x/ocap2/addons/main/config.cpp | 2 + x/ocap2/addons/recorder/XEH_preInit.sqf | 28 +-- x/ocap2/addons/recorder/XEH_prep.sqf | 4 +- x/ocap2/addons/recorder/config.cpp | 2 + x/ocap2/addons/recorder/fnc_aceExplosives.sqf | 13 +- x/ocap2/addons/recorder/fnc_aceThrowing.sqf | 180 +++++++----------- .../addons/recorder/fnc_addEventMission.sqf | 132 ++++++++----- .../recorder/fnc_addUnitEventHandlers.sqf | 33 ++-- x/ocap2/addons/recorder/fnc_eh_firedMan.sqf | 161 +++++++++++----- x/ocap2/addons/recorder/fnc_eh_hit.sqf | 23 +-- x/ocap2/addons/recorder/fnc_eh_killed.sqf | 12 +- x/ocap2/addons/recorder/fnc_exportData.sqf | 23 ++- ...AmmoData.sqf => fnc_getAmmoMarkerData.sqf} | 12 +- .../recorder/fnc_getEventWeaponText.sqf | 59 ++---- x/ocap2/addons/recorder/fnc_getInstigator.sqf | 12 +- .../recorder/fnc_getWeaponDisplayData.sqf | 11 ++ x/ocap2/addons/recorder/fnc_handleMarkers.sqf | 23 +-- x/ocap2/addons/recorder/fnc_init.sqf | 121 +++++------- .../recorder/fnc_projectileMonitors.sqf | 170 +++++++++++++++++ 20 files changed, 601 insertions(+), 422 deletions(-) rename x/ocap2/addons/recorder/{fnc_getAmmoData.sqf => fnc_getAmmoMarkerData.sqf} (65%) create mode 100644 x/ocap2/addons/recorder/fnc_getWeaponDisplayData.sqf create mode 100644 x/ocap2/addons/recorder/fnc_projectileMonitors.sqf diff --git a/x/ocap2/addons/extension/config.cpp b/x/ocap2/addons/extension/config.cpp index 9edd0f1..b9abb22 100644 --- a/x/ocap2/addons/extension/config.cpp +++ b/x/ocap2/addons/extension/config.cpp @@ -17,6 +17,8 @@ class CfgPatches }; }; +cba_settings_whitelist[] = {"admin"}; + class Extended_PreInit_EventHandlers { class ADDON { // This will be executed once in 3DEN, main menu and before briefing has started for every mission diff --git a/x/ocap2/addons/main/config.cpp b/x/ocap2/addons/main/config.cpp index f0ff80a..8316bd7 100644 --- a/x/ocap2/addons/main/config.cpp +++ b/x/ocap2/addons/main/config.cpp @@ -17,6 +17,8 @@ class CfgPatches }; }; +cba_settings_whitelist[] = {"admin"}; + class Extended_PreInit_EventHandlers { class ADDON { // This will be executed once in 3DEN, main menu and before briefing has started for every mission diff --git a/x/ocap2/addons/recorder/XEH_preInit.sqf b/x/ocap2/addons/recorder/XEH_preInit.sqf index d73a536..0d7ffd2 100644 --- a/x/ocap2/addons/recorder/XEH_preInit.sqf +++ b/x/ocap2/addons/recorder/XEH_preInit.sqf @@ -115,20 +115,6 @@ GVAR(allSettings) = [ false // requires restart to apply ], - [ - QEGVAR(settings,pauseOnEmpty), - "CHECKBOX", // setting type - [ - "Auto-Save When No Players", // Pretty name shown inside the ingame settings menu. Can be stringtable entry. - "Will automatically save recording when there are 0 players on the server and existing data accounts for more time than the minumum save duration setting. Default: true" - ], - [COMPONENT_NAME, "Recording Settings"], // Pretty name of the category where the setting can be found. Can be stringtable entry. - true, // default enabled - true, // "_isGlobal" flag. Set this to true to always have this setting synchronized between all clients in multiplayer - {}, // function that will be executed once on mission start and every time the setting is changed. - false // requires restart to apply - ], - [ QEGVAR(settings,trackTimes), "CHECKBOX", // setting type @@ -194,6 +180,20 @@ GVAR(allSettings) = [ false // requires restart to apply ], + [ + QEGVAR(settings,saveOnEmpty), + "CHECKBOX", // setting type + [ + "Auto-Save When No Players", // Pretty name shown inside the ingame settings menu. Can be stringtable entry. + "Will automatically save recording when there are 0 players on the server and existing data accounts for more time than the minimum save duration setting. Default: true" + ], + [COMPONENT_NAME, "Recording Settings"], // Pretty name of the category where the setting can be found. Can be stringtable entry. + true, // default enabled + true, // "_isGlobal" flag. Set this to true to always have this setting synchronized between all clients in multiplayer + {}, // function that will be executed once on mission start and every time the setting is changed. + false // requires restart to apply + ], + [ QEGVAR(settings,minMissionTime), "SLIDER", // setting type diff --git a/x/ocap2/addons/recorder/XEH_prep.sqf b/x/ocap2/addons/recorder/XEH_prep.sqf index a7afbb9..7232104 100644 --- a/x/ocap2/addons/recorder/XEH_prep.sqf +++ b/x/ocap2/addons/recorder/XEH_prep.sqf @@ -23,8 +23,10 @@ PREP(eh_hit); PREP(eh_killed); PREP(getInstigator); PREP(getEventWeaponText); -PREP(getAmmoData); +PREP(getAmmoMarkerData); +PREP(getWeaponDisplayData); +PREP(projectileMonitors); PREP(aceThrowing); PREP(aceExplosives); diff --git a/x/ocap2/addons/recorder/config.cpp b/x/ocap2/addons/recorder/config.cpp index b43487e..455ebd1 100644 --- a/x/ocap2/addons/recorder/config.cpp +++ b/x/ocap2/addons/recorder/config.cpp @@ -17,6 +17,8 @@ class CfgPatches }; }; +cba_settings_whitelist[] = {"admin"}; + class Extended_PreInit_EventHandlers { class ADDON { // This will be executed once in 3DEN, main menu and before briefing has started for every mission diff --git a/x/ocap2/addons/recorder/fnc_aceExplosives.sqf b/x/ocap2/addons/recorder/fnc_aceExplosives.sqf index cdb1af0..1990e4b 100644 --- a/x/ocap2/addons/recorder/fnc_aceExplosives.sqf +++ b/x/ocap2/addons/recorder/fnc_aceExplosives.sqf @@ -41,6 +41,7 @@ EGVAR(listener,aceExplosives) = ["ace_explosives_place", { _explType = typeOf _explosive; _explosiveMag = getText(configFile >> "CfgAmmo" >> _explType >> "defaultMagazine"); _explosiveDisp = getText(configFile >> "CfgMagazines" >> _explosiveMag >> "displayName"); + _explosivePic = getText(configFile >> "CfgMagazines" >> _explosiveMag >> "picture"); _placedPos = getPosASL _explosive; _unit addOwnedMine _explosive; @@ -50,15 +51,17 @@ EGVAR(listener,aceExplosives) = ["ace_explosives_place", { _markColor = "ColorRed"; _markerType = "Minefield"; - if (GVARMAIN(isDebug)) then { - format["Created explosive placed marker, %1, %2", _markName, _explosiveDisp] SYSCHAT; - OCAPEXTLOG(ARR3("Created explosive placed marker", _markName, _explosiveDisp)); - }; - [QGVARMAIN(handleMarker), [ "CREATED", _markName, _unit, _placedPos, _markerType, "ICON", [1,1], 0, "Solid", "ColorRed", 1, _markTextLocal, true ]] call CBA_fnc_localEvent; + if (GVARMAIN(isDebug)) then { + // add to map draw array + private _debugArr = [_explosive, _explosivePic, format["%1 %2 - %3", str side group _unit, name _unit, _markTextLocal], [side group _unit] call BIS_fnc_sideColor]; + GVAR(liveDebugMagIcons) pushBack _debugArr; + publicVariable QGVAR(liveDebugMagIcons); + }; + [{isNull (_this#0)}, { // wait until the mine is null (exploded), and mark this for playback diff --git a/x/ocap2/addons/recorder/fnc_aceThrowing.sqf b/x/ocap2/addons/recorder/fnc_aceThrowing.sqf index 4fddecd..bd321d2 100644 --- a/x/ocap2/addons/recorder/fnc_aceThrowing.sqf +++ b/x/ocap2/addons/recorder/fnc_aceThrowing.sqf @@ -27,135 +27,103 @@ EGVAR(listener,aceThrowing) = ["ace_throwableThrown", { if (!SHOULDSAVEEVENTS) exitWith {}; - _this spawn { + params["_unit", "_projectile"]; - params["_unit", "_projectile"]; + if (isNull _projectile) then { + _projectile = nearestObject [_unit, "CA_Magazine"]; + }; - if (isNull _projectile) then { - _projectile = nearestObject [_unit, "CA_Magazine"]; - }; + // systemChat str _this; - // systemChat str _this; + // note that thrown objects outside of ACE explosives do not include a "default magazine" property in their config. + // this script will attempt to find a matching classname in CfgMagazines, as some chemlights and smokes are built this way. + // if not found, a default magazine value will be assigned (m67 frag, white smoke, green chemlight) - // note that thrown objects outside of ACE explosives do not include a "default magazine" property in their config. - // this script will attempt to find a matching classname in CfgMagazines, as some chemlights and smokes are built this way. - // if not found, a default magazine value will be assigned (m67 frag, white smoke, green chemlight) + _projType = typeOf _projectile; + _projConfig = configOf _projectile; + _projName = getText(configFile >> "CfgAmmo" >> _projType >> "displayName"); - _projType = typeOf _projectile; - _projConfig = configOf _projectile; - _projName = getText(configFile >> "CfgAmmo" >> _projType >> "displayName"); + // systemChat format["Config name: %1", configOf _projectile]; - if (GVARMAIN(isDebug)) then { - format["Detected ACE throwing of %1", _projName] SYSCHAT; - OCAPEXTLOG(ARR2("Detected ACE throwing of ", _projName)); - }; + _ammoSimType = getText(configFile >> "CfgAmmo" >> _projType >> "simulation"); + // systemChat format["Projectile type: %1", _ammoSimType]; - // systemChat format["Config name: %1", configOf _projectile]; + _markerType = ""; + _markColor = ""; + _magDisp = ""; + _magPic = ""; - _ammoSimType = getText(configFile >> "CfgAmmo" >> _projType >> "simulation"); - // systemChat format["Projectile type: %1", _ammoSimType]; + _magType = getText(_projConfig >> "defaultMagazine"); + if (_magType == "") then { + _magType = configName(configfile >> "CfgMagazines" >> _projType) + }; - _markerType = ""; - _markColor = ""; - _magDisp = ""; - _magPic = ""; + if (!(_magType isEqualTo "")) then { + // systemChat format["Mag type: %1", _magType]; - _magType = getText(_projConfig >> "defaultMagazine"); - if (_magType == "") then { - _magType = configName(configfile >> "CfgMagazines" >> _projType) + _magDisp = getText(configFile >> "CfgMagazines" >> _magType >> "displayNameShort"); + if (_magDisp == "") then { + _magDisp = getText(configFile >> "CfgMagazines" >> _magType >> "displayName") + }; + if (_magDisp == "") then { + _magDisp = _projName; }; - if (!(_magType isEqualTo "")) then { - // systemChat format["Mag type: %1", _magType]; - - _magDisp = getText(configFile >> "CfgMagazines" >> _magType >> "displayNameShort"); - if (_magDisp == "") then { - _magDisp = getText(configFile >> "CfgMagazines" >> _magType >> "displayName") - }; - if (_magDisp == "") then { - _magDisp = _projName; - }; - - _magPic = (getText(configfile >> "CfgMagazines" >> _magType >> "picture")); - // hint parseText format["Projectile fired:
", _magPic]; - if (_magPic == "") then { - _markerType = "mil_triangle"; - _markColor = "ColorRed"; - } else { - _magPicSplit = _magPic splitString "\"; - _magPic = _magPicSplit#((count _magPicSplit) - 1); - _markerType = format["magIcons/%1", _magPic]; - _markColor = "ColorWhite"; - }; - } else { + _magPic = (getText(configfile >> "CfgMagazines" >> _magType >> "picture")); + // hint parseText format["Projectile fired:
", _magPic]; + if (_magPic == "") then { _markerType = "mil_triangle"; _markColor = "ColorRed"; - // set defaults based on ammo sim type, if no magazine could be matched - switch (_ammoSimType) do { - case "shotGrenade":{ - _magPic = "\A3\Weapons_F\Data\UI\gear_M67_CA.paa"; - _magDisp = "Frag"; - }; - case "shotSmokeX":{ - _magPic = "\A3\Weapons_f\data\ui\gear_smokegrenade_white_ca.paa"; - _magDisp = "Smoke"; - }; - case "shotIlluminating":{ - _magPic = "\A3\Weapons_F\Data\UI\gear_flare_white_ca.paa"; - _magDisp = "Flare"; - }; - default { - _magPic = "\A3\Weapons_F\Data\UI\gear_M67_CA.paa"; - _magDisp = "Frag"; - }; - }; - // hint parseText format["Projectile fired:
", _magPic]; + } else { _magPicSplit = _magPic splitString "\"; _magPic = _magPicSplit#((count _magPicSplit) - 1); _markerType = format["magIcons/%1", _magPic]; _markColor = "ColorWhite"; }; + } else { + _markerType = "mil_triangle"; + _markColor = "ColorRed"; + // set defaults based on ammo sim type, if no magazine could be matched + switch (_ammoSimType) do { + case "shotGrenade":{ + _magPic = "\A3\Weapons_F\Data\UI\gear_M67_CA.paa"; + _magDisp = "Frag"; + }; + case "shotSmokeX":{ + _magPic = "\A3\Weapons_f\data\ui\gear_smokegrenade_white_ca.paa"; + _magDisp = "Smoke"; + }; + case "shotIlluminating":{ + _magPic = "\A3\Weapons_F\Data\UI\gear_flare_white_ca.paa"; + _magDisp = "Flare"; + }; + default { + _magPic = "\A3\Weapons_F\Data\UI\gear_M67_CA.paa"; + _magDisp = "Frag"; + }; + }; + // hint parseText format["Projectile fired:
", _magPic]; + _magPicSplit = _magPic splitString "\"; + _magPic = _magPicSplit#((count _magPicSplit) - 1); + _markerType = format["magIcons/%1", _magPic]; + _markColor = "ColorWhite"; + }; - if (!(_ammoSimType isEqualTo "shotBullet")) then { - - _int = random 2000; - - _markTextLocal = format["%1", _magDisp]; - _markName = format["Projectile#%1", _int]; - - _throwerPos = getPosASL _unit; - - [QGVARMAIN(handleMarker), ["CREATED", _markName, _unit, _throwerPos, _markerType, "ICON", [1,1], 0, "Solid", _markColor, 1, _markTextLocal, true]] call CBA_fnc_serverEvent; + _int = random 2000; - private _lastPos = []; - waitUntil { - _pos = getPosASL _projectile; - if (((_pos select 0) isEqualTo 0) || isNull _projectile) exitWith { - true - }; - _lastPos = _pos; - [QGVARMAIN(handleMarker), ["UPDATED", _markName, _unit, _pos, "", "", "", 0, "", "", 1]] call CBA_fnc_serverEvent; + _markTextLocal = format["%1", _magDisp]; + _markName = format["Projectile#%1", _int]; - // here, monitor fast moving missiles/rockets/shells every 0.3 seconds. monitor smokes, grenades, flares, mines in line with the configured frame capture delay - sleep (([0.3, GVAR(frameCaptureDelay)] select {_ammoSimType in ["ShotSmokeX","ShotGrenade","ShotIlluminating","ShotMine"]})#0); - false; - }; + // MAKE MARKER FOR PLAYBACK + _throwerPos = getPosASL _unit; + [QGVARMAIN(handleMarker), ["CREATED", _markName, _unit, _throwerPos, _markerType, "ICON", [1,1], 0, "Solid", _markColor, 1, _markTextLocal, true]] call CBA_fnc_serverEvent; - if !((count _lastPos) isEqualTo 0) then { - isNil { - // for non-bullets, set the last fired variable of the soldier so hits/kills are recorded accurately - if (_ammoSimType == "shotGrenade") then { - _unit setVariable [ - QGVARMAIN(lastFired), - _projName - ]; - }; - }; - [QGVARMAIN(handleMarker), ["UPDATED", _markName, _unit, _lastPos, "", "", "", 0, "", "", 1]] call CBA_fnc_serverEvent; - }; + GVAR(liveGrenades) pushBack [_projectile, _magazine, _unit, getPosASL _projectile, _markName, _markTextLocal, _ammoSimType]; - sleep 10; - [QGVARMAIN(handleMarker), ["DELETED", _markName]] call CBA_fnc_serverEvent; - }; + if (GVARMAIN(isDebug)) then { + // add to map draw array + private _debugArr = [_projectile, _magPic, format["%1 %2 - %3", str side group _unit, name _unit, _markTextLocal], [side group _unit] call BIS_fnc_sideColor]; + GVAR(liveDebugMagIcons) pushBack _debugArr; + publicVariable QGVAR(liveDebugMagIcons); }; }] call CBA_fnc_addEventHandler; diff --git a/x/ocap2/addons/recorder/fnc_addEventMission.sqf b/x/ocap2/addons/recorder/fnc_addEventMission.sqf index f837861..66349d3 100644 --- a/x/ocap2/addons/recorder/fnc_addEventMission.sqf +++ b/x/ocap2/addons/recorder/fnc_addEventMission.sqf @@ -25,80 +25,118 @@ Author: ---------------------------------------------------------------------------- */ #include "script_component.hpp" -addMissionEventHandler["HandleDisconnect", { - _this call FUNC(eh_disconnected); -}]; +if (isNil QEGVAR(EH,HandleDisconnect)) then { + addMissionEventHandler["HandleDisconnect", { + _this call FUNC(eh_disconnected); + }]; + OCAPEXTLOG(["Initialized HandleDisconnect EH"]); +}; -addMissionEventHandler["PlayerConnected", { - _this call FUNC(eh_connected); -}]; +if (isNil QEGVAR(EH,PlayerConnected)) then { + addMissionEventHandler["PlayerConnected", { + _this call FUNC(eh_connected); + }]; + OCAPEXTLOG(["Initialized PlayerConnected EH"]); +}; -addMissionEventHandler ["EntityKilled", { - _this call FUNC(eh_killed); -}]; +if (isNil QEGVAR(EH,EntityKilled)) then { + addMissionEventHandler ["EntityKilled", { + _this call FUNC(eh_killed); + }]; + OCAPEXTLOG(["Initialized EntityKilled EH"]); +}; -addMissionEventHandler ["EntityRespawned", { - params ["_entity", "_corpse"]; +if (isNil QEGVAR(EH,EntityRespawned)) then { + EGVAR(EH,EntityRespawned) = addMissionEventHandler ["EntityRespawned", { + params ["_entity", "_corpse"]; - // Reset unit back to normal - _entity setvariable [QGVARMAIN(isKilled), false]; + // Reset unit back to normal + _entity setvariable [QGVARMAIN(isKilled), false]; - // Stop tracking old unit - if (_corpse getVariable [QGVARMAIN(isInitialized), false]) then { - _corpse setVariable [QGVARMAIN(exclude), true]; + // Stop tracking old unit + if (_corpse getVariable [QGVARMAIN(isInitialized), false]) then { + _corpse setVariable [QGVARMAIN(exclude), true]; - [_entity, true] spawn FUNC(addUnitEventHandlers); - }; -}]; + [_entity, true] spawn FUNC(addUnitEventHandlers); + }; + }]; + OCAPEXTLOG(["Initialized EntityRespawned EH"]); +}; // Listen for global ACE Explosive placement events if (isClass (configFile >> "CfgPatches" >> "ace_explosives")) then { - call FUNC(aceExplosives); + if (isNil QEGVAR(listener,aceExplosives)) then { + call FUNC(aceExplosives); + OCAPEXTLOG(["Initialized ACE Explosives listener"]); + }; }; -// Listen for local ACE Throwing events, for any units owned by the server +// Listen for global ACE Throwing events that take place when a throwable is primed. use existing firedMan code, since identical args if (isClass (configFile >> "CfgPatches" >> "ace_advanced_throwing")) then { - call FUNC(aceThrowing); + if (isNil QEGVAR(listener,aceThrowing)) then { + ["ace_advanced_throwing_throwFiredXEH", {_this call FUNC(eh_firedMan)}] call CBA_fnc_addEventHandler; + OCAPEXTLOG(["Initialized ACE Throwing listener"]); + }; }; -addMissionEventHandler ["MPEnded", { - if (EGVAR(settings,saveMissionEnded) && (GVAR(captureFrameNo) * GVAR(frameCaptureDelay)) >= GVAR(minMissionTime)) then { - ["Mission ended automatically"] call FUNC(exportData); - }; -}]; +if (isNil QEGVAR(EH,MPEnded)) then { + EGVAR(EH,MPEnded) = addMissionEventHandler ["MPEnded", { + if (EGVAR(settings,saveMissionEnded) && (GVAR(captureFrameNo) * GVAR(frameCaptureDelay)) >= GVAR(minMissionTime)) then { + ["Mission ended automatically"] call FUNC(exportData); + }; + }]; + OCAPEXTLOG(["Initialized MPEnded EH"]); +}; -addMissionEventHandler ["Ended", { - if (EGVAR(settings,saveMissionEnded) && (GVAR(captureFrameNo) * GVAR(frameCaptureDelay)) >= GVAR(minMissionTime)) then { - ["Mission ended automatically"] call FUNC(exportData); - }; -}]; +if (isNil QEGVAR(EH,Ended)) then { + EGVAR(EH,Ended) = addMissionEventHandler ["Ended", { + if (EGVAR(settings,saveMissionEnded) && (GVAR(captureFrameNo) * GVAR(frameCaptureDelay)) >= GVAR(minMissionTime)) then { + ["Mission ended automatically"] call FUNC(exportData); + }; + }]; + OCAPEXTLOG(["Initialized Ended EH"]); +}; // Add event saving markers -call FUNC(handleMarkers); +if (isNil QEGVAR(listener,markers)) then { + call FUNC(handleMarkers); +}; // Custom event handler with key "ocap2_customEvent" // Used for showing custom events in playback events list -EGVAR(listener,customEvent) = [QGVARMAIN(customEvent), { - _this call FUNC(handleCustomEvent); -}] call CBA_fnc_addEventHandler; +if (isNil QEGVAR(listener,customEvent)) then { + EGVAR(listener,customEvent) = [QGVARMAIN(customEvent), { + _this call FUNC(handleCustomEvent); + }] call CBA_fnc_addEventHandler; + OCAPEXTLOG(["Initialized customEvent listener"]); +}; // Custom event handler with key "ocap2_record" // This will START OR RESUME recording if not already. -EGVAR(listener,exportData) = [QGVARMAIN(record), { - call FUNC(startRecording); -}] call CBA_fnc_addEventHandler; +if (isNil QEGVAR(listener,record)) then { + EGVAR(listener,record) = [QGVARMAIN(record), { + call FUNC(startRecording); + }] call CBA_fnc_addEventHandler; + OCAPEXTLOG(["Initialized record listener"]); +}; // Custom event handler with key "ocap2_pause" // This will PAUSE recording -EGVAR(listener,exportData) = [QGVARMAIN(pause), { - GVAR(recording) = false; - publicVariable QGVAR(recording); -}] call CBA_fnc_addEventHandler; +if (isNil QEGVAR(listener,pause)) then { + EGVAR(listener,pause) = [QGVARMAIN(pause), { + GVAR(recording) = false; + publicVariable QGVAR(recording); + }] call CBA_fnc_addEventHandler; + OCAPEXTLOG(["Initialized pause listener"]); +}; // Custom event handler with key "ocap2_exportData" // This will export the mission immediately regardless of restrictions. // params ["_side", "_message", "_tag"]; -EGVAR(listener,exportData) = [QGVARMAIN(exportData), { - _this set [3, true]; - _this call FUNC(exportData); -}] call CBA_fnc_addEventHandler; +if (isNil QEGVAR(listener,exportData)) then { + EGVAR(listener,exportData) = [QGVARMAIN(exportData), { + _this set [3, true]; + _this call FUNC(exportData); + }] call CBA_fnc_addEventHandler; + OCAPEXTLOG(["Initialized exportData listener"]); +}; diff --git a/x/ocap2/addons/recorder/fnc_addUnitEventHandlers.sqf b/x/ocap2/addons/recorder/fnc_addUnitEventHandlers.sqf index bd28b62..aafb014 100644 --- a/x/ocap2/addons/recorder/fnc_addUnitEventHandlers.sqf +++ b/x/ocap2/addons/recorder/fnc_addUnitEventHandlers.sqf @@ -31,29 +31,18 @@ params ["_entity", ["_respawn", false]]; // FIREDMAN if ((_entity call BIS_fnc_objectType) # 0 == "Soldier") then { - _entity addEventHandler ["FiredMan", { _this call FUNC(eh_firedMan); }]; + if (isNil {_entity getVariable QGVARMAIN(FiredManEH)}) then { + _entity setVariable [ + QGVARMAIN(FiredManEH), + _entity addEventHandler ["FiredMan", { _this call FUNC(eh_firedMan); }] + ]; + }; }; // MPHIT -_entity addMPEventHandler ["MPHit", { _this spawn FUNC(eh_hit); }]; - -private _ownerId = owner _entity; -if ( - !_respawn && // exclude re-adding on respawn, as the CBA listener will already be present on the owner's machine - (_entity call BIS_fnc_objectType) # 0 == "Soldier" && - _ownerId != 2 && // only add to entities not owned by server, as otherwise server will receive local events already - isClass (configFile >> "CfgPatches" >> "ace_advanced_throwing") -) then { - // here, we must place a local listener of ACE throw events on the owner of the entity - // the client will then notify the server when these local events happen - // we'll wait a max of 15 secs until that owner is at least to briefing stage, has completed PostInit, before sending - [ - {(getUserInfo (_this#0)) select 6 > 8}, - { - FUNC(aceThrowing) remoteExec ["call", _this#1]; - OCAPEXTLOG(ARR3("ADD ACE THROWING LISTENER", _this#0, _this#1)); - }, - [_ownerId, _entity], - 15 - ] call CBA_fnc_waitUntilAndExecute; +if (isNil {_entity getVariable QGVARMAIN(MPHitEH)}) then { + _entity setVariable [ + QGVARMAIN(MPHitEH), + _entity addMPEventHandler ["MPHit", { _this call FUNC(eh_hit); }] + ]; }; diff --git a/x/ocap2/addons/recorder/fnc_eh_firedMan.sqf b/x/ocap2/addons/recorder/fnc_eh_firedMan.sqf index 690efa8..0450ec2 100644 --- a/x/ocap2/addons/recorder/fnc_eh_firedMan.sqf +++ b/x/ocap2/addons/recorder/fnc_eh_firedMan.sqf @@ -33,22 +33,27 @@ if (!SHOULDSAVEEVENTS) exitWith {}; params ["_firer", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile", "_vehicle"]; +// not sent in ACE Throwing events +if (isNil "_vehicle") then {_vehicle = objNull}; + private _frame = GVAR(captureFrameNo); private _firerId = (_firer getVariable [QGVARMAIN(id), -1]); if (_firerId == -1) exitWith {}; // set the firer's lastFired var as this weapon, so subsequent kills are logged accurately -isNil { - _firer setVariable [ - QGVARMAIN(lastFired), - format[ - "%1 [%2]", - getText (configFile >> "CfgWeapons" >> _weapon >> "displayName"), - getText (configFile >> "CfgWeapons" >> _muzzle >> "displayName") - ] - ]; +([_weapon, _muzzle, _magazine, _ammo] call FUNC(getWeaponDisplayData)) params ["_muzzleDisp", "_magDisp"]; + +private _wepString = ""; +if (_muzzleDisp find _magDisp == 1 && _magDisp isNotEqualTo "") then { + _wepString = format["%1 [%2]", _muzzleDisp, _magDisp]; +} else { + _wepString = _muzzleDisp; +}; +if (vehicle _firer != _firer) then { + _wepString = format["%1 [%2]", (configOf (vehicle _firer)) call BIS_fnc_displayName, _wepString]; }; +_firer setVariable [QGVARMAIN(lastFired), _wepString]; _ammoSimType = getText(configFile >> "CfgAmmo" >> _ammo >> "simulation"); // _ammoSimType @@ -60,39 +65,56 @@ _ammoSimType = getText(configFile >> "CfgAmmo" >> _ammo >> "simulation"); // "ShotIlluminating" // 40mm_green Flare // "ShotSmokeX"; // M18 Smoke +#define LOGBULLET GVAR(liveBullets) pushBack [_projectile, _firerId, _firer, getPosASL _projectile] +#define LOGMISSILE GVAR(liveMissiles) pushBack [_projectile, _wepString, _firer, getPosASL _projectile, _markName, _markTextLocal] +#define LOGGRENADE GVAR(liveGrenades) pushBack [_projectile, _wepString, _firer, getPosASL _projectile, _markName, _markTextLocal, _ammoSimType]; -switch (_ammoSimType) do { +switch (true) do { case (_ammoSimType isEqualTo "shotBullet"): { // [_projectile, _firer, _frame, _ammoSimType, _ammo] spawn { // params["_projectile", "_firer", "_frame", "_ammoSimType", "_ammo"]; if (isNull _projectile) then { _projectile = nearestObject [_firer, _ammo]; }; - GVAR(liveBullets) pushBack [_projectile, _firerId, _frame, getPosASL _projectile]; + if (isNil "_projectile") exitWith {}; + LOGBULLET; }; case (_ammoSimType isNotEqualTo "shotSubmunitions"): { - // MAKE MARKER FOR PLAYBACK - _firerPos = getPosASL _firer; - [QGVARMAIN(handleMarker), ["CREATED", _markName, _firer, _firerPos, _markerType, "ICON", [1,1], getDirVisual _firer, "Solid", _markColor, 1, _markTextLocal, true]] call CBA_fnc_localEvent; - if (isNull _projectile) then { _projectile = nearestObject [_firer, _ammo]; + _this set [6, _projectile]; }; + ([_weapon, _muzzle, _ammo, _magazine, _projectile, _vehicle, _ammoSimType] call FUNC(getAmmoMarkerData)) params ["_markTextLocal","_markName","_markColor","_markerType"]; + private _magIcon = getText(configFile >> "CfgMagazines" >> _magazine >> "picture"); + + // MAKE MARKER FOR PLAYBACK + _firerPos = getPosASL _firer; + [QGVARMAIN(handleMarker), ["CREATED", _markName, _firer, _firerPos, _markerType, "ICON", [1,1], getDirVisual _firer, "Solid", _markColor, 1, _markTextLocal, true]] call CBA_fnc_localEvent; + switch (true) do { - case (_ammoSimType isEqualTo "shotBullet"): { - GVAR(liveBullets) pushBack [_projectile, _firerId, _frame, getPosASL _projectile]; - }; case (_ammoSimType in ["shotMissile", "shotRocket", "shotShell"]): { - GVAR(liveMissiles) pushBack [_projectile, _magazine, _firer, getPosASL _projectile, _markName]; + LOGMISSILE; + + if (GVARMAIN(isDebug)) then { + // add to map draw array + private _debugArr = [_projectile, _magIcon, format["%1 %2 - %3", str side group _firer, name _firer, _markTextLocal], [side group _firer] call BIS_fnc_sideColor]; + [QGVAR(addDebugMagIcon), _debugArr] call CBA_fnc_globalEvent; + }; }; case (_ammoSimType in ["shotGrenade", "shotIlluminating", "shotMine", "shotSmokeX"]): { - GVAR(liveGrenades) pushBack [_projectile, _magazine, _firer, getPosASL _projectile, _markName, _ammoSimType]; + LOGGRENADE; + + if (GVARMAIN(isDebug)) then { + // add to map draw array + private _debugArr = [_projectile, _magIcon, format["%1 %2 - %3", str side group _firer, name _firer, _markTextLocal], [side group _firer] call BIS_fnc_sideColor]; + [QGVAR(addDebugMagIcon), _debugArr] call CBA_fnc_globalEvent; + }; }; - default {OCAPEXTLOG(ARR3("Invalid ammo sim type, check it", _projectile, _newAmmoSimType))}; + default {OCAPEXTLOG(ARR3("Invalid ammo sim type, check it", _projectile, _ammoSimType))}; }; }; @@ -106,34 +128,77 @@ switch (_ammoSimType) do { // once we have that, we'll add it to the bullet tracking array for positions so a fireline is drawn in playback private _simDelay = (configFile >> "CfgAmmo" >> _ammo >> "simulationStep") call BIS_fnc_getCfgData; private _subTypes = ((configFile >> "CfgAmmo" >> _ammo >> "submunitionAmmo") call BIS_fnc_getCfgDataArray) select {_x isEqualType ""}; - [{ - params ["_EHData", "_subTypes", "_magazine", "_firer", "_firerId", "_firerPos", "_frame"]; - private _projectile = objNull; - while {isNull _projectile} do { - { - _projSearch = nearestObject [_firer, _x]; - if !(isNull _projSearch) exitWith {_projectile = _projSearch}; - } forEach _subTypes; - }; - - // get marker details based on original EH data - (_EHData call FUNC(getAmmoData)) params ["_markTextLocal","_markName","_markColor","_markerType"]; - // create our marker record in the timeline - [QGVARMAIN(handleMarker), ["CREATED", _markName, _firer, getPosASL _firer, _markerType, "ICON", [1,1], getDir _firer, "Solid", _markColor, 1, _markTextLocal, true]] call CBA_fnc_localEvent; - - private _newAmmoSimType = getText(configFile >> "CfgAmmo" >> _projectile >> "simulation"); - switch (true) do { - case (_newAmmoSimType isEqualTo "shotBullet"): { - GVAR(liveBullets) pushBack [_projectile, _firerId, _frame, getPosASL _projectile]; - }; - case (_newAmmoSimType in ["shotMissile", "shotRocket", "shotShell"]): { - GVAR(liveMissiles) pushBack [_projectile, _magazine, _firer, getPosASL _projectile, _markName]; + private _subTypesAmmoSimType = _subTypes apply {(configFile >> "CfgAmmo" >> _x >> "simulation") call BIS_fnc_getCfgData}; + if (count _subTypesAmmoSimType > 0) then { + _subTypesAmmoSimType = selectRandom(_subTypesAmmoSimType); + }; + [ + { + params ["_EHData", "_subTypes", "_magazine", "_wepString", "_firer", "_firerId", "_firerPos", "_frame", "_subTypesAmmoSimType"]; + private _projectile = _EHData # 6; + + // if submunitions are NOT bullets, wait until the original projectile deploys then search for submunitions and 're-fire' them to appear in playback + // if !(_subTypesAmmoSimType == "shotBullet") exitWith { + // if (_magazine isKindOf "VehicleMagazine") then { + // [_EHData, _projectile, _subTypes] spawn { + // params ["_EHData", "_projectile", "_subTypes"]; + // private _ogPos = getPos _firer; + // while {!isNull _projectile} do {_ogPos = getPosASL _projectile; sleep 0.1;}; + // isNil { + // _projSearch = nearestObjects [ASLtoAGL _ogPos, _subTypes, 50, false]; + // { + // _EHData set [6, _x]; + // _EHData spawn FUNC(eh_firedMan); + // } forEach _projSearch; + // }; + // }; + // }; + + // if submunitions ARE bullets, process normally and just look for one item + while {isNull _projectile} do { + { + _projSearch = nearestObject [_firer, _x]; + if !(isNull _projSearch) exitWith {_projectile = _projSearch}; + } forEach _subTypes; + sleep 0.1 }; - case (_newAmmoSimType in ["shotGrenade", "shotIlluminating", "shotMine", "shotSmokeX"]): { - GVAR(liveGrenades) pushBack [_projectile, _magazine, _firer, getPosASL _projectile, _markName, _newAmmoSimType]; + + // get marker details based on original EH data + ([_weapon, _muzzle, _ammo, _magazine, _projectile, _vehicle, _ammoSimType] call FUNC(getAmmoMarkerData)) params ["_markTextLocal","_markName","_markColor","_markerType"]; + private _magIcon = getText(configFile >> "CfgMagazines" >> _magazine >> "picture"); + + // create our marker record in the timeline + [QGVARMAIN(handleMarker), ["CREATED", _markName, _firer, getPosASL _firer, _markerType, "ICON", [1,1], getDir _firer, "Solid", _markColor, 1, _markTextLocal, true]] call CBA_fnc_localEvent; + + // then get data of submunition to determine how to track it + private _ammoSimType = getText(configFile >> "CfgAmmo" >> (typeOf _projectile) >> "simulation"); + switch (true) do { + case (_ammoSimType isEqualTo "shotBullet"): { + LOGBULLET; + }; + case (_ammoSimType in ["shotMissile", "shotRocket", "shotShell"]): { + LOGMISSILE; + + if (GVARMAIN(isDebug)) then { + // add to clients' map draw array + private _debugArr = [_projectile, _magIcon, format["%1 %2 - %3", str side group _firer, name _firer, _markTextLocal], [side group _firer] call BIS_fnc_sideColor]; + [QGVAR(addDebugMagIcon), _debugArr] call CBA_fnc_globalEvent; + }; + }; + case (_ammoSimType in ["shotGrenade", "shotIlluminating", "shotMine", "shotSmokeX"]): { + LOGGRENADE; + + if (GVARMAIN(isDebug)) then { + // add to map draw array + private _debugArr = [_projectile, _magIcon, format["%1 %2 - %3", str side group _firer, name _firer, _markTextLocal], [side group _firer] call BIS_fnc_sideColor]; + [QGVAR(addDebugMagIcon), _debugArr] call CBA_fnc_globalEvent; + }; + }; + default {OCAPEXTLOG(ARR3("Invalid ammo sim type, check it", _projectile, _ammoSimType))}; }; - default {OCAPEXTLOG(ARR3("Invalid ammo sim type, check it", _projectile, _newAmmoSimType))}; - }; - }, [_this, _subTypes, _magazine, _firer, _firerId, _firerPos, _frame], _simDelay] call CBA_fnc_waitAndExecute; + }, + [_this, _subTypes, _magazine, _wepString, _firer, _firerId, _firerPos, _frame, _subTypesAmmoSimType], + _simDelay + ] call CBA_fnc_waitAndExecute; }; }; diff --git a/x/ocap2/addons/recorder/fnc_eh_hit.sqf b/x/ocap2/addons/recorder/fnc_eh_hit.sqf index 8ba40b4..3640db8 100644 --- a/x/ocap2/addons/recorder/fnc_eh_hit.sqf +++ b/x/ocap2/addons/recorder/fnc_eh_hit.sqf @@ -48,7 +48,7 @@ params ["_unit", "_causedBy", "_damage", "_instigator"]; private _causedByInfo = []; private _distanceInfo = 0; - if (_causedBy isKindOf "CAManBase" && _causedById > -1) then { + if (_causedById > -1 && _causedBy isEqualTo _instigator) then { _causedByInfo = [ _causedById, ([_causedBy] call FUNC(getEventWeaponText)) @@ -59,19 +59,14 @@ params ["_unit", "_causedBy", "_damage", "_instigator"]; OCAPEXTLOG(ARR4("HIT EVENT", _hitFrame, _unitID, _causedById)); }; } else { - if (!isNull _instigator && _causedBy != _instigator && _instigator isKindOf "CAManBase" && _instigatorId > -1) then { - _causedByInfo = [ - _instigatorId, - ([_instigator] call FUNC(getEventWeaponText)) - ]; - _distanceInfo = round (_unit distance _instigator); - - if (GVARMAIN(isDebug)) then { - OCAPEXTLOG(ARR4("HIT EVENT", _hitFrame, _unitID, _instigatorId)); - }; - } else { - _causedByInfo = [_causedById]; - _distanceInfo = round (_unit distance _causedBy); + _causedByInfo = [ + _instigatorId, + ([_instigator] call FUNC(getEventWeaponText)) + ]; + _distanceInfo = round (_unit distance _instigator); + + if (GVARMAIN(isDebug)) then { + OCAPEXTLOG(ARR4("HIT EVENT", _hitFrame, _unitID, _instigatorId)); }; }; _eventData = [ diff --git a/x/ocap2/addons/recorder/fnc_eh_killed.sqf b/x/ocap2/addons/recorder/fnc_eh_killed.sqf index 42aff77..755df62 100644 --- a/x/ocap2/addons/recorder/fnc_eh_killed.sqf +++ b/x/ocap2/addons/recorder/fnc_eh_killed.sqf @@ -37,6 +37,10 @@ if !(_victim getvariable [QGVARMAIN(isKilled),false]) then { private _killedFrame = GVAR(captureFrameNo); + // allow some time for last-fired variable on killer to be updated + // namely for explosives, shells, grenades explosions, which are updated on impact + sleep GVAR(frameCaptureDelay); + if (_killer == _victim && owner _victim != 2 && EGVAR(settings,preferACEUnconscious) && isClass(configFile >> "CfgPatches" >> "ace_medical_status")) then { private _time = diag_tickTime; [_victim, { @@ -62,14 +66,14 @@ if !(_victim getvariable [QGVARMAIN(isKilled),false]) then { if (_killerId == -1) exitWith {}; private _killerInfo = []; - if (_instigator isKindOf "CAManBase") then { + // if (_instigator isKindOf "CAManBase") then { _killerInfo = [ _killerId, ([_instigator] call FUNC(getEventWeaponText)) ]; - } else { - _killerInfo = [_killerId]; - }; + // } else { + // _killerInfo = [_killerId]; + // }; _eventData = [ _killedFrame, diff --git a/x/ocap2/addons/recorder/fnc_exportData.sqf b/x/ocap2/addons/recorder/fnc_exportData.sqf index 2987ed1..a383f4b 100644 --- a/x/ocap2/addons/recorder/fnc_exportData.sqf +++ b/x/ocap2/addons/recorder/fnc_exportData.sqf @@ -65,7 +65,7 @@ if (isNil QGVAR(startTime)) exitWith { _elapsedTime = time - GVAR(startTime); -_frameTimeDuration = GVAR(frameCaptureDelay) * GVAR(captureFrameNo); +_frameTimeDuration = (GVAR(frameCaptureDelay) * GVAR(captureFrameNo)) * 60; TRACE_7("Save attempted. Elapsed Time =", _elapsedTime," Frame Count * Delay Duration =", _frameTimeDuration," delta =", _elapsedTime - _frameTimeDuration); @@ -93,29 +93,36 @@ if (_frameTimeDuration < GVAR(minMissionTime) && !_overrideLimits) exitWith { GVAR(recording) = false; publicVariable QGVAR(recording); -GVAR(endFrameNumber) = GVAR(captureFrameNo); +private _endFrameNumber = GVAR(captureFrameNo); -publicVariable QGVAR(endFrameNumber); +// reset vars in case a new recording is started +GVAR(captureFrameNo) = nil; +GVAR(startTime) = nil; + +// TO DO HEREEEEEE if (isNil "_side") then { - [":EVENT:", [GVAR(endFrameNumber), "endMission", ["", "Mission ended"]]] call EFUNC(extension,sendData); + [":EVENT:", [_endFrameNumber, "endMission", ["", "Mission ended"]]] call EFUNC(extension,sendData); +}; +if (isNil "_side" && !isNil "_message") then { + [":EVENT:", [_endFrameNumber, "endMission", ["", _message]]] call EFUNC(extension,sendData); }; if (!isNil "_side" && isNil "_message") then { - [":EVENT:", [GVAR(endFrameNumber), "endMission", ["", _side]]] call EFUNC(extension,sendData); + [":EVENT:", [_endFrameNumber, "endMission", ["", _side]]] call EFUNC(extension,sendData); }; if (!isNil "_side" && !isNil "_message") then { private _sideString = str(_side); if (_side == sideUnknown) then { _sideString = "" }; - [":EVENT:", [GVAR(endFrameNumber), "endMission", [_sideString, _message]]] call EFUNC(extension,sendData); + [":EVENT:", [_endFrameNumber, "endMission", [_sideString, _message]]] call EFUNC(extension,sendData); }; if (!isNil "_tag") then { - [":SAVE:", [worldName, GVAR(missionName), getMissionConfigValue ["author", ""], GVAR(frameCaptureDelay), GVAR(endFrameNumber), _tag]] call EFUNC(extension,sendData); + [":SAVE:", [worldName, GVAR(missionName), getMissionConfigValue ["author", ""], GVAR(frameCaptureDelay), _endFrameNumber, _tag]] call EFUNC(extension,sendData); OCAPEXTLOG(ARR4("Saved recording of mission", GVAR(missionName), "with tag", _tag)); } else {// default tag to configured setting - [":SAVE:", [worldName, GVAR(missionName), getMissionConfigValue ["author", ""], GVAR(frameCaptureDelay), GVAR(endFrameNumber), EGVAR(settings,saveTag)]] call EFUNC(extension,sendData); + [":SAVE:", [worldName, GVAR(missionName), getMissionConfigValue ["author", ""], GVAR(frameCaptureDelay), _endFrameNumber, EGVAR(settings,saveTag)]] call EFUNC(extension,sendData); OCAPEXTLOG(ARR3("Saved recording of mission", GVAR(missionName), "with default tag")); }; diff --git a/x/ocap2/addons/recorder/fnc_getAmmoData.sqf b/x/ocap2/addons/recorder/fnc_getAmmoMarkerData.sqf similarity index 65% rename from x/ocap2/addons/recorder/fnc_getAmmoData.sqf rename to x/ocap2/addons/recorder/fnc_getAmmoMarkerData.sqf index e22ff7b..5cadad4 100644 --- a/x/ocap2/addons/recorder/fnc_getAmmoData.sqf +++ b/x/ocap2/addons/recorder/fnc_getAmmoMarkerData.sqf @@ -1,13 +1,9 @@ -params ["_firer", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile", "_vehicle"]; +#include "script_component.hpp" + +params ["_weapon", "_muzzle", "_ammo", "_magazine", "_projectile", "_vehicle", "_ammoSimType"]; _int = random 2000; -_muzzleDisp = getText(configFile >> "CfgWeapons" >> _weapon >> _muzzle >> "displayName"); -if (_muzzleDisp == "") then {_muzzleDisp = getText(configFile >> "CfgWeapons" >> _weapon >> "displayNameShort")}; -if (_muzzleDisp == "") then {_muzzleDisp = getText(configFile >> "CfgWeapons" >> _weapon >> "displayName")}; -_magDisp = getText(configFile >> "CfgMagazines" >> _magazine >> "displayNameShort"); -if (_magDisp == "") then {_magDisp = getText(configFile >> "CfgMagazines" >> _magazine >> "displayName")}; -if (_magDisp == "") then {_magDisp = getText(configFile >> "CfgAmmo" >> _ammo >> "displayNameShort")}; -if (_magDisp == "") then {_magDisp = getText(configFile >> "CfgAmmo" >> _ammo >> "displayName")}; +([_weapon, _muzzle, _magazine, _ammo] call FUNC(getWeaponDisplayData)) params ["_muzzleDisp", "_magDisp"]; // non-bullet handling private ["_markTextLocal"]; diff --git a/x/ocap2/addons/recorder/fnc_getEventWeaponText.sqf b/x/ocap2/addons/recorder/fnc_getEventWeaponText.sqf index 04dbc45..1f9678e 100644 --- a/x/ocap2/addons/recorder/fnc_getEventWeaponText.sqf +++ b/x/ocap2/addons/recorder/fnc_getEventWeaponText.sqf @@ -27,48 +27,29 @@ Author: params ["_instigator"]; -if (vehicle _instigator isEqualTo _instigator) exitWith { +if (isNull _instigator) exitWith {""}; + +if !(_instigator call CBA_fnc_isPerson) then { + _instigator = _instigator call { + if(alive(gunner _this))exitWith{gunner _this}; + if(alive(commander _this))exitWith{commander _this}; + if(alive(driver _this))exitWith{driver _this}; + effectiveCommander _this + }; +}; + +if (_instigator call CBA_fnc_isPerson) then { + (weaponstate _instigator) params ["_weapon", "_muzzle", "_mode", "_magazine"]; + ([_weapon, _muzzle, _magazine] call FUNC(getWeaponDisplayData)) params ["_muzDisp", "_magDisp"]; + _instigator getVariable [ QGVARMAIN(lastFired), - format[ + format [ "%1 [%2]", - getText (configFile >> "CfgWeapons" >> currentWeapon _instigator >> "displayName"), - getText (configFile >> "CfgWeapons" >> currentMuzzle _instigator >> "displayName") + _muzDisp, + _magDisp ] - ] -}; - -// pilot/driver doesn't return a value, so check for this -private _turPath = []; -if (count (assignedVehicleRole _instigator) > 1) then { - _turPath = assignedVehicleRole _instigator select 1; + ]; } else { - _turPath = [-1]; + getText(configFile >> "CfgVehicles" >> (typeOf vehicle _instigator) >> "displayName"); }; - -private _curVic = getText(configFile >> "CfgVehicles" >> (typeOf vehicle _instigator) >> "displayName"); -(weaponstate [vehicle _instigator, _turPath]) params ["_curWep", "_curMuzzle", "_curFiremode", "_curMag"]; -private _curWepDisplayName = getText(configFile >> "CfgWeapons" >> _curWep >> "displayName"); -private _curMagDisplayName = getText(configFile >> "CfgMagazines" >> _curMag >> "displayName"); -private _text = _curVic; -if (count _curMagDisplayName < 22) then { - if !(_curWepDisplayName isEqualTo "") then { - _text = _text + " [" + _curWepDisplayName; - if !(_curMagDisplayName isEqualTo "") then { - _text = _text + " / " + _curMagDisplayName + "]"; - } else { - _text = _text + "]" - }; - }; -} else { - if !(_curWepDisplayName isEqualTo "") then { - _text = _text + " [" + _curWepDisplayName; - if (_curWep != _curMuzzle && !(_curMuzzle isEqualTo "")) then { - _text = _text + " / " + _curMuzzle + "]"; - } else { - _text = _text + "]"; - }; - }; -}; - -_text; diff --git a/x/ocap2/addons/recorder/fnc_getInstigator.sqf b/x/ocap2/addons/recorder/fnc_getInstigator.sqf index 77d3376..2d66c7e 100644 --- a/x/ocap2/addons/recorder/fnc_getInstigator.sqf +++ b/x/ocap2/addons/recorder/fnc_getInstigator.sqf @@ -25,7 +25,7 @@ Author: ---------------------------------------------------------------------------- */ #include "script_component.hpp" -params ["_victim", ["_killer", objNull], ["_instigator", objNull]]; +params [["_victim", objNull], ["_killer", objNull], ["_instigator", objNull]]; if (isNull _instigator) then { _instigator = UAVControl vehicle _killer select 0; @@ -34,11 +34,11 @@ if ((isNull _instigator) || (_instigator == _victim)) then { _instigator = _killer; }; if (_instigator isKindOf "AllVehicles") then { - _instigator = call { - if(alive(gunner _instigator))exitWith{gunner _instigator}; - if(alive(commander _instigator))exitWith{commander _instigator}; - if(alive(driver _instigator))exitWith{driver _instigator}; - effectiveCommander _instigator + _instigator = _instigator call { + if(alive(gunner _this))exitWith{gunner _this}; + if(alive(commander _this))exitWith{commander _this}; + if(alive(driver _this))exitWith{driver _this}; + effectiveCommander _this }; }; if (isNull _instigator) then { diff --git a/x/ocap2/addons/recorder/fnc_getWeaponDisplayData.sqf b/x/ocap2/addons/recorder/fnc_getWeaponDisplayData.sqf new file mode 100644 index 0000000..cddafc4 --- /dev/null +++ b/x/ocap2/addons/recorder/fnc_getWeaponDisplayData.sqf @@ -0,0 +1,11 @@ +params ["_weapon", "_muzzle", "_magazine", "_ammo"]; + +_muzzleDisp = getText(configFile >> "CfgWeapons" >> _weapon >> _muzzle >> "displayName"); +if (_muzzleDisp == "") then {_muzzleDisp = getText(configFile >> "CfgWeapons" >> _weapon >> "displayNameShort")}; +if (_muzzleDisp == "") then {_muzzleDisp = getText(configFile >> "CfgWeapons" >> _weapon >> "displayName")}; +_magDisp = getText(configFile >> "CfgMagazines" >> _magazine >> "displayNameShort"); +if (_magDisp == "") then {_magDisp = getText(configFile >> "CfgMagazines" >> _magazine >> "displayName")}; +if (_magDisp == "" && !isNil "_ammo") then {_magDisp = getText(configFile >> "CfgAmmo" >> _ammo >> "displayNameShort")}; +if (_magDisp == "" && !isNil "_ammo") then {_magDisp = getText(configFile >> "CfgAmmo" >> _ammo >> "displayName")}; + +[_muzzleDisp, _magDisp]; diff --git a/x/ocap2/addons/recorder/fnc_handleMarkers.sqf b/x/ocap2/addons/recorder/fnc_handleMarkers.sqf index 012f185..1d6c933 100644 --- a/x/ocap2/addons/recorder/fnc_handleMarkers.sqf +++ b/x/ocap2/addons/recorder/fnc_handleMarkers.sqf @@ -38,28 +38,7 @@ Author: // Entries are added at marker create events and removed at marker delete events to avoid duplicate processing. GVAR(trackedMarkers) = []; // Markers which we saves into replay -// On the dedicated server, the color of the markers is blue -// This overrides it with client data so it's saved properly -{ - _x params ["_name", "_color"]; - profilenamespace setVariable [_name, _color]; -} forEach [ - ["map_blufor_r", 0], - ["map_blufor_g", 0.3], - ["map_blufor_b", 0.6], - ["map_independent_r", 0], - ["map_independent_g", 0.5], - ["map_independent_b", 0], - ["map_civilian_r", 0.4], - ["map_civilian_g", 0], - ["map_civilian_b", 0.5], - ["map_unknown_r", 0.7], - ["map_unknown_g", 0.6], - ["map_unknown_b", 0], - ["map_opfor_r", 0.5], - ["map_opfor_g", 0], - ["map_opfor_b", 0] -]; + // create CBA event handler to be called on server with key "ocap2_handleMarker" EGVAR(listener,markers) = [QGVARMAIN(handleMarker), { diff --git a/x/ocap2/addons/recorder/fnc_init.sqf b/x/ocap2/addons/recorder/fnc_init.sqf index 2771aae..c5f0252 100644 --- a/x/ocap2/addons/recorder/fnc_init.sqf +++ b/x/ocap2/addons/recorder/fnc_init.sqf @@ -43,6 +43,7 @@ publicVariable QGVAR(captureFrameNo); GVAR(frameCaptureDelay) = EGVAR(settings,frameCaptureDelay); GVAR(autoStart) = EGVAR(settings,autoStart); GVAR(minMissionTime) = EGVAR(settings,minMissionTime); +GVAR(projectileMonitorMultiplier) = 1; // macro: GVARMAIN(version)SION GVARMAIN(version) = QUOTE(VERSION_STR); @@ -51,7 +52,6 @@ publicVariable QGVARMAIN(version); EGVAR(extension,version) = ([":VERSION:", []] call EFUNC(extension,sendData)); publicVariable QEGVAR(extension,version); - // remoteExec diary creation commands to clients listing version numbers and waiting start state { [{!isNil QGVARMAIN(version) && !isNil QEGVAR(extension,version)}, { @@ -87,7 +87,7 @@ publicVariable QEGVAR(extension,version); ] ]; }] call CBA_fnc_waitUntilAndExecute; -} remoteExecCall ["call", 0, true]; +} remoteExecCall ["call", [0, -2] select isDedicated, true]; // Support both methods of setting mission name. @@ -96,6 +96,31 @@ if (GVAR(missionName) == "") then { GVAR(missionName) = briefingName; }; + +// On the dedicated server, the color of the markers is blue +// This overrides it with client data so it's saved properly +{ + _x params ["_name", "_color"]; + profilenamespace setVariable [_name, _color]; +} forEach [ + ["map_blufor_r", 0], + ["map_blufor_g", 0.3], + ["map_blufor_b", 0.6], + ["map_independent_r", 0], + ["map_independent_g", 0.5], + ["map_independent_b", 0], + ["map_civilian_r", 0.4], + ["map_civilian_g", 0], + ["map_civilian_b", 0.5], + ["map_unknown_r", 0.7], + ["map_unknown_g", 0.6], + ["map_unknown_b", 0], + ["map_opfor_r", 0.5], + ["map_opfor_g", 0], + ["map_opfor_b", 0] +]; + + /* Conditional Start Recording We'll wait to see if auto-start is enabled and minPlayercount setting is met. This covers scenarios where someone changes the autostart setting during the mission as well, and excludes cases where autostart is disabled. @@ -110,82 +135,22 @@ if (GVAR(missionName) == "") then { // When the server progresses past briefing and enters the mission, save an event to the timeline if recording [{getClientStateNumber > 9}, { if (!SHOULDSAVEEVENTS) exitWith {}; - [QGVARMAIN(customEvent), ["generalEvent", "Mission has started!"]] call CBA_fnc_serverEvent; + if (time < 20) then { + [QGVARMAIN(customEvent), ["generalEvent", "Mission has started!"]] call CBA_fnc_serverEvent; + } else { + _timeArr = [time, "HH:MM:SS", true] call BIS_fnc_secondsToString; + [QGVARMAIN(customEvent), ["generalEvent", format["Recording began %1H %2M %3S into the mission", _timeArr#0, _timeArr#1, _timeArr#2]]] call CBA_fnc_serverEvent; + } }] call CBA_fnc_waitUntilAndExecute; - - -// PFH to track bullets -[{ - { - if (isNull (_x#0)) then { - _x params ["_obj", "_firerId", "_frame", "_pos"]; - [":FIRED:", [ - _firerId, - _frame, - _pos - ]] call EFUNC(extension,sendData); - - if (GVARMAIN(isDebug)) then { - OCAPEXTLOG(ARR4("FIRED EVENT: BULLET", _frame, _firerId, str _pos)); - }; - GVAR(liveBullets) = GVAR(liveBullets) - [_x]; - } else { - _x set [3, getPosASL (_x#0)]; - }; - } forEach GVAR(liveBullets); -}] call CBA_fnc_addPerFrameHandler; - -// PFH to track missiles, rockets, shells -[{ - { - _x params ["_obj", "_magazine", "_firer", "_pos", "_markName"]; - if (isNull (_x#0)) then { - - _firer setVariable [ - QGVARMAIN(lastFired), - getText(configFile >> "CfgMagazines" >> _magazine >> "displayName") - ]; - - if (GVARMAIN(isDebug)) then { - OCAPEXTLOG(ARR4("FIRED EVENT: SHELL-ROCKET-MISSILE", _frame, _firerId, str _pos)); - }; - - [{[QGVARMAIN(handleMarker), ["DELETED", _this]] call CBA_fnc_localEvent}, _markName, 10] call CBA_fnc_waitAndExecute; - GVAR(liveMissiles) = GVAR(liveMissiles) - [_x]; - - } else { - _nowPos = getPosASL (_x#0); - _x set [3, _nowPos]; - [QGVARMAIN(handleMarker), ["UPDATED", _markName, _firer, _nowPos, "", "", "", getDir (_x#0), "", "", 1]] call CBA_fnc_localEvent; - }; - } forEach GVAR(liveMissiles); -}, GVAR(frameCaptureDelay) * 0.3] call CBA_fnc_addPerFrameHandler; - -// PFH to track grenades, flares, thrown charges +// Auto-save on empty - checked every 30 seconds +// If a recording has been started, exceeds min mission time, and no players are on the server, auto-save [{ - { - _x params ["_obj", "_magazine", "_firer", "_pos", "_markName", "_ammoSimType"]; - if (isNull (_x#0)) then { - - if !(_ammoSimType in ["shotSmokeX", "shotIlluminating"]) then { - _firer setVariable [ - QGVARMAIN(lastFired), - getText(configFile >> "CfgMagazines" >> _magazine >> "displayName") - ]; - }; - - if (GVARMAIN(isDebug)) then { - OCAPEXTLOG(ARR4("FIRED EVENT: GRENADE-FLARE-SMOKE", _frame, _firerId, str _pos)); - }; - - [{[QGVARMAIN(handleMarker), ["DELETED", _this]] call CBA_fnc_localEvent}, _markName, 10] call CBA_fnc_waitAndExecute; - GVAR(liveGrenades) = GVAR(liveGrenades) - [_x]; - - } else { - _nowPos = getPosASL (_x#0); - _x set [3, _nowPos]; - [QGVARMAIN(handleMarker), ["UPDATED", _markName, _firer, _nowPos, "", "", "", getDir (_x#0), "", "", 1]] call CBA_fnc_localEvent; - }; - } forEach GVAR(liveGrenades); -}, GVAR(frameCaptureDelay)] call CBA_fnc_addPerFrameHandler; + if (!isNil QGVAR(startTime) && (GVAR(frameCaptureDelay) * GVAR(captureFrameNo)) / 60 >= GVAR(minMissionTime) && count allPlayers == 0) then { + [nil, "Mission ended due to server being empty"] call FUNC(exportData); + }; +}, 30] call CBA_fnc_addPerFrameHandler; + +if (isNil QGVAR(projectileMonitorsInitialized)) then { + call FUNC(projectileMonitors); +}; diff --git a/x/ocap2/addons/recorder/fnc_projectileMonitors.sqf b/x/ocap2/addons/recorder/fnc_projectileMonitors.sqf new file mode 100644 index 0000000..ed1d422 --- /dev/null +++ b/x/ocap2/addons/recorder/fnc_projectileMonitors.sqf @@ -0,0 +1,170 @@ +#include "script_component.hpp" + +// PFH to track bullets +GVAR(liveBullets) = []; +[{ + + private _processNow = GVAR(liveBullets) select {isNull (_x#0)}; + GVAR(liveBullets) = GVAR(liveBullets) select {!isNull (_x#0)}; + + // _processNow + // for bullets that have hit something and become null, trigger FIRED events in timeline and add to clients for debug draw + { + _x params ["_obj", "_firerId", "_firer", "_pos"]; + + [":FIRED:", [ + _firerId, + GVAR(captureFrameNo), + _pos + ]] call EFUNC(extension,sendData); + + if (GVARMAIN(isDebug)) then { + OCAPEXTLOG(ARR4("FIRED EVENT: BULLET", GVAR(captureFrameNo), _firerId, str _pos)); + + // add to clients' map draw array + private _debugArr = [getPos _firer, _pos, [side group _firer] call BIS_fnc_sideColor, cba_missionTime]; + [QGVAR(addDebugBullet), _debugArr] call CBA_fnc_globalEvent; + }; + } forEach _processNow; + + // for bullets that still exist, update positions + { + _x set [3, getPosASL (_x#0)]; + } forEach GVAR(liveBullets); +}, 0.1 * GVAR(projectileMonitorMultiplier)] call CBA_fnc_addPerFrameHandler; + +// PFH to track missiles, rockets, shells +GVAR(liveMissiles) = []; +[{ + private _processNow = GVAR(liveMissiles) select {isNull (_x#0)}; + GVAR(liveMissiles) = GVAR(liveMissiles) select {!isNull (_x#0)}; + + // _processNow + // for missiles that have hit something and become null, trigger FIRED events in timeline and add to clients for debug draw + { + _x params ["_obj", "_wepString", "_firer", "_pos", "_markName", "_markTextLocal"]; + _firer setVariable [ + QGVARMAIN(lastFired), + getText(configFile >> "CfgMagazines" >> _magazine >> "displayName") + ]; + + if (GVARMAIN(isDebug)) then { + OCAPEXTLOG(ARR4("FIRED EVENT: SHELL-ROCKET-MISSILE", GVAR(captureFrameNo), _firer getVariable QGVARMAIN(id), str _pos)); + }; + + [{[QGVARMAIN(handleMarker), ["DELETED", _this]] call CBA_fnc_localEvent}, _markName, 10] call CBA_fnc_waitAndExecute; + } forEach _processNow; + + // for missiles that still exist, update positions + { + _x params ["_obj", "_wepString", "_firer", "_pos", "_markName", "_markTextLocal"]; + _nowPos = getPosASL (_x#0); + _x set [3, _nowPos]; + [QGVARMAIN(handleMarker), ["UPDATED", _markName, _firer, _nowPos, "", "", "", getDir (_x#0), "", "", 1]] call CBA_fnc_localEvent; + } forEach GVAR(liveMissiles); +}, 0.1 * GVAR(projectileMonitorMultiplier)] call CBA_fnc_addPerFrameHandler; + +// PFH to track grenades, flares, thrown charges +GVAR(liveGrenades) = []; +[{ + private _processNow = GVAR(liveMissiles) select {isNull (_x#0)}; + GVAR(liveMissiles) = GVAR(liveMissiles) select {!isNull (_x#0)}; + + // _processNow + // for grenades that have hit something and become null, trigger FIRED events in timeline and add to clients for debug draw + { + _x params ["_obj", "_magazine", "_firer", "_pos", "_markName", "_markTextLocal", "_ammoSimType"]; + + if !(_ammoSimType in ["shotSmokeX", "shotIlluminating"]) then { + _firer setVariable [ + QGVARMAIN(lastFired), + getText(configFile >> "CfgMagazines" >> _magazine >> "displayName") + ]; + }; + + if (GVARMAIN(isDebug)) then { + OCAPEXTLOG(ARR4("FIRED EVENT: GRENADE-FLARE-SMOKE", GVAR(captureFrameNo), _firer getVariable QGVARMAIN(id), str _pos)); + }; + + [{[QGVARMAIN(handleMarker), ["DELETED", _this]] call CBA_fnc_localEvent}, _markName, 10] call CBA_fnc_waitAndExecute; + } forEach _processNow; + + // for grenades that still exist, update positions + { + _x params ["_obj", "_magazine", "_firer", "_pos", "_markName", "_markTextLocal", "_ammoSimType"]; + _nowPos = getPosASL (_x#0); + _x set [3, _nowPos]; + [QGVARMAIN(handleMarker), ["UPDATED", _markName, _firer, _nowPos, "", "", "", getDir (_x#0), "", "", 1]] call CBA_fnc_localEvent; + } forEach GVAR(liveGrenades); +}, GVAR(frameCaptureDelay)] call CBA_fnc_addPerFrameHandler; + + + +// DEBUG draws on clients + +// drawLine for each bullet on map if debug active, to verify rounds are captured +// these are only added once the projectile has hit something +{ + if (!hasInterface) exitWith {}; + [] spawn { + waitUntil {!isNull (findDisplay 12)}; + GVAR(liveDebugBullets) = []; + disableSerialization; + (findDisplay 12 displayCtrl 51) ctrlAddEventHandler ["Draw", { + if (GVARMAIN(isDebug)) then { + // remove bullets from display that have landed > 7 seconds ago + GVAR(liveDebugBullets) = GVAR(liveDebugBullets) select {cba_missionTime < (_x#3) + 7}; + { + // _x params ["_startPos", "_endPos", "_color", "_timeHit"]; + (_this#0) drawLine [ + _x#0, + _x#1, + _x#2 + ]; + } forEach GVAR(liveDebugBullets); + }; + }]; + + [QGVAR(addDebugBullet), { + GVAR(liveDebugBullets) pushBack _this; + }] call CBA_fnc_addEventHandler; + }; +} remoteExec ["call", [0, -2] select isDedicated, true]; + +// drawIcon for magazines/non-bullet projectiles +// these are added when fired and tracked in array +{ + if (!hasInterface) exitWith {}; + [] spawn { + waitUntil {!isNull (findDisplay 12)}; + GVAR(liveDebugMagIcons) = []; + disableSerialization; + (findDisplay 12 displayCtrl 51) ctrlAddEventHandler ["Draw", { + if (GVARMAIN(isDebug)) then { + GVAR(liveDebugMagIcons) = GVAR(liveDebugMagIcons) select {!isNull (_x#0)}; + { + // _x params ["_obj", "_magIcon", "_text", "_sideColor"]; + (_this#0) drawIcon [ + _x#1, // Custom images can also be used: getMissionPath "\myFolder\myIcon.paa" + _x#3, + getPos (_x#0), + 25, + 25, + getDir (_x#0), + _x#2, + 0, + 0.03, + "PuristaLight", + "center" + ]; + } forEach GVAR(liveDebugMagIcons); + }; + }]; + + [QGVAR(addDebugMagIcon), { + GVAR(liveDebugMagIcons) pushBack _this; + }] call CBA_fnc_addEventHandler; + }; +} remoteExec ["call", [0, -2] select isDedicated, true]; + +GVAR(projectileMonitorsInitialized) = true;