-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathfnc_captureLoop.sqf
More file actions
326 lines (289 loc) · 12.7 KB
/
fnc_captureLoop.sqf
File metadata and controls
326 lines (289 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/* ----------------------------------------------------------------------------
FILE: fnc_captureLoop.sqf
FUNCTION: OCAP_recorder_fnc_captureLoop
Description:
This function is run unscheduled and creates a CBA PerFrameHandler object, a logic object which executes code every specified interval (<OCAP_settings_frameCaptureDelay>) while a condition (<SHOULDSAVEEVENTS>) is true.
Iterates through units and vehicles, declares they exist, and conditionally sends their information to the extension to populate recording data.
This is the core processing loop that determines when new units enter the world, all the details about them, classifies which to exclude, and determines their health/life status. It has both unit and vehicle tracking.
Parameters:
None
Returns:
Nothing
Examples:
> call FUNC(captureLoop);
Public:
No
Author:
Dell, Zealot, IndigoFox, Fank
---------------------------------------------------------------------------- */
#include "script_component.hpp"
if (!isNil QGVAR(PFHObject)) then {
[GVAR(PFHObject)] call CBA_fnc_deletePerFrameHandlerObject;
GVAR(PFHObject) = nil;
};
if (isNil QGVAR(startTime)) then {
GVAR(startTime) = time;
publicVariable QGVAR(startTime);
OCAPEXTLOG(ARR3(__FILE__,QGVAR(recording) + " started, time:",GVAR(startTime)));
LOG(ARR3(__FILE__,QGVAR(recording) + " started, time:",GVAR(startTime)));
};
GVAR(trackedVehicles) = createHashMap;
// Pre-compute frame intervals that depend on frameCaptureDelay (constant for the mission)
// Must be GVARs, not private — PFH code runs in a different scope.
GVAR(ticketInterval) = round (30 / GVAR(frameCaptureDelay));
GVAR(diaryInterval) = round (320 / GVAR(frameCaptureDelay));
// Variable: OCAP_PFHObject
// The CBA PerFrameHandler object that is created and used to run the capture loop.
GVAR(PFHObject) = [
{
private _loopStart = diag_tickTime;
// Increment BEFORE processing so frames start at 1, not 0.
// Frame 0 is reserved as sentinel for "not yet recording" (see fnc_init.sqf).
GVAR(captureFrameNo) = GVAR(captureFrameNo) + 1;
publicVariable QGVAR(captureFrameNo);
if (GVAR(captureFrameNo) == 10 || (GVAR(captureFrameNo) > 10 && EGVAR(settings,trackTimes) && GVAR(captureFrameNo) % EGVAR(settings,trackTimeInterval) == 0)) then {
[] call FUNC(updateTime);
};
// every 15 frames of recording check respawn ticket state of each of three sides
if (GVAR(captureFrameNo) % GVAR(ticketInterval) == 0 && EGVAR(settings,trackTickets)) then {
private _scores = [];
{
_scores pushBack ([_x] call BIS_fnc_respawnTickets);
} forEach [missionNamespace, east, west, independent];
[QGVARMAIN(customEvent), ["respawnTickets", _scores]] call CBA_fnc_localEvent;
};
// update diary record every ~320 seconds
if (GVAR(captureFrameNo) % GVAR(diaryInterval) == 0) then {
publicVariable QGVAR(captureFrameNo);
{
player createDiaryRecord [
"OCAPInfo",
[
"Status",
("<font color='#CCCCCC'>Capture frame: " + str (missionNamespace getVariable [QGVAR(captureFrameNo), "[not yet received]"]) + "</font>")
]
];
} remoteExec ["call", 0, false];
};
{
private _justInitialized = false;
if !(_x getVariable [QGVARMAIN(isInitialized), false]) then {
if (
_x isKindOf "Logic"
) exitWith {
_x setVariable [QGVARMAIN(exclude), true, true];
_x setVariable [QGVARMAIN(isInitialized), true, true];
};
_x setVariable [QGVARMAIN(id), GVAR(nextId), true];
private _newUnit = [
GVAR(captureFrameNo), //1
GVAR(nextId), //2
name _x, //3
groupID (group _x), //4
str side group _x, //5
BOOL(isPlayer _x), //6
roleDescription _x, // 7
typeOf _x, // 8 classname
[configOf _x] call BIS_fnc_displayName, // 9 type displayname
if (isPlayer _x) then {getPlayerUID _x} else {""}, // 10 player uid
[squadParams _x] call CBA_fnc_encodeJSON // 11 squad params
];
_x setVariable [QGVARMAIN(newUnitData), _newUnit];
[
{missionNamespace getVariable [QEGVAR(extension,sessionReady), false]},
{[":SOLDIER:CREATE:", _this] call EFUNC(extension,sendData);},
_newUnit,
30
] call CBA_fnc_waitUntilAndExecute;
[_x] spawn FUNC(addUnitEventHandlers);
GVAR(nextId) = GVAR(nextId) + 1;
_x setVariable [QGVARMAIN(isInitialized), true, true];
_justInitialized = true;
};
// Re-include units that have become player-controlled again (e.g., reconnected players)
private _isExcluded = _x getVariable [QGVARMAIN(exclude), false];
if (_isExcluded && {isPlayer _x}) then {
_x setVariable [QGVARMAIN(exclude), false];
_isExcluded = false;
};
if (!_justInitialized && {!_isExcluded}) then {
private _unitRole = _x getVariable [QGVARMAIN(unitType), ""];
private _weapons = [primaryWeapon _x, secondaryWeapon _x];
if (_weapons isNotEqualTo (_x getVariable [QGVAR(lastWeapons), []]) || _unitRole == "") then {
_unitRole = [_x] call FUNC(getUnitType);
_x setVariable [QGVARMAIN(unitType), _unitRole];
_x setVariable [QGVAR(lastWeapons), _weapons];
};
private _lifeState = 0;
if (alive _x) then {
if (EGVAR(settings,preferACEUnconscious) && GVAR(hasACEIsAwake)) then {
_lifeState = if ([_x] call ace_common_fnc_isAwake) then {1} else {2};
} else {
_lifeState = if (lifeState _x isEqualTo "INCAPACITATED") then {2} else {1};
};
};
_pos = getPosASL _x;
private _unitGroup = group _x;
private _scores = getPlayerScores _x;
private _scoresStr = _x getVariable [QGVAR(lastScoresStr), ""];
if (_scores isNotEqualTo (_x getVariable [QGVAR(lastScores), []])) then {
_scoresStr = _scores joinString ",";
_x setVariable [QGVAR(lastScores), _scores];
_x setVariable [QGVAR(lastScoresStr), _scoresStr];
};
private _parent = objectParent _x;
private _unitData = [
(_x getVariable QGVARMAIN(id)), //1
_pos, //2
round getDir _x, //3
_lifeState, //4
BOOL((vehicle _x) isNotEqualTo _x), //5
if (alive _x) then {name _x} else {""}, //6
BOOL(isPlayer _x), //7
_unitRole, //8
0, // frame placeholder for comparison (set before sending) 9
if (GVAR(hasACEStableVitals)) then {BOOL([_x] call ace_medical_status_fnc_hasStableVitals)} else {true}, // 10
if (GVAR(hasACEIsBeingDragged)) then {BOOL([_x] call ace_medical_status_fnc_isBeingDragged)} else {false}, // 11
_scoresStr, // scores 12
_x call CBA_fnc_vehicleRole, // vehicle role 13
if (!isNull _parent) then {_parent getVariable [QGVARMAIN(id), -1]} else {-1}, // 14
stance _x, // 15
groupID _unitGroup, // 16 group name (dynamic)
str side _unitGroup // 17 side (dynamic)
];
if (_x getVariable [QGVARMAIN(unitData), []] isNotEqualTo _unitData) then {
_x setVariable [QGVARMAIN(unitData), +_unitData];
_unitData set [8, GVAR(captureFrameNo)];
[":SOLDIER:STATE:", _unitData] call EFUNC(extension,sendData);
};
};
} forEach (allUnits + allDeadMen);
{
private _justInitialized = false;
if !(_x getVariable [QGVARMAIN(isInitialized), false]) then {
_vehType = typeOf _x;
_class = _vehType call FUNC(getClass);
private _vic = _x;
private _toExcludeKind = false;
private _kindList = GVAR(excludeKindList);
if (_kindList isNotEqualTo []) then {
{
if (_vic isKindOf _x) exitWith {
_toExcludeKind = true;
};
} forEach _kindList;
};
private _toExcludeClass = false;
private _classList = GVAR(excludeClassList);
if (_classList isNotEqualTo []) then {
{
if (typeOf _vic == _x) exitWith {
_toExcludeClass = true;
};
} forEach _classList;
};
if ((_class isEqualTo "unknown") || _toExcludeKind || _toExcludeClass) exitWith {
LOG(ARR2("WARNING: vehicle is defined as 'unknown' or exclude:",_vehType));
_x setVariable [QGVARMAIN(isInitialized), true, true];
_x setVariable [QGVARMAIN(exclude), true, true];
};
_x setVariable [QGVARMAIN(id), GVAR(nextId), true];
private _newVehicleData = [
GVAR(captureFrameNo), //1
GVAR(nextId), //2
_class, //3
getText (configFile >> "CfgVehicles" >> _vehType >> "displayName"), //4
typeOf _x, //5
format ["%1", [_x] call BIS_fnc_getVehicleCustomization], //6
str side _x //7
];
_x setVariable [QGVARMAIN(newVehicleData), _newVehicleData];
[
{missionNamespace getVariable [QEGVAR(extension,sessionReady), false]},
{[":VEHICLE:CREATE:", _this] call EFUNC(extension,sendData);},
_newVehicleData,
30
] call CBA_fnc_waitUntilAndExecute;
[_x] spawn FUNC(addUnitEventHandlers);
GVAR(nextId) = GVAR(nextId) + 1;
_x setVariable [QGVARMAIN(vehicleClass), _class];
_x setVariable [QGVARMAIN(hasTurret), (_x weaponsTurret [0]) isNotEqualTo []];
_x setVariable [QGVARMAIN(isInitialized), true, true];
_justInitialized = true;
};
if (!_justInitialized && {!(_x getVariable [QGVARMAIN(exclude), false])}) then {
private _crew = [];
{
if (_x getVariable [QGVARMAIN(isInitialized), false]) then {
_crew pushBack (_x getVariable QGVARMAIN(id));
};
} forEach (crew _x);
_pos = getPosASL _x;
private _turretAz = 0;
private _turretEl = 0;
if (_x getVariable [QGVARMAIN(hasTurret), false]) then {
private _turretResult = [_x, [0], true] call CBA_fnc_turretDir;
_turretAz = _turretResult select 0;
_turretEl = _turretResult select 1;
};
private _vehicleData = [
(_x getVariable QGVARMAIN(id)), //1
_pos, //2
round getDir _x, //3
BOOL(alive _x), //4
_crew, //5
0, // frame placeholder for comparison (set before sending) 6
fuel _x, // 7
damage _x, // 8
isEngineOn _x, // 9
(locked _x) >= 2, // 10
side _x, // 11
vectorDir _x, // 12
vectorUp _x, // 13
_turretAz, // 14
_turretEl // 15
];
private _ocapId = _vehicleData select 0;
// Stop tracking parachutes/ejection seats that are empty or dead
if ((_x getVariable [QGVARMAIN(vehicleClass), ""]) isEqualTo "parachute" && {!((alive _x) && {_crew isNotEqualTo []})}) then {
_vehicleData set [3, 0];
_vehicleData set [5, GVAR(captureFrameNo)];
[":VEHICLE:STATE:", _vehicleData] call EFUNC(extension,sendData);
_x setVariable [QGVARMAIN(exclude), true, true];
GVAR(trackedVehicles) deleteAt _ocapId;
} else {
if (_x getVariable [QGVARMAIN(vehicleData), []] isNotEqualTo _vehicleData) then {
_x setVariable [QGVARMAIN(vehicleData), +_vehicleData];
_vehicleData set [5, GVAR(captureFrameNo)];
[":VEHICLE:STATE:", _vehicleData] call EFUNC(extension,sendData);
GVAR(trackedVehicles) set [_ocapId, [_x, _pos, round getDir _x, side _x, vectorDir _x, vectorUp _x]];
};
};
};
} forEach vehicles;
// Detect disappeared vehicles (deleted/garbage-collected) and send final dead state
private _toRemove = [];
{
(GVAR(trackedVehicles) get _x) params ["_obj", "_lastPos", "_lastDir", "_lastSide", "_lastVectorDir", "_lastVectorUp"];
if (isNull _obj) then {
[":VEHICLE:STATE:", [
_x, _lastPos, _lastDir, 0, [], GVAR(captureFrameNo),
0, 1, false, false, _lastSide, _lastVectorDir, _lastVectorUp, 0, 0
]] call EFUNC(extension,sendData);
_toRemove pushBack _x;
};
} forEach (keys GVAR(trackedVehicles));
{ GVAR(trackedVehicles) deleteAt _x } forEach _toRemove;
if (GVARMAIN(isDebug)) then {
private _logStr = format["Frame %1 processed in %2ms", GVAR(captureFrameNo), diag_tickTime - _loopStart];
OCAPEXTLOG([_logStr]);
_logStr SYSCHAT;
};
},
GVAR(frameCaptureDelay), // delay
[], // args
{}, // code, executed when added
{}, // code, executed when removed
{SHOULDSAVEEVENTS}, // if true, execute PFH cycle
{false} // if true, delete object
] call CBA_fnc_createPerFrameHandlerObject;