View difference between Paste ID: xSiUUq1E and 5SbNj6F5
SHOW: | | - or go back to the newest paste.
1
diag_log "Towing script line 1 found...";
2
#define SA_Find_Surface_ASL_Under_Position(_object,_positionAGL,_returnSurfaceASL,_canFloat) \
3
_objectASL = AGLToASL (_object modelToWorldVisual (getCenterOfMass _object)); \
4
_surfaceIntersectStartASL = [_positionAGL select 0, _positionAGL select 1, (_objectASL select 2) + 1]; \
5
_surfaceIntersectEndASL = [_positionAGL select 0, _positionAGL select 1, (_objectASL select 2) - 5]; \
6
_surfaces = lineIntersectsSurfaces [_surfaceIntersectStartASL, _surfaceIntersectEndASL, _object, objNull, true, 5]; \
7
_returnSurfaceASL = AGLToASL _positionAGL; \
8
{ \
9
    scopeName "surfaceLoop"; \
10
    if( isNull (_x select 2) ) then { \
11
        _returnSurfaceASL = _x select 0; \
12
        breakOut "surfaceLoop"; \
13
    } else { \
14
        if!((_x select 2) isKindOf "RopeSegment") then { \
15
            _objectFileName = str (_x select 2); \
16
            if((_objectFileName find " t_") == -1 && (_objectFileName find " b_") == -1) then { \
17
                _returnSurfaceASL = _x select 0; \
18
                breakOut "surfaceLoop"; \
19
            }; \
20
        }; \
21
    }; \
22
} forEach _surfaces; \
23
if(_canFloat && (_returnSurfaceASL select 2) < 0) then { \
24
    _returnSurfaceASL set [2,0]; \
25
}; \
26
diag_log "Towing script started..."; 
27
#define SA_Find_Surface_ASL_Under_Model(_object,_modelOffset,_returnSurfaceASL,_canFloat) \
28
SA_Find_Surface_ASL_Under_Position(_object, (_object modelToWorldVisual _modelOffset), _returnSurfaceASL,_canFloat);
29
 
30
#define SA_Find_Surface_AGL_Under_Model(_object,_modelOffset,_returnSurfaceAGL,_canFloat) \
31
SA_Find_Surface_ASL_Under_Model(_object,_modelOffset,_returnSurfaceAGL,_canFloat); \
32
_returnSurfaceAGL = ASLtoAGL _returnSurfaceAGL;
33
 
34
#define SA_Get_Cargo(_vehicle,_cargo) \
35
if( count (ropeAttachedObjects _vehicle) == 0 ) then { \
36
    _cargo = objNull; \
37
} else { \
38
    _cargo = ((ropeAttachedObjects _vehicle) select 0) getVariable ["SA_Cargo",objNull]; \
39
};
40
 
41
SA_Advanced_Towing_Install = {
42
 
43
// Prevent advanced towing from installing twice
44-
if(!isNil "SA_TOW_INIT") exitWith {};
44+
if(!isNil "SA_TOW_INIT") exitWith {diag_log "Towing script exited! SA_TOW_INIT already defined"};
45
SA_TOW_INIT = true;
46
 
47
diag_log "Advanced Towing Loading...";
48
 
49
SA_Simulate_Towing_Speed = {
50
 
51
    params ["_vehicle"];
52
 
53
    private ["_runSimulation","_currentCargo","_maxVehicleSpeed","_maxTowedVehicles","_vehicleMass"];
54
 
55
    _maxVehicleSpeed = getNumber (configFile >> "CfgVehicles" >> typeOf _vehicle >> "maxSpeed");
56
    _vehicleMass = 1000 max (getMass _vehicle);
57
    _maxTowedCargo = missionNamespace getVariable ["SA_MAX_TOWED_CARGO",2];
58
    _runSimulation = true;
59
 
60
    private ["_currentVehicle","_totalCargoMass","_totalCargoCount","_findNextCargo","_towRopes","_ropeLength"];
61
    private ["_ends","_endsDistance","_currentMaxSpeed","_newMaxSpeed"];
62
 
63
    while {_runSimulation} do {
64
 
65
        // Calculate total mass and count of cargo being towed (only takes into account
66
        // cargo that's actively being towed (e.g. there's no slack in the rope)
67
 
68
        _currentVehicle = _vehicle;
69
        _totalCargoMass = 0;
70
        _totalCargoCount = 0;
71
        _findNextCargo = true;
72
        while {_findNextCargo} do {
73
            _findNextCargo = false;
74
            SA_Get_Cargo(_currentVehicle,_currentCargo);
75
            if(!isNull _currentCargo) then {
76
                _towRopes = _currentVehicle getVariable ["SA_Tow_Ropes",[]];
77
                if(count _towRopes > 0) then {
78
                    _ropeLength = ropeLength (_towRopes select 0);
79
                    _ends = ropeEndPosition (_towRopes select 0);
80
                    _endsDistance = (_ends select 0) distance (_ends select 1);
81
                    if( _endsDistance >= _ropeLength - 2 ) then {
82
                        _totalCargoMass = _totalCargoMass + (1000 max (getMass _currentCargo));
83
                        _totalCargoCount = _totalCargoCount + 1;
84
                        _currentVehicle = _currentCargo;
85
                        _findNextCargo = true;
86
                    };
87
                };
88
            };
89
        };
90
 
91
        _newMaxSpeed = _maxVehicleSpeed / (1 max ((_totalCargoMass /  _vehicleMass) * 2));
92
        _newMaxSpeed = (_maxVehicleSpeed * 0.75) min _newMaxSpeed;
93
 
94
        // Prevent vehicle from moving if trying to move more cargo than pre-defined max
95
        if(_totalCargoCount > _maxTowedCargo) then {
96
            _newMaxSpeed = 0;
97
        };
98
 
99
        _currentMaxSpeed = _vehicle getVariable ["SA_Max_Tow_Speed",_maxVehicleSpeed];
100
 
101
        if(_currentMaxSpeed != _newMaxSpeed) then {
102
            _vehicle setVariable ["SA_Max_Tow_Speed",_newMaxSpeed];
103
        };
104
 
105
        sleep 0.1;
106
 
107
    };
108
};
109
 
110
SA_Simulate_Towing = {
111
 
112
    params ["_vehicle","_vehicleHitchModelPos","_cargo","_cargoHitchModelPos","_ropeLength"];
113
 
114
    private ["_lastCargoHitchPosition","_lastCargoVectorDir","_cargoLength","_maxDistanceToCargo","_lastMovedCargoPosition","_cargoHitchPoints"];
115
    private ["_vehicleHitchPosition","_cargoHitchPosition","_newCargoHitchPosition","_cargoVector","_movedCargoVector","_attachedObjects","_currentCargo"];
116
    private ["_newCargoDir","_lastCargoVectorDir","_newCargoPosition","_doExit","_cargoPosition","_vehiclePosition","_maxVehicleSpeed","_vehicleMass","_cargoMass","_cargoCanFloat"];
117
    private ["_cargoCorner1AGL","_cargoCorner1ASL","_cargoCorner2AGL","_cargoCorner2ASL","_cargoCorner3AGL","_cargoCorner3ASL","_cargoCorner4AGL","_cargoCorner4ASL","_surfaceNormal1","_surfaceNormal2","_surfaceNormal"];
118
    private ["_cargoCenterASL","_surfaceHeight","_surfaceHeight2","_maxSurfaceHeight"];
119
 
120
    _maxVehicleSpeed = getNumber (configFile >> "CfgVehicles" >> typeOf _vehicle >> "maxSpeed");
121
    _cargoCanFloat = if( getNumber (configFile >> "CfgVehicles" >> typeOf _cargo >> "canFloat") == 1 ) then { true } else { false };
122
 
123
    private ["_cargoCenterOfMassAGL","_cargoModelCenterGroundPosition"];
124
    SA_Find_Surface_AGL_Under_Model(_cargo,getCenterOfMass _cargo,_cargoCenterOfMassAGL,_cargoCanFloat);
125
    _cargoModelCenterGroundPosition = _cargo worldToModelVisual _cargoCenterOfMassAGL;
126
    _cargoModelCenterGroundPosition set [0,0];
127
    _cargoModelCenterGroundPosition set [1,0];
128
    _cargoModelCenterGroundPosition set [2, (_cargoModelCenterGroundPosition select 2) - 0.05]; // Adjust height so that it doesn't ride directly on ground
129
 
130
    // Calculate cargo model corner points
131
    private ["_cargoCornerPoints"];
132
    _cargoCornerPoints = [_cargo] call SA_Get_Corner_Points;
133
    _corner1 = _cargoCornerPoints select 0;
134
    _corner2 = _cargoCornerPoints select 1;
135
    _corner3 = _cargoCornerPoints select 2;
136
    _corner4 = _cargoCornerPoints select 3;
137
 
138
 
139
    // Try to set cargo owner if the towing client doesn't own the cargo
140
    if(local _vehicle && !local _cargo) then {
141
        [[_cargo, clientOwner],"SA_Set_Owner"] call SA_RemoteExecServer;
142
    };
143
 
144
    _vehicleHitchModelPos set [2,0];
145
    _cargoHitchModelPos set [2,0];
146
 
147
    _lastCargoHitchPosition = _cargo modelToWorld _cargoHitchModelPos;
148
    _lastCargoVectorDir = vectorDir _cargo;
149
    _lastMovedCargoPosition = getPos _cargo;
150
 
151
    _cargoHitchPoints = [_cargo] call SA_Get_Hitch_Points;
152
    _cargoLength = (_cargoHitchPoints select 0) distance (_cargoHitchPoints select 1);
153
 
154
    _vehicleMass = 1 max (getMass _vehicle);
155
    _cargoMass = getMass _cargo;
156
    if(_cargoMass == 0) then {
157
        _cargoMass = _vehicleMass;
158
    };
159
 
160
    _maxDistanceToCargo = _ropeLength;
161
 
162
    _doExit = false;
163
 
164
    // Start vehicle speed simulation
165
    [_vehicle] spawn SA_Simulate_Towing_Speed;
166
 
167
    while {!_doExit} do {
168
 
169
        _vehicleHitchPosition = _vehicle modelToWorld _vehicleHitchModelPos;
170
        _vehicleHitchPosition set [2,0];
171
        _cargoHitchPosition = _lastCargoHitchPosition;
172
        _cargoHitchPosition set [2,0];
173
 
174
        _cargoPosition = getPos _cargo;
175
        _vehiclePosition = getPos _vehicle;
176
 
177
        if(_vehicleHitchPosition distance _cargoHitchPosition > _maxDistanceToCargo) then {
178
 
179
            // Calculated simulated towing position + direction
180
            _newCargoHitchPosition = _vehicleHitchPosition vectorAdd ((_vehicleHitchPosition vectorFromTo _cargoHitchPosition) vectorMultiply _ropeLength);
181
            _cargoVector = _lastCargoVectorDir vectorMultiply _cargoLength;
182
            _movedCargoVector = _newCargoHitchPosition vectorDiff _lastCargoHitchPosition;
183
            _newCargoDir = vectorNormalized (_cargoVector vectorAdd _movedCargoVector);
184
            //if(_isRearCargoHitch) then {
185
            //  _newCargoDir = _newCargoDir vectorMultiply -1;
186
            //};
187
            _lastCargoVectorDir = _newCargoDir;
188
            _newCargoPosition = _newCargoHitchPosition vectorAdd (_newCargoDir vectorMultiply -(vectorMagnitude (_cargoHitchModelPos)));
189
 
190
            SA_Find_Surface_ASL_Under_Position(_cargo,_newCargoPosition,_newCargoPosition,_cargoCanFloat);
191
 
192
            // Calculate surface normal (up) (more realistic than surfaceNormal function)
193
            SA_Find_Surface_ASL_Under_Model(_cargo,_corner1,_cargoCorner1ASL,_cargoCanFloat);
194
            SA_Find_Surface_ASL_Under_Model(_cargo,_corner2,_cargoCorner2ASL,_cargoCanFloat);
195
            SA_Find_Surface_ASL_Under_Model(_cargo,_corner3,_cargoCorner3ASL,_cargoCanFloat);
196
            SA_Find_Surface_ASL_Under_Model(_cargo,_corner4,_cargoCorner4ASL,_cargoCanFloat);
197
            _surfaceNormal1 = (_cargoCorner1ASL vectorFromTo _cargoCorner3ASL) vectorCrossProduct (_cargoCorner1ASL vectorFromTo _cargoCorner2ASL);
198
            _surfaceNormal2 = (_cargoCorner4ASL vectorFromTo _cargoCorner2ASL) vectorCrossProduct (_cargoCorner4ASL vectorFromTo _cargoCorner3ASL);
199
            _surfaceNormal = _surfaceNormal1 vectorAdd _surfaceNormal2;
200
 
201
            if(missionNamespace getVariable ["SA_TOW_DEBUG_ENABLED", false]) then {
202
                if(isNil "sa_tow_debug_arrow_1") then {
203
                    sa_tow_debug_arrow_1 = "Sign_Arrow_F" createVehicleLocal [0,0,0];
204
                    sa_tow_debug_arrow_2 = "Sign_Arrow_F" createVehicleLocal [0,0,0];
205
                    sa_tow_debug_arrow_3 = "Sign_Arrow_F" createVehicleLocal [0,0,0];
206
                    sa_tow_debug_arrow_4 = "Sign_Arrow_F" createVehicleLocal [0,0,0];
207
                };
208
                sa_tow_debug_arrow_1 setPosASL _cargoCorner1ASL;
209
                sa_tow_debug_arrow_1 setVectorUp _surfaceNormal;
210
                sa_tow_debug_arrow_2 setPosASL _cargoCorner2ASL;
211
                sa_tow_debug_arrow_2 setVectorUp _surfaceNormal;
212
                sa_tow_debug_arrow_3 setPosASL _cargoCorner3ASL;
213
                sa_tow_debug_arrow_3 setVectorUp _surfaceNormal;
214
                sa_tow_debug_arrow_4 setPosASL _cargoCorner4ASL;
215
                sa_tow_debug_arrow_4 setVectorUp _surfaceNormal;
216
            };
217
 
218
            // Calculate adjusted surface height based on surface normal (prevents vehicle from clipping into ground)
219
            _cargoCenterASL = AGLtoASL (_cargo modelToWorldVisual [0,0,0]);
220
            _cargoCenterASL set [2,0];
221
            _surfaceHeight = ((_cargoCorner1ASL vectorAdd ( _cargoCenterASL vectorMultiply -1)) vectorDotProduct _surfaceNormal1) /  ([0,0,1] vectorDotProduct _surfaceNormal1);
222
            _surfaceHeight2 = ((_cargoCorner1ASL vectorAdd ( _cargoCenterASL vectorMultiply -1)) vectorDotProduct _surfaceNormal2) /  ([0,0,1] vectorDotProduct _surfaceNormal2);
223
            _maxSurfaceHeight = (_newCargoPosition select 2) max _surfaceHeight max _surfaceHeight2;
224
            _newCargoPosition set [2, _maxSurfaceHeight ];
225
 
226
            _newCargoPosition = _newCargoPosition vectorAdd ( _cargoModelCenterGroundPosition vectorMultiply -1 );
227
 
228
            _cargo setVectorDir _newCargoDir;
229
            _cargo setVectorUp _surfaceNormal;
230
            _cargo setPosWorld _newCargoPosition;
231
 
232
            _lastCargoHitchPosition = _newCargoHitchPosition;
233
            _maxDistanceToCargo = _vehicleHitchPosition distance _newCargoHitchPosition;
234
            _lastMovedCargoPosition = _cargoPosition;
235
 
236
            _massAdjustedMaxSpeed = _vehicle getVariable ["SA_Max_Tow_Speed",_maxVehicleSpeed];
237
            if(speed _vehicle > (_massAdjustedMaxSpeed)+0.1) then {
238
                _vehicle setVelocity ((vectorNormalized (velocity _vehicle)) vectorMultiply (_massAdjustedMaxSpeed/3.6));
239
            };
240
 
241
        } else {
242
 
243
            if(_lastMovedCargoPosition distance _cargoPosition > 2) then {
244
                _lastCargoHitchPosition = _cargo modelToWorld _cargoHitchModelPos;
245
                _lastCargoVectorDir = vectorDir _cargo;
246
            };
247
 
248
        };
249
 
250
        // If vehicle isn't local to the client, switch client running towing simulation
251
        if(!local _vehicle) then {
252
            [_this,"SA_Simulate_Towing",_vehicle] call SA_RemoteExec;
253
            _doExit = true;
254
        };
255
 
256
        // If the vehicle isn't towing anything, stop the towing simulation
257
        SA_Get_Cargo(_vehicle,_currentCargo);
258
        if(isNull _currentCargo) then {
259
            _doExit = true;
260
        };
261
 
262
        sleep 0.01;
263
 
264
    };
265
};
266
 
267
SA_Get_Corner_Points = {
268
    params ["_vehicle"];
269
    private ["_centerOfMass","_bbr","_p1","_p2","_rearCorner","_rearCorner2","_frontCorner","_frontCorner2"];
270
    private ["_maxWidth","_widthOffset","_maxLength","_lengthOffset","_widthFactor","_lengthFactor"];
271
 
272
    // Correct width and length factor for air
273
    _widthFactor = 0.75;
274
    _lengthFactor = 0.75;
275
    if(_vehicle isKindOf "Air") then {
276
        _widthFactor = 0.3;
277
    };
278
    if(_vehicle isKindOf "Helicopter") then {
279
        _widthFactor = 0.2;
280
        _lengthFactor = 0.45;
281
    };
282
 
283
    _centerOfMass = getCenterOfMass _vehicle;
284
    _bbr = boundingBoxReal _vehicle;
285
    _p1 = _bbr select 0;
286
    _p2 = _bbr select 1;
287
    _maxWidth = abs ((_p2 select 0) - (_p1 select 0));
288
    _widthOffset = ((_maxWidth / 2) - abs ( _centerOfMass select 0 )) * _widthFactor;
289
    _maxLength = abs ((_p2 select 1) - (_p1 select 1));
290
    _lengthOffset = ((_maxLength / 2) - abs (_centerOfMass select 1 )) * _lengthFactor;
291
    _rearCorner = [(_centerOfMass select 0) + _widthOffset, (_centerOfMass select 1) - _lengthOffset, _centerOfMass select 2];
292
    _rearCorner2 = [(_centerOfMass select 0) - _widthOffset, (_centerOfMass select 1) - _lengthOffset, _centerOfMass select 2];
293
    _frontCorner = [(_centerOfMass select 0) + _widthOffset, (_centerOfMass select 1) + _lengthOffset, _centerOfMass select 2];
294
    _frontCorner2 = [(_centerOfMass select 0) - _widthOffset, (_centerOfMass select 1) + _lengthOffset, _centerOfMass select 2];
295
 
296
    if(missionNamespace getVariable ["SA_TOW_DEBUG_ENABLED", false]) then {
297
        if(isNil "sa_tow_debug_arrow_1") then {
298
            sa_tow_debug_arrow_1 = "Sign_Arrow_F" createVehicleLocal [0,0,0];
299
            sa_tow_debug_arrow_2 = "Sign_Arrow_F" createVehicleLocal [0,0,0];
300
            sa_tow_debug_arrow_3 = "Sign_Arrow_F" createVehicleLocal [0,0,0];
301
            sa_tow_debug_arrow_4 = "Sign_Arrow_F" createVehicleLocal [0,0,0];
302
        };
303
        sa_tow_debug_arrow_1 setPosASL  AGLtoASL (_vehicle modelToWorldVisual _rearCorner);
304
        sa_tow_debug_arrow_2 setPosASL  AGLtoASL (_vehicle modelToWorldVisual _rearCorner2);
305
        sa_tow_debug_arrow_3 setPosASL  AGLtoASL (_vehicle modelToWorldVisual _frontCorner);
306
        sa_tow_debug_arrow_4 setPosASL  AGLtoASL (_vehicle modelToWorldVisual _frontCorner2);
307
    };
308
 
309
    [_rearCorner,_rearCorner2,_frontCorner,_frontCorner2];
310
};
311
 
312
SA_Get_Hitch_Points = {
313
    params ["_vehicle"];
314
    private ["_cornerPoints","_rearCorner","_rearCorner2","_frontCorner","_frontCorner2","_rearHitchPoint"];
315
    private ["_frontHitchPoint","_sideLeftPoint","_sideRightPoint"];
316
    _cornerPoints = [_vehicle] call SA_Get_Corner_Points;
317
    _rearCorner = _cornerPoints select 0;
318
    _rearCorner2 = _cornerPoints select 1;
319
    _frontCorner = _cornerPoints select 2;
320
    _frontCorner2 = _cornerPoints select 3;
321
    _rearHitchPoint = ((_rearCorner vectorDiff _rearCorner2) vectorMultiply 0.5) vectorAdd  _rearCorner2;
322
    _frontHitchPoint = ((_frontCorner vectorDiff _frontCorner2) vectorMultiply 0.5) vectorAdd  _frontCorner2;
323
    //_sideLeftPoint = ((_frontCorner vectorDiff _rearCorner) vectorMultiply 0.5) vectorAdd  _frontCorner;
324
    //_sideRightPoint = ((_frontCorner2 vectorDiff _rearCorner2) vectorMultiply 0.5) vectorAdd  _frontCorner2;
325
    [_frontHitchPoint,_rearHitchPoint];
326
};
327
 
328
SA_Attach_Tow_Ropes = {
329
    params ["_cargo","_player"];
330
    _vehicle = _player getVariable ["SA_Tow_Ropes_Vehicle", objNull];
331
    if(!isNull _vehicle) then {
332
        if(local _vehicle) then {
333
            private ["_towRopes","_vehicleHitch","_cargoHitch","_objDistance","_ropeLength"];
334
            _towRopes = _vehicle getVariable ["SA_Tow_Ropes",[]];
335
            if(count _towRopes == 1) then {
336
 
337
                /*
338
                private ["_cargoHitchPoints","_distanceToFrontHitch","_distanceToRearHitch","_isRearCargoHitch"];
339
                _cargoHitchPoints = [_cargo] call SA_Get_Hitch_Points;
340
                _distanceToFrontHitch = player distance (_cargo modelToWorld (_cargoHitchPoints select 0));
341
                _distanceToRearHitch = player distance (_cargo modelToWorld (_cargoHitchPoints select 1));
342
                if( _distanceToFrontHitch < _distanceToRearHitch ) then {
343
                    _cargoHitch = _cargoHitchPoints select 0;
344
                    _isRearCargoHitch = false;
345
                } else {
346
                    _cargoHitch = _cargoHitchPoints select 1;
347
                    _isRearCargoHitch = true;
348
                };
349
                */
350
 
351
                _cargoHitch = ([_cargo] call SA_Get_Hitch_Points) select 0;
352
 
353
                _vehicleHitch = ([_vehicle] call SA_Get_Hitch_Points) select 1;
354
                _ropeLength = (ropeLength (_towRopes select 0));
355
                _objDistance = ((_vehicle modelToWorld _vehicleHitch) distance (_cargo modelToWorld _cargoHitch));
356
                if( _objDistance > _ropeLength ) then {
357
                    [["The tow ropes are too short. Move vehicle closer.", false],"SA_Hint",_player] call SA_RemoteExec;
358
                } else {
359
                    [_vehicle,_player] call SA_Drop_Tow_Ropes;
360
                    _helper = "Land_Can_V2_F" createVehicle position _cargo;
361
                    _helper attachTo [_cargo, _cargoHitch];
362
                    _helper setVariable ["SA_Cargo",_cargo,true];
363
                    hideObject _helper;
364
                    [[_helper],"SA_Hide_Object_Global"] call SA_RemoteExecServer;
365
                    [_helper, [0,0,0], [0,0,-1]] ropeAttachTo (_towRopes select 0);
366
                    [_vehicle,_vehicleHitch,_cargo,_cargoHitch,_ropeLength] spawn SA_Simulate_Towing;
367
                };
368
            };
369
        } else {
370
            [_this,"SA_Attach_Tow_Ropes",_vehicle,true] call SA_RemoteExec;
371
        };
372
    };
373
};
374
 
375
SA_Take_Tow_Ropes = {
376
    params ["_vehicle","_player"];
377
    if(local _vehicle) then {
378
        diag_log format ["Take Tow Ropes Called %1", _this];
379
        private ["_existingTowRopes","_hitchPoint","_rope"];
380
        _existingTowRopes = _vehicle getVariable ["SA_Tow_Ropes",[]];
381
        if(count _existingTowRopes == 0) then {
382
            _hitchPoint = [_vehicle] call SA_Get_Hitch_Points select 1;
383
            _rope = ropeCreate [_vehicle, _hitchPoint, 10];
384
            _vehicle setVariable ["SA_Tow_Ropes",[_rope],true];
385
            _this call SA_Pickup_Tow_Ropes;
386
        };
387
    } else {
388
        [_this,"SA_Take_Tow_Ropes",_vehicle,true] call SA_RemoteExec;
389
    };
390
};
391
 
392
SA_Pickup_Tow_Ropes = {
393
    params ["_vehicle","_player"];
394
    if(local _vehicle) then {
395
        private ["_attachedObj","_helper"];
396
        {
397
            _attachedObj = _x;
398
            {
399
                _attachedObj ropeDetach _x;
400
            } forEach (_vehicle getVariable ["SA_Tow_Ropes",[]]);
401
            deleteVehicle _attachedObj;
402
        } forEach ropeAttachedObjects _vehicle;
403
        _helper = "Land_Can_V2_F" createVehicle position _player;
404
        {
405
            [_helper, [0, 0, 0], [0,0,-1]] ropeAttachTo _x;
406
            _helper attachTo [_player, [-0.1, 0.1, 0.15], "Pelvis"];
407
        } forEach (_vehicle getVariable ["SA_Tow_Ropes",[]]);
408
        hideObject _helper;
409
        [[_helper],"SA_Hide_Object_Global"] call SA_RemoteExecServer;
410
        _player setVariable ["SA_Tow_Ropes_Vehicle", _vehicle,true];
411
        _player setVariable ["SA_Tow_Ropes_Pick_Up_Helper", _helper,true];
412
    } else {
413
        [_this,"SA_Pickup_Tow_Ropes",_vehicle,true] call SA_RemoteExec;
414
    };
415
};
416
 
417
SA_Drop_Tow_Ropes = {
418
    params ["_vehicle","_player"];
419
    if(local _vehicle) then {
420
        private ["_helper"];
421
        _helper = (_player getVariable ["SA_Tow_Ropes_Pick_Up_Helper", objNull]);
422
        if(!isNull _helper) then {
423
            {
424
                _helper ropeDetach _x;
425
            } forEach (_vehicle getVariable ["SA_Tow_Ropes",[]]);
426
            detach _helper;
427
            deleteVehicle _helper;
428
        };
429
        _player setVariable ["SA_Tow_Ropes_Vehicle", nil,true];
430
        _player setVariable ["SA_Tow_Ropes_Pick_Up_Helper", nil,true];
431
    } else {
432
        [_this,"SA_Drop_Tow_Ropes",_vehicle,true] call SA_RemoteExec;
433
    };
434
};
435
diag_log "Towing script 50% done..."; 
436
SA_Put_Away_Tow_Ropes = {
437
    params ["_vehicle","_player"];
438
    if(local _vehicle) then {
439
        private ["_existingTowRopes","_hitchPoint","_rope"];
440
        _existingTowRopes = _vehicle getVariable ["SA_Tow_Ropes",[]];
441
        if(count _existingTowRopes > 0) then {
442
            _this call SA_Pickup_Tow_Ropes;
443
            _this call SA_Drop_Tow_Ropes;
444
            {
445
                ropeDestroy _x;
446
            } forEach _existingTowRopes;
447
            _vehicle setVariable ["SA_Tow_Ropes",nil,true];
448
        };
449
    } else {
450
        [_this,"SA_Put_Away_Tow_Ropes",_vehicle,true] call SA_RemoteExec;
451
    };
452
};
453
 
454
SA_Attach_Tow_Ropes_Action = {
455
    private ["_vehicle","_cargo","_canBeTowed"];
456
    _cargo = cursorTarget;
457
    _vehicle = player getVariable ["SA_Tow_Ropes_Vehicle", objNull];
458
    if([_vehicle,_cargo] call SA_Can_Attach_Tow_Ropes) then {
459
 
460
        _canBeTowed = true;
461
 
462
        if!(missionNamespace getVariable ["SA_TOW_LOCKED_VEHICLES_ENABLED",false]) then {
463
            if( locked _cargo > 1 ) then {
464
                ["Cannot attach tow ropes to locked vehicle",false] call SA_Hint;
465
                _canBeTowed = false;
466
            };
467
        };
468
 
469
        if!(missionNamespace getVariable ["SA_TOW_IN_EXILE_SAFEZONE_ENABLED",false]) then {
470
            if(!isNil "ExilePlayerInSafezone") then {
471
                if( ExilePlayerInSafezone ) then {
472
                    ["Cannot attach tow ropes in safe zone",false] call SA_Hint;
473
                    _canBeTowed = false;
474
                };
475
            };
476
        };
477
 
478
        if(_canBeTowed) then {
479
            [_cargo,player] call SA_Attach_Tow_Ropes;
480
        };
481
 
482
    };
483
};
484
 
485
SA_Attach_Tow_Ropes_Action_Check = {
486
    private ["_vehicle","_cargo"];
487
    _vehicle = player getVariable ["SA_Tow_Ropes_Vehicle", objNull];
488
    _cargo = cursorTarget;
489
    [_vehicle,_cargo] call SA_Can_Attach_Tow_Ropes;
490
};
491
 
492
SA_Can_Attach_Tow_Ropes = {
493
    params ["_vehicle","_cargo"];
494
    if(!isNull _vehicle && !isNull _cargo) then {
495
        [_vehicle,_cargo] call SA_Is_Supported_Cargo && vehicle player == player && player distance _cargo < 10 && _vehicle != _cargo;
496
    } else {
497
        false;
498
    };
499
};
500
 
501
SA_Take_Tow_Ropes_Action = {
502
    private ["_vehicle","_canTakeTowRopes"];
503
    _vehicle = cursorTarget;
504
    if([_vehicle] call SA_Can_Take_Tow_Ropes) then {
505
 
506
        _canTakeTowRopes = true;
507
 
508
        if!(missionNamespace getVariable ["SA_TOW_LOCKED_VEHICLES_ENABLED",false]) then {
509
            if( locked _vehicle > 1 ) then {
510
                ["Cannot take tow ropes from locked vehicle",false] call SA_Hint;
511
                _canTakeTowRopes = false;
512
            };
513
        };
514
 
515
        if!(missionNamespace getVariable ["SA_TOW_IN_EXILE_SAFEZONE_ENABLED",false]) then {
516
            if(!isNil "ExilePlayerInSafezone") then {
517
                if( ExilePlayerInSafezone ) then {
518
                    ["Cannot take tow ropes in safe zone",false] call SA_Hint;
519
                    _canTakeTowRopes = false;
520
                };
521
            };
522
        };
523
 
524
        if(_canTakeTowRopes) then {
525
            [_vehicle,player] call SA_Take_Tow_Ropes;
526
        };
527
 
528
    };
529
};
530
 
531
SA_Take_Tow_Ropes_Action_Check = {
532
    [cursorTarget] call SA_Can_Take_Tow_Ropes;
533
};
534
 
535
SA_Can_Take_Tow_Ropes = {
536
    params ["_vehicle"];
537
    if([_vehicle] call SA_Is_Supported_Vehicle) then {
538
        private ["_existingVehicle","_existingTowRopes"];
539
        _existingTowRopes = _vehicle getVariable ["SA_Tow_Ropes",[]];
540
        _existingVehicle = player getVariable ["SA_Tow_Ropes_Vehicle", objNull];
541
        vehicle player == player && player distance _vehicle < 10 && (count _existingTowRopes) == 0 && isNull _existingVehicle;
542
    } else {
543
        false;
544
    };
545
};
546
 
547
SA_Put_Away_Tow_Ropes_Action = {
548
    private ["_vehicle","_canPutAwayTowRopes"];
549
    _vehicle = cursorTarget;
550
    if([_vehicle] call SA_Can_Put_Away_Tow_Ropes) then {
551
 
552
        _canPutAwayTowRopes = true;
553
 
554
        if!(missionNamespace getVariable ["SA_TOW_LOCKED_VEHICLES_ENABLED",false]) then {
555
            if( locked _vehicle > 1 ) then {
556
                ["Cannot put away tow ropes in locked vehicle",false] call SA_Hint;
557
                _canPutAwayTowRopes = false;
558
            };
559
        };
560
 
561
        if!(missionNamespace getVariable ["SA_TOW_IN_EXILE_SAFEZONE_ENABLED",false]) then {
562
            if(!isNil "ExilePlayerInSafezone") then {
563
                if( ExilePlayerInSafezone ) then {
564
                    ["Cannot put away tow ropes in safe zone",false] call SA_Hint;
565
                    _canPutAwayTowRopes = false;
566
                };
567
            };
568
        };
569
 
570
        if(_canPutAwayTowRopes) then {
571
            [_vehicle,player] call SA_Put_Away_Tow_Ropes;
572
        };
573
 
574
    };
575
};
576
 
577
SA_Put_Away_Tow_Ropes_Action_Check = {
578
    [cursorTarget] call SA_Can_Put_Away_Tow_Ropes;
579
};
580
 
581
SA_Can_Put_Away_Tow_Ropes = {
582
    params ["_vehicle"];
583
    private ["_existingTowRopes"];
584
    if([_vehicle] call SA_Is_Supported_Vehicle) then {
585
        _existingTowRopes = _vehicle getVariable ["SA_Tow_Ropes",[]];
586
        vehicle player == player && player distance _vehicle < 10 && (count _existingTowRopes) > 0;
587
    } else {
588
        false;
589
    };
590
};
591
 
592
 
593
SA_Drop_Tow_Ropes_Action = {
594
    private ["_vehicle"];
595
    _vehicle = player getVariable ["SA_Tow_Ropes_Vehicle", objNull];
596
    if([] call SA_Can_Drop_Tow_Ropes) then {
597
        [_vehicle, player] call SA_Drop_Tow_Ropes;
598
    };
599
};
600
 
601
SA_Drop_Tow_Ropes_Action_Check = {
602
    [] call SA_Can_Drop_Tow_Ropes;
603
};
604
 
605
SA_Can_Drop_Tow_Ropes = {
606
    !isNull (player getVariable ["SA_Tow_Ropes_Vehicle", objNull]) && vehicle player == player;
607
};
608
 
609
 
610
 
611
SA_Pickup_Tow_Ropes_Action = {
612
    private ["_nearbyTowVehicles","_canPickupTowRopes","_vehicle"];
613
    _nearbyTowVehicles = missionNamespace getVariable ["SA_Nearby_Tow_Vehicles",[]];
614
    if([] call SA_Can_Pickup_Tow_Ropes) then {
615
 
616
        _vehicle = _nearbyTowVehicles select 0;
617
        _canPickupTowRopes = true;
618
 
619
        if!(missionNamespace getVariable ["SA_TOW_LOCKED_VEHICLES_ENABLED",false]) then {
620
            if( locked _vehicle > 1 ) then {
621
                ["Cannot pick up tow ropes from locked vehicle",false] call SA_Hint;
622
                _canPickupTowRopes = false;
623
            };
624
        };
625
 
626
        if!(missionNamespace getVariable ["SA_TOW_IN_EXILE_SAFEZONE_ENABLED",false]) then {
627
            if(!isNil "ExilePlayerInSafezone") then {
628
                if( ExilePlayerInSafezone ) then {
629
                    ["Cannot pick up tow ropes in safe zone",false] call SA_Hint;
630
                    _canPickupTowRopes = false;
631
                };
632
            };
633
        };
634
 
635
        if(_canPickupTowRopes) then {
636
            [_nearbyTowVehicles select 0, player] call SA_Pickup_Tow_Ropes;
637
        };
638
 
639
    };
640
};
641
 
642
SA_Pickup_Tow_Ropes_Action_Check = {
643
    [] call SA_Can_Pickup_Tow_Ropes;
644
};
645
 
646
SA_Can_Pickup_Tow_Ropes = {
647
    isNull (player getVariable ["SA_Tow_Ropes_Vehicle", objNull]) && count (missionNamespace getVariable ["SA_Nearby_Tow_Vehicles",[]]) > 0 && vehicle player == player;
648
};
649
 
650
SA_TOW_SUPPORTED_VEHICLES = [
651
    "Tank", "Car", "Ship"
652
];
653
 
654
SA_Is_Supported_Vehicle = {
655
    params ["_vehicle","_isSupported"];
656
    _isSupported = false;
657
    if(not isNull _vehicle) then {
658
        {
659
            if(_vehicle isKindOf _x) then {
660
                _isSupported = true;
661
            };
662
        } forEach (missionNamespace getVariable ["SA_TOW_SUPPORTED_VEHICLES_OVERRIDE",SA_TOW_SUPPORTED_VEHICLES]);
663
    };
664
    _isSupported;
665
};
666
 
667
SA_TOW_RULES = [
668
    ["Tank","CAN_TOW","Tank"],
669
    ["Tank","CAN_TOW","Car"],
670
    ["Tank","CAN_TOW","Ship"],
671
    ["Tank","CAN_TOW","Air"],
672
    ["Car","CAN_TOW","Tank"],
673
    ["Car","CAN_TOW","Car"],
674
    ["Car","CAN_TOW","Ship"],
675
    ["Car","CAN_TOW","Air"],
676
    ["Ship","CAN_TOW","Ship"]
677
];
678
 
679
SA_Is_Supported_Cargo = {
680
    params ["_vehicle","_cargo"];
681
    private ["_canTow"];
682
    _canTow = false;
683
    if(not isNull _vehicle && not isNull _cargo) then {
684
        {
685
            if(_vehicle isKindOf (_x select 0)) then {
686
                if(_cargo isKindOf (_x select 2)) then {
687
                    if( (toUpper (_x select 1)) == "CAN_TOW" ) then {
688
                        _canTow = true;
689
                    } else {
690
                        _canTow = false;
691
                    };
692
                };
693
            };
694
        } forEach (missionNamespace getVariable ["SA_TOW_RULES_OVERRIDE",SA_TOW_RULES]);
695
    };
696
    _canTow;
697
};
698
 
699
SA_Hint = {
700
    params ["_msg",["_isSuccess",true]];
701
    if(!isNil "ExileClient_gui_notification_event_addNotification") then {
702
        if(_isSuccess) then {
703
            ["Success", [_msg]] call ExileClient_gui_notification_event_addNotification;
704
        } else {
705
            ["Whoops", [_msg]] call ExileClient_gui_notification_event_addNotification;
706
        };
707
    } else {
708
        hint _msg;
709
    };
710
};
711
 
712
SA_Hide_Object_Global = {
713
    params ["_obj"];
714
    if( _obj isKindOf "Land_Can_V2_F" ) then {
715
        hideObjectGlobal _obj;
716
    };
717
};
718
 
719
SA_Set_Owner = {
720
    params ["_obj","_client"];
721
    _obj setOwner _client;
722
};
723
 
724
SA_Add_Player_Tow_Actions = {
725
 
726
    player addAction ["Deploy Tow Ropes", {
727
        [] call SA_Take_Tow_Ropes_Action;
728
    }, nil, 0, false, true, "", "call SA_Take_Tow_Ropes_Action_Check"];
729
 
730
    player addAction ["Put Away Tow Ropes", {
731
        [] call SA_Put_Away_Tow_Ropes_Action;
732
    }, nil, 0, false, true, "", "call SA_Put_Away_Tow_Ropes_Action_Check"];
733
 
734
    player addAction ["Attach To Tow Ropes", {
735
        [] call SA_Attach_Tow_Ropes_Action;
736
    }, nil, 0, false, true, "", "call SA_Attach_Tow_Ropes_Action_Check"];
737
 
738
    player addAction ["Drop Tow Ropes", {
739
        [] call SA_Drop_Tow_Ropes_Action;
740
    }, nil, 0, false, true, "", "call SA_Drop_Tow_Ropes_Action_Check"];
741
 
742
    player addAction ["Pickup Tow Ropes", {
743
        [] call SA_Pickup_Tow_Ropes_Action;
744
    }, nil, 0, false, true, "", "call SA_Pickup_Tow_Ropes_Action_Check"];
745
 
746
    player addEventHandler ["Respawn", {
747
        player setVariable ["SA_Tow_Actions_Loaded",false];
748
    }];
749
 
750
};
751
 
752
SA_Find_Nearby_Tow_Vehicles = {
753
    private ["_nearVehicles","_nearVehiclesWithTowRopes","_vehicle","_ends","_end1","_end2"];
754
    _nearVehicles = [];
755
    {
756
        _nearVehicles append  (position player nearObjects [_x, 30]);
757
    } forEach (missionNamespace getVariable ["SA_TOW_SUPPORTED_VEHICLES_OVERRIDE",SA_TOW_SUPPORTED_VEHICLES]);
758
    _nearVehiclesWithTowRopes = [];
759
    {
760
        _vehicle = _x;
761
        {
762
            _ends = ropeEndPosition _x;
763
            if(count _ends == 2) then {
764
                _end1 = _ends select 0;
765
                _end2 = _ends select 1;
766
                if(((position player) distance _end1) < 5 || ((position player) distance _end2) < 5 ) then {
767
                    _nearVehiclesWithTowRopes pushBack _vehicle;
768
                }
769
            };
770
        } forEach (_vehicle getVariable ["SA_Tow_Ropes",[]]);
771
    } forEach _nearVehicles;
772
    _nearVehiclesWithTowRopes;
773
};
774
 
775
if(!isDedicated) then {
776
    [] spawn {
777
        while {true} do {
778
            if(!isNull player && isPlayer player) then {
779
                if!( player getVariable ["SA_Tow_Actions_Loaded",false] ) then {
780
                    [] call SA_Add_Player_Tow_Actions;
781
                    player setVariable ["SA_Tow_Actions_Loaded",true];
782
                };
783
            };
784
            missionNamespace setVariable ["SA_Nearby_Tow_Vehicles", (call SA_Find_Nearby_Tow_Vehicles)];
785
            sleep 2;
786
        };
787
    };
788
};
789
 
790
SA_RemoteExec = {
791
    params ["_params","_functionName","_target",["_isCall",false]];
792
    if(!isNil "ExileClient_system_network_send") then {
793
        ["AdvancedTowingRemoteExecClient",[_params,_functionName,_target,_isCall]] call ExileClient_system_network_send;
794
    } else {
795
        if(_isCall) then {
796
            _params remoteExecCall [_functionName, _target];
797
        } else {
798
            _params remoteExec [_functionName, _target];
799
        };
800
    };
801
};
802
 
803
SA_RemoteExecServer = {
804
    params ["_params","_functionName",["_isCall",false]];
805
    if(!isNil "ExileClient_system_network_send") then {
806
        ["AdvancedTowingRemoteExecServer",[_params,_functionName,_isCall]] call ExileClient_system_network_send;
807
    } else {
808
        if(_isCall) then {
809
            _params remoteExecCall [_functionName, 2];
810
        } else {
811
            _params remoteExec [_functionName, 2];
812
        };
813
    };
814
};
815
 
816
if(isServer) then {
817
 
818
    // Adds support for exile network calls (Only used when running exile) //
819
 
820
    SA_SUPPORTED_REMOTEEXECSERVER_FUNCTIONS = ["SA_Set_Owner","SA_Hide_Object_Global"];
821
 
822
    ExileServer_AdvancedTowing_network_AdvancedTowingRemoteExecServer = {
823
        params ["_sessionId", "_messageParameters",["_isCall",false]];
824
        _messageParameters params ["_params","_functionName"];
825
        if(_functionName in SA_SUPPORTED_REMOTEEXECSERVER_FUNCTIONS) then {
826
            if(_isCall) then {
827
                _params call (missionNamespace getVariable [_functionName,{}]);
828
            } else {
829
                _params spawn (missionNamespace getVariable [_functionName,{}]);
830
            };
831
        };
832
    };
833
 
834
    SA_SUPPORTED_REMOTEEXECCLIENT_FUNCTIONS = ["SA_Simulate_Towing","SA_Attach_Tow_Ropes","SA_Take_Tow_Ropes","SA_Put_Away_Tow_Ropes","SA_Pickup_Tow_Ropes","SA_Drop_Tow_Ropes","SA_Hint"];
835
 
836
    ExileServer_AdvancedTowing_network_AdvancedTowingRemoteExecClient = {
837
        params ["_sessionId", "_messageParameters"];
838
        _messageParameters params ["_params","_functionName","_target",["_isCall",false]];
839
        if(_functionName in SA_SUPPORTED_REMOTEEXECCLIENT_FUNCTIONS) then {
840
            if(_isCall) then {
841
                _params remoteExecCall [_functionName, _target];
842
            } else {
843
                _params remoteExec [_functionName, _target];
844
            };
845
        };
846
    };
847
 
848
    // Install Advanced Towing on all clients (plus JIP) //
849
 
850
    publicVariable "SA_Advanced_Towing_Install";
851
    remoteExecCall ["SA_Advanced_Towing_Install", -2,true];
852
 
853
};
854
 
855
diag_log "Advanced Towing Loaded";
856
 
857
};
858
 
859
if(isServer) then {
860
    [] call SA_Advanced_Towing_Install;
861
};