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 | }; |