· 6 years ago · Sep 18, 2019, 05:58 PM
1/*
2 Author: Jiri Wainar
3
4 Description:
5 Play set of ambient animations on given unit.
6
7 Remarks:
8 * Can handle several different anims and auto-switching between them.
9 * Detects nearby units with the same animation set and tries to select different animations.
10 * A game logic is created on units position and animated unit is then attached to it, to prevent possible problems, like player pushing units around.
11 * The unit can be snapped to a set snappoint to prevent game from repositioning the unit.
12 * The snappoint is automatically detected, snap distance is 2 meters (in top-down view, ignoring vertical info).
13 * To position unit vertically, move its snappoint up/down.
14 * A snappoint is a (cyan colored) helper "Sign_Pointer_Cyan_F".
15 * This is extremely handy if the unit needs to be placed near object(s) - like on chair near a table.
16
17 Parameter(s):
18 0: OBJECT - unit the anim & gear changes are going to be applied to
19 1: STRING - animation set id, describing what the unit's action looks like.
20 > "STAND" - standing still, slightly turning to the sides, with rifle weapon
21 > "STAND_IA" - standing still, slightly turning to the sides, with rifle weapon
22 > "STAND_U1-3" - standing still, slightly turning to the sides, no weapon
23 > "WATCH1-2" - standing and turning around, with rifle weapon
24 > "GUARD" - standing still, like on guard with hands behing the body
25 > "LISTEN_BRIEFING" - standing still, hands behind back, recieving briefing / commands, no rifle.
26 > "LEAN_ON_TABLE" - standing while leaning on the table
27 > "LEAN" - standing while leaning (on wall)
28 > "BRIEFING" - standing, playing ambient briefing loop with occasional random moves
29 > "BRIEFING_POINT_LEFT" - contains 1 extra pointing animation, pointing left & high
30 > "BRIEFING_POINT_RIGHT" - contains 1 extra pointing animation, pointing right & high
31 > "BRIEFING_POINT_TABLE" - contains 1 extra pointing animation, pointing front & low, like at table
32 > "SIT1-3" - sitting on chair or bench, with rifle weapon
33 > "SIT_U1-3" - sitting on chair or bench, without weapon
34 > "SIT_AT_TABLE" - sitting @ table, hands on table
35 > "SIT_HIGH1-2" - sitting on taller objects like a table or wall, legs not touching the ground. Needs a rifle!
36 > "SIT_LOW" - sitting on the ground, with weapon.
37 > "SIT_LOW_U" - sitting on the ground, without weapon.
38 > "SIT_SAD1-2" - sitting on a chair, looking very sad.
39 > "KNEEL" - kneeling, with weapon.
40 > "PRONE_INJURED_U1-2" - laying wounded, back on the ground, wothout weapon
41 > "PRONE_INJURED" - laying wounded & still, back on the ground, with or without weapon
42 > "KNEEL_TREAT" - kneeling while treating the wounded
43 > "REPAIR_VEH_PRONE" - repairing vehicle while laying on the ground (under the vehicle)
44 > "REPAIR_VEH_KNEEL" - repairing vehicle while kneeling (like changing a wheel)
45 > "REPAIR_VEH_STAND" - repairing/cleaning a vehicle while standing
46
47 2: STRING - equipment level id, describing how heavily is the unit equipped.
48 > "NONE" - no goggles, headgear, vest, weapon
49 > "LIGHT" - no goggles, headgear, vest
50 > "MEDIUM" - no goggles, headgear
51 > "FULL" - no goggles
52 > "ASIS" - no touches to the gear
53 > "RANDOM" (default) - gear is randomized according to the animation set
54
55 3: OBJECT - object unit to snap to; function won't try to search for the closest snappoint and will use this snappoint instead
56 4: BOOL - function will try to interpolate into the ambient animation, if the interpolateTo link exists
57 - it is not officialy supported, so it's disabled by default
58 - works only for transitions from (some) default A3 stances to sets "STAND", "SIT_LOW" and "KNEEL"
59
60 Returns:
61 -
62
63 Example:
64 [this,"SIT_HIGH2"] call BIS_fnc_ambientAnim;
65*/
66
67//surpress the debuglog output
68private["_fnc_log_disable"];_fnc_log_disable = false;
69
70if (isNil "BIS_fnc_ambientAnim__group") then
71{
72 BIS_fnc_ambientAnim__group = createGroup west;
73};
74
75//do the immediate operations ----------------------------------------------------------------------
76private["_unit","_animset","_gear","_anims","_anim","_linked","_xSet","_azimutFix","_interpolate","_canInterpolate","_attach"];
77private["_attachOffset","_attachObj","_attachSpecsAuto","_attachSpecs","_attachSnap","_noBackpack","_noWeapon","_randomGear","_weapon","_forcedSnapPoint","_params"];
78
79_unit = _this param [0, objNull, [objNull]];
80_animset = _this param [1, "STAND", [""]];
81_gear = _this param [2, "RANDOM", [""]];
82_forcedSnapPoint = _this param [3, objNull, [objNull]];
83_interpolate = _this param [4, false, [true]];
84_attach = _this param [5, true, [true]];
85
86if (isNull _unit) exitWith
87{
88 //["Function terminated, unit doesn't exist!"] call BIS_fnc_logFormat;
89};
90
91if (isNil "_forcedSnapPoint") then
92{
93 ["Forced snappoint doesn't exist!"] call BIS_fnc_error;
94
95 _forcedSnapPoint = objNull;
96};
97
98if ((_unit getVariable ["BIS_fnc_ambientAnim__animset",""]) != "") exitWith
99{
100 ["[%1] Trying to play an ambient animation [%3] while another [%2] is already playing!",_unit,_unit getVariable ["BIS_fnc_ambientAnim__animset",""],_animset] call BIS_fnc_logFormat;
101
102 /*
103 _unit call BIS_fnc_ambientAnim__terminate;
104
105 _this spawn
106 {
107 sleep 1;
108
109 _this call BIS_fnc_ambientAnim;
110 };
111 */
112};
113
114//surpress the unit "intelligence"
115{_unit disableAI _x} forEach ["ANIM","AUTOTARGET","FSM","MOVE","TARGET"];
116
117//detach unit, if already attached to something
118detach _unit;
119
120//store primary weapon
121_weapon = primaryWeapon _unit;
122
123if (_weapon != "") then
124{
125 _unit setVariable ["BIS_fnc_ambientAnim__weapon",_weapon];
126};
127
128/*--------------------------------------------------------------------------------------------------
129
130 GET ANIMATION PARAMETERS
131
132--------------------------------------------------------------------------------------------------*/
133_params = _animset call BIS_fnc_ambientAnimGetParams;
134
135//defaults
136_anims = _params select 0;
137_azimutFix = _params select 1;
138_attachSnap = _params select 2;
139_attachOffset = _params select 3;
140_noBackpack = _params select 4;
141_noWeapon = _params select 5;
142_randomGear = _params select 6;
143_canInterpolate = _params select 7;
144
145if (count _anims == 0) exitWith {};
146
147if (_gear == "RANDOM") then
148{
149 _gear = _randomGear call BIS_fnc_selectRandom;
150};
151
152//setup the gear
153[_unit,_gear,_noWeapon,_noBackpack,_weapon] spawn
154{
155 private["_unit","_gear","_noWeapon","_noBackpack","_weapon"];
156
157 _unit = _this select 0;
158 _gear = _this select 1;
159 _noWeapon = _this select 2;
160 _noBackpack = _this select 3;
161 _weapon = _this select 4;
162
163 sleep 1;
164
165 switch (_gear) do
166 {
167 case "NONE":
168 {
169 removeGoggles _unit;
170 removeHeadgear _unit;
171 removeVest _unit;
172 removeAllWeapons _unit;
173
174 _noBackpack = true;
175 _noWeapon = true;
176 };
177 case "LIGHT":
178 {
179 removeGoggles _unit;
180 removeHeadgear _unit;
181 removeVest _unit;
182
183 _noBackpack = true;
184 };
185 case "MEDIUM":
186 {
187 removeGoggles _unit;
188 removeHeadgear _unit;
189 };
190 case "FULL":
191 {
192 removeGoggles _unit;
193 };
194 default
195 {
196 };
197 };
198
199 //remove NV goggles from units without helmets
200 if (_gear != "ASIS") then
201 {
202 { _unit unassignItem _x } forEach
203 [
204 "NVGogglesB_grn_F",
205 "NVGoggles_tna_F",
206 "NVGogglesB_gry_F",
207 "NVGoggles_ghex_F",
208 "NVGoggles_hex_F",
209 "NVGoggles_urb_F",
210 "nvgoggles",
211 "nvgoggles_opfor",
212 "nvgoggles_indep"
213 ];
214 };
215
216 //remove backpack for some anim sets
217 if (_noBackpack) then
218 {
219 removeBackpack _unit;
220 };
221
222 //["[%1] _noWeapon = %2 | _storedWeapon = %3",_unit,_noWeapon,_unit getVariable ["BIS_fnc_ambientAnim__weapon",""]] call BIS_fnc_logFormat;
223
224 //remove primary weapon for some anim sets
225 if (_noWeapon) then
226 {
227 _unit removeWeapon _weapon;
228 }
229 else
230 {
231 private["_storedWeapon"];
232
233 _storedWeapon = _unit getVariable ["BIS_fnc_ambientAnim__weapon",""];
234
235 if (primaryWeapon _unit == "" && _storedWeapon != "") then
236 {
237 //["Weapon [%1] provided to unit [%2].",_storedWeapon,_unit] call BIS_fnc_logFormat;
238
239 _unit addWeapon _storedWeapon;
240 _unit selectWeapon _storedWeapon;
241 };
242 };
243};
244
245//find linked units = nearby units playing same animation set
246_linked = _unit nearObjects ["man",5];
247_linked = _linked - [_unit];
248
249//["[%1] RAW linked: %2",_unit,_linked] call BIS_fnc_logFormat;
250
251{
252 _xSet = _x getVariable ["BIS_fnc_ambientAnim__animset",""];
253
254 //["[%1] %2 has animset %3",_unit,_x,_xSet] call BIS_fnc_logFormat;
255
256 if (_xSet != _animset || _xSet == "") then
257 {
258 _linked set [_forEachIndex,objNull];
259
260 //["[%1] unit %2 removed from linked",_unit,_x] call BIS_fnc_logFormat;
261 }
262 else
263 {
264 //put a backlink into the linked unit
265 _xLinked = _x getVariable ["BIS_fnc_ambientAnim__linked",[]];
266
267 //["[%1] %2 has linked units %3",_unit,_x,_xLinked] call BIS_fnc_logFormat;
268
269 if !(_unit in _xLinked) then
270 {
271 _xLinked = _xLinked + [_unit];
272 _x setVariable ["BIS_fnc_ambientAnim__linked",_xLinked];
273
274 //["[%1] %2 got a backlink to %1",_unit,_x] call BIS_fnc_logFormat;
275 };
276 };
277}
278forEach _linked; _linked = _linked - [objNull];
279
280//["[%1] AFTER CLEAN linked: %2",_unit,_linked] call BIS_fnc_logFormat;
281
282//get the auto snappoint specs
283_attachSpecsAuto = switch (_animset) do
284{
285 case "SIT_AT_TABLE":
286 {
287 [
288 ["Land_CampingChair_V2_F",[0,0.08,-0.02],-180],
289 ["Land_ChairPlastic_F",[0,0.08,-0.02],90],
290 ["Land_ChairWood_F",[0,0.02,-0.02],-180]
291 ];
292 };
293 case "SIT";
294 case "SIT1";
295 case "SIT2";
296 case "SIT3";
297 case "SIT_U1";
298 case "SIT_U2";
299 case "SIT_U3":
300 {
301 [
302 ["Land_CampingChair_V2_F",[0,0.08,0.05],-180],
303 ["Land_ChairPlastic_F",[0,0.08,0.05],90],
304 ["Land_ChairWood_F",[0,0.02,0.05],-180]
305 ];
306 };
307
308 case "SIT_SAD1":
309 {
310 [
311 ["Box_NATO_Wps_F",[0,-0.27,0.03],0]
312 ];
313 };
314 case "SIT_SAD2":
315 {
316 [
317 ["Box_NATO_Wps_F",[0,-0.3,0.05],0]
318 ];
319 };
320 case "SIT_HIGH1":
321 {
322 [
323 ["Box_NATO_Wps_F",[0,-0.23,0.03],0]
324 ];
325 };
326 case "SIT_HIGH";
327 case "SIT_HIGH2":
328 {
329 [
330 ["Box_NATO_Wps_F",[0,-0.12,-0.20],0]
331 ];
332 };
333
334
335 default
336 {
337 [];
338 };
339};
340
341//adjust the auto attach data according to the soldiers gear
342if ((count _attachSpecsAuto > 0) && !(_gear in ["NONE","LIGHT"])) then
343{
344 private["_attachPoint","_attachGearFix","_vest"];
345
346 _attachGearFix = 0.06;
347 _vest = toLower (vest _unit);
348
349 if (_vest in ["v_platecarrier1_rgr"]) then
350 {
351 _attachGearFix = _attachGearFix + 0.08;
352 };
353
354 {
355 _attachPoint = _x select 1;
356 _attachPoint set [1, (_attachPoint select 1) + _attachGearFix];
357 _x set [1, _attachPoint];
358 }
359 forEach _attachSpecsAuto;
360};
361
362//add the possible helper snappoint
363_attachSpecsAuto = _attachSpecsAuto + [["Sign_Pointer_Cyan_F",[0,0,_attachOffset],0]];
364
365if !(isNull _forcedSnapPoint) then
366{
367 _attachObj = _forcedSnapPoint;
368 _attachSpecs = [typeOf _forcedSnapPoint,[0,0,_attachOffset],0];
369
370 //get the attach specs
371 {
372 if ((_x select 0) == typeOf _forcedSnapPoint) exitWith
373 {
374 _attachSpecs = _x;
375 };
376 }
377 forEach _attachSpecsAuto;
378}
379else
380{
381 //default situation, snappoint not found = using unit position
382 _attachSpecs = [typeOf _unit,[0,0,_attachOffset],0];
383 _attachObj = _unit;
384
385 //get the snappoint object
386 private["_obj"];
387
388 {
389 _obj = nearestObject [_unit, _x select 0];
390
391 if (([_obj,_unit] call BIS_fnc_distance2D) < _attachSnap) exitWith
392 {
393 _attachSpecs = _x;
394 _attachObj = _obj;
395 };
396 }
397 forEach _attachSpecsAuto;
398};
399
400
401//store linked units, won't be changed
402_unit setVariable ["BIS_fnc_ambientAnim__linked",_linked]; //array of units that should be checked for not playing same animation
403
404//store persistant animation data in the units namespace
405_unit setVariable ["BIS_fnc_ambientAnim__anims",_anims];
406_unit setVariable ["BIS_fnc_ambientAnim__animset",_animset];
407_unit setVariable ["BIS_fnc_ambientAnim__interpolate",_interpolate && _canInterpolate];
408
409//store variable animation data in the units namespace
410_unit setVariable ["BIS_fnc_ambientAnim__time",0]; //time when the animation has started
411
412//disable collisions between unit and helper/attach object
413_attachObj disableCollisionWith _unit;
414_unit disableCollisionWith _attachObj;
415
416//do the delayed operations ------------------------------------------------------------------------
417[_unit,_attachObj,_attachSpecs,_azimutFix,_attach] spawn
418{
419 private["_unit","_attachObj","_attachSpecs","_azimutFix","_group","_attach"];
420 private["_attachPos","_logic","_ehAnimDone","_ehKilled"];
421
422 _unit = _this select 0;
423 _attachObj = _this select 1;
424 _attachSpecs = _this select 2;
425 _azimutFix = (_this select 3) + (_attachSpecs select 2); //animation dir fix + snappoint (object) direction fix
426 _attach = _this select 4;
427
428 //wait for the simulation to start
429 waitUntil{time > 0};
430
431 if (isNil "_unit") exitWith {};
432 if (isNull _unit) exitWith {};
433 if !(alive _unit && canMove _unit) exitWith {};
434
435 _attachPos = getPosASL _attachObj;
436
437 //create a logic for attaching of the unit
438 //_group = createGroup west;
439 //_group = group _unit;
440 _group = BIS_fnc_ambientAnim__group;
441
442 _logic = _group createUnit ["Logic", [_attachPos select 0,_attachPos select 1,0], [], 0, "NONE"];
443
444 if (isNull _logic) exitWith
445 {
446 _unit call BIS_fnc_ambientAnim__playAnim;
447
448 if (count units _group == 0) then
449 {
450 deleteGroup _group;
451 };
452 };
453
454 _logic setPosASL _attachPos;
455 _logic setDir ((getDir _attachObj) + _azimutFix);
456
457 //4debug
458 _unit setVariable ["BIS_fnc_ambientAnim__logic",_logic];
459 _unit setVariable ["BIS_fnc_ambientAnim__helper",_attachObj];
460
461 //attach the unit to the game logic
462 if (_attach) then
463 {
464 _unit attachTo [_logic,_attachSpecs select 1];
465 _unit setVariable ["BIS_fnc_ambientAnim__attached",true];
466 };
467
468 //"smart-select" animation that is not played on nearby unit and play it
469 _unit call BIS_fnc_ambientAnim__playAnim;
470
471 //play next anim when previous finishes
472 _ehAnimDone = _unit addEventHandler
473 [
474 "AnimDone",
475 {
476 private["_unit","_anim","_pool"];
477
478 _unit = _this select 0;
479 _anim = _this select 1;
480 _pool = _unit getVariable ["BIS_fnc_ambientAnim__anims",[]];
481
482 //["[%1] Anim finished: %2",_unit,_anim] call BIS_fnc_logFormat;
483
484 //ignore all non-animset animations
485 /*
486 if !(_anim in _pool) exitWith
487 {
488 ["[i][%1] Anim '%2' not in unit's animset!",_unit,_anim] call BIS_fnc_logFormat;
489 };
490 */
491
492 if (alive _unit) then
493 {
494 _unit call BIS_fnc_ambientAnim__playAnim;
495 }
496 else
497 {
498 _unit call BIS_fnc_ambientAnim__terminate;
499 };
500 }
501 ];
502 _unit setVariable ["BIS_EhAnimDone", _ehAnimDone];
503
504 //free unit from anim loop if it is killed
505 _ehKilled = _unit addEventHandler
506 [
507 "Killed",
508 {
509 (_this select 0) call BIS_fnc_ambientAnim__terminate;
510 }
511 ];
512 _unit setVariable ["BIS_EhKilled", _ehKilled];
513};
514
515//_unit call BIS_fnc_ambientAnim__playAnim;
516BIS_fnc_ambientAnim__playAnim =
517{
518 private["_unit","_anims","_anim","_available","_time","_linkedUnits","_linkedAnims","_xTime","_interpolate"];
519
520 if (isNull _this) exitWith {};
521 if !(alive _this && canMove _this) exitWith {};
522
523 _unit = _this;
524 _anims = _unit getVariable ["BIS_fnc_ambientAnim__anims",[]];
525
526 if (count _anims == 0) exitWith
527 {
528 ["Unit '%1' doesn't have defined ambient anims!",_unit,_anims] call BIS_fnc_logFormat;
529 };
530
531 _linkedUnits = _unit getVariable ["BIS_fnc_ambientAnim__linked",[]];
532
533 //find animations that are being played on linked units
534 _linkedAnims = [];
535
536 _time = time - 10;
537
538 {
539 _xTime = _x getVariable ["BIS_fnc_ambientAnim__time",_time];
540
541 if (_xTime > _time) then
542 {
543 _linkedAnims = _linkedAnims + [animationState _x];
544 };
545 }
546 forEach _linkedUnits;
547
548 //get animations available for player = not recently played by linked units
549 _available = _anims - _linkedAnims;
550
551 if (count _available == 0) then
552 {
553 ["Unit '%1' doesn't have an available/free animation to play",_unit] call BIS_fnc_logFormat;
554
555 _available = _anims;
556 };
557
558 //select a random anim from the pool of available animations and play it
559 _anim = _available call BIS_fnc_selectRandom;
560
561 _interpolate = _unit getVariable ["BIS_fnc_ambientAnim__interpolate",false];
562
563 if (_interpolate) then
564 {
565 _unit playMoveNow _anim;
566 }
567 else
568 {
569 _unit switchMove _anim;
570 };
571};
572
573//_unit call BIS_fnc_ambientAnim__terminate;
574BIS_fnc_ambientAnim__terminate =
575{
576 private["_unit","_ehAnimDone","_ehKilled","_fnc_log_disable","_detachCode"];
577
578 _fnc_log_disable = false;
579
580 if (typeName _this == typeName []) exitWith
581 {
582 {
583 _x call BIS_fnc_ambientAnim__terminate;
584 }
585 forEach _this;
586 };
587
588 if (typeName _this != typeName objNull) exitWith {};
589
590 if (isNull _this) exitWith {};
591
592 _unit = _this;
593
594 //["[%1] Terminating an ambient animation ...",_unit] call BIS_fnc_logFormat;
595
596 /*
597 if !(alive _unit) exitWith
598 {
599 ["[%1] Ambient animation was not terminated, unit is dead!",_unit] call BIS_fnc_logFormat;
600 };
601 */
602
603 /*
604 if !(_unit getVariable ["BIS_fnc_ambientAnim__attached",false]) exitWith
605 {
606 ["[%1] Ambient animation was not terminated, unit is not attached!",_unit] call BIS_fnc_logFormat;
607 };
608 */
609
610 //enable the unit "intelligence"
611 {_unit enableAI _x} forEach ["ANIM", "AUTOTARGET", "FSM", "MOVE", "TARGET"];
612
613 //remove the event handlers
614 _ehAnimDone = _unit getVariable ["BIS_EhAnimDone",-1];
615 _ehKilled = _unit getVariable ["BIS_EhKilled",-1];
616
617 if (_ehAnimDone != -1) then
618 {
619 _unit removeEventHandler ["AnimDone",_ehAnimDone];
620 _unit setVariable ["BIS_EhAnimDone",-1];
621 };
622 if (_ehKilled != -1) then
623 {
624 _unit removeEventHandler ["Killed",_ehKilled];
625 _unit setVariable ["BIS_EhKilled",-1];
626 };
627
628 _detachCode =
629 {
630 private["_logic"];
631
632 //exit if object/unit doesn't exist
633 if (isNull _this) exitWith {};
634
635 _logic = _this getVariable ["BIS_fnc_ambientAnim__logic",objNull];
636
637 //delete the game logic
638 if !(isNull _logic) then
639 {
640 deleteVehicle _logic;
641 };
642
643 //["[%1] Ambient animation-set [%2] terminated!",_this,_this getVariable ["BIS_fnc_ambientAnim__animset",""]] call BIS_fnc_logFormat;
644
645 _this setVariable ["BIS_fnc_ambientAnim__attached",nil];
646 _this setVariable ["BIS_fnc_ambientAnim__animset",nil];
647 _this setVariable ["BIS_fnc_ambientAnim__anims",nil];
648 _this setVariable ["BIS_fnc_ambientAnim__interpolate",nil];
649 _this setVariable ["BIS_fnc_ambientAnim__time",nil];
650 _this setVariable ["BIS_fnc_ambientAnim__logic",nil];
651 _this setVariable ["BIS_fnc_ambientAnim__helper",nil];
652 //_this setVariable ["BIS_fnc_ambientAnim__weapon",nil];
653 _this setVariable ["BIS_fnc_ambientAnim__linked",nil];
654
655 //detach unit, so it can freely move
656 detach _this;
657
658 if (alive _this) then {
659 _this switchMove "";
660 };
661 };
662
663 if (time > 0) then
664 {
665 _unit call _detachCode;
666 }
667 else
668 {
669 [_unit,_detachCode] spawn
670 {
671 sleep 0.3; (_this select 0) call (_this select 1);
672 };
673 };
674};