1+ --[[
2+ -- This resource is not finished. Make sure to read the below notes, as well as always use the latest code version from https://github.com/multitheftauto/mtasa-resources/tree/master/%5Bgameplay%5D/parachute2
3+ -- If you are willing to collaborate finishing this resource, or with testing/feedback
4+ -- Then please visit the #parachute channel in MTA Development Discord ( invite link: https://discord.gg/GNN6PRtTnu )
5+ -- Specifically, the posts around this point are relevant,as they are regarding this resource: https://discord.com/channels/801330706252038164/801411291024457778/1361397061123051832
6+ -- Use on production servers is still discouraged, unless you're satisfied with what you see and think it's already better than MTA's parachuting up until now
7+ -- Special thanks to -ffs-Plasma (initial version, written from scratch, and his research on original SA behavior/Parachute SCM)
8+ --]]
9+
10+ local strParachuteState = " NONE" ;
11+ local strPlayerState = " GROUND" ;
12+ local strPlayerAction = " NONE" ;
13+ local bHasParachute = false ;
14+ local uParachuteSound = nil ;
15+ local uParachuteObject = nil ;
16+ local uParachuteCollision = nil ;
17+ local iParachuteAnim = false ;
18+ local fParachuteX = 0 ;
19+ local fParachuteY = 0 ;
20+ local fParachuteZ = 0 ;
21+ local fParachuteYaw = 0 ;
22+ local fParachuteRoll = 0 ;
23+ local fParachutePitch = 0 ;
24+ local fRotationX , fRotationY , fRotationZ = 0 , 0 , 0 ;
25+ local fEasingProgress = 0 ;
26+ local fEasingProgress2 = 0 ;
27+ local fEasingProgress3 = 0 ;
28+
29+ local fAccelerate = 0.1 ;
30+
31+ local function handleParachuteLogic ()
32+ if (getElementHealth (localPlayer ) > 0 ) then
33+ -- get pos/rot/vel/ground from localPlayer
34+ local fPX , fPY , fPZ = getElementPosition (localPlayer );
35+ local fRX , fRY , fRZ = getElementRotation (localPlayer );
36+ local fVX , fVY , fVZ = getElementVelocity (localPlayer );
37+ local fGroundPosition = getGroundPosition (fPX , fPY , fPZ );
38+
39+ -- get player input
40+ local fControlForwards = getPedAnalogControlState (localPlayer , " forwards" );
41+ local fControlBackwards = getPedAnalogControlState (localPlayer , " backwards" );
42+ local fControlLeft = getPedAnalogControlState (localPlayer , " left" );
43+ local fControlRight = getPedAnalogControlState (localPlayer , " right" );
44+
45+ -- movement/direction velocity
46+ local fMVX , fMVY , fMVZ , fMRZ = 0 , 0 , 0 , 0 ;
47+
48+ -- check if player got parachute
49+ if (getPedWeapon (localPlayer , 11 ) ~= 0 ) then
50+ if (getPedTotalAmmo (localPlayer , 11 ) > 0 ) then
51+ bHasParachute = true ;
52+ end
53+ end
54+
55+ -- if we do anything with parachute, disable weapon switching
56+ if (strParachuteState ~= " NONE" ) then
57+ toggleControl (" next_weapon" , false );
58+ toggleControl (" previous_weapon" , false );
59+ end
60+
61+ -- can we go to freefall/skydive
62+ if (strPlayerState == " GROUND" and bHasParachute ) then
63+ fRotationX , fRotationY , fRotationZ = getElementRotation (localPlayer );
64+
65+ if (not isPedOnGround (localPlayer ) and not getPedContactElement (localPlayer )) then
66+ if (fVZ < - 0.1 ) then
67+ if ((fPZ - fGroundPosition ) > 20 ) then
68+ if (isElement (uParachuteSound )) then destroyElement (uParachuteSound ) end
69+ setPedWeaponSlot (localPlayer , 11 );
70+ setPedAnimation (localPlayer , " parachute" , " fall_skydive" , - 1 , true , true , false , false );
71+ uParachuteSound = playSFX (" genrl" , 137 , 20 , true );
72+ strParachuteState = " READY" ;
73+ strPlayerState = " FREEFALL" ;
74+ end
75+ end
76+ end
77+ end
78+
79+ -- player is in freefall aka. skydive
80+ if (strPlayerState == " FREEFALL" ) then
81+ if (fControlForwards ~= 0 ) then
82+ if (strPlayerAction ~= " FORWARDS" ) then
83+ setPedAnimation (localPlayer , " parachute" , " fall_skydive_accel" , - 2 , true , true , false , false );
84+ end
85+
86+ if (fControlLeft ~= 0 ) then
87+ fRotationZ = fRotationZ - 1 ;
88+ elseif (fControlRight ~= 0 ) then
89+ fRotationZ = fRotationZ + 1 ;
90+ end
91+
92+ if (fRotationZ < 0 ) then
93+ fRotationZ = fRotationZ + 360 ;
94+ elseif (fRotationZ > 360 ) then
95+ fRotationZ = fRotationZ - 360 ;
96+ end
97+
98+ local fPitch = interpolateBetween (0 , 0 , 0 , 50 , 0 , 0 , fEasingProgress , " Linear" );
99+
100+ fEasingProgress = fEasingProgress + 0.01
101+
102+ if (fEasingProgress > 1 ) then
103+ fEasingProgress = 1 ;
104+ end
105+
106+ setElementRotation (localPlayer , fPitch , 0 , fRotationZ );
107+
108+ strPlayerAction = " FORWARDS" ;
109+ elseif (fControlBackwards ~= 0 ) then
110+ if (strPlayerAction ~= " BACKWARDS" ) then
111+ setPedAnimation (localPlayer , " parachute" , " fall_skydive" , - 2 , true , true , false , false );
112+ end
113+
114+ if (fControlLeft ~= 0 ) then
115+ fRotationZ = fRotationZ - 1 ;
116+ elseif (fControlRight ~= 0 ) then
117+ fRotationZ = fRotationZ + 1 ;
118+ end
119+
120+ if (fRotationZ < 0 ) then
121+ fRotationZ = fRotationZ + 360 ;
122+ elseif (fRotationZ > 360 ) then
123+ fRotationZ = fRotationZ - 360 ;
124+ end
125+
126+ local fPitch = interpolateBetween (0 , 0 , 0 , 50 , 0 , 0 , fEasingProgress2 , " Linear" );
127+
128+ fEasingProgress2 = fEasingProgress2 + 0.01 ;
129+
130+ if (fEasingProgress2 > 1 ) then
131+ fEasingProgress2 = 1 ;
132+ end
133+
134+ setElementRotation (localPlayer , fPitch * - 1 , 0 , fRotationZ );
135+
136+ strPlayerAction = " BACKWARDS" ;
137+ else
138+ if (fControlLeft ~= 0 ) then
139+ if (strPlayerAction ~= " LEFT" ) then
140+ setPedAnimation (localPlayer , " parachute" , " fall_skydive_l" , - 2 , true , true , false , false );
141+ end
142+
143+ fRotationZ = fRotationZ - 1 ;
144+
145+ if (fRotationZ < 0 ) then
146+ fRotationZ = fRotationZ + 360 ;
147+ end
148+
149+ setElementRotation (localPlayer , 0 , 0 , fRotationZ );
150+
151+ strPlayerAction = " LEFT" ;
152+ elseif (fControlRight ~= 0 ) then
153+ if (strPlayerAction ~= " RIGHT" ) then
154+ setPedAnimation (localPlayer , " parachute" , " fall_skydive_r" , - 2 , true , true , false , false );
155+ end
156+
157+ fRotationZ = fRotationZ + 1 ;
158+
159+ if (fRotationZ > 360 ) then
160+ fRotationZ = fRotationZ - 360 ;
161+ end
162+
163+ setElementRotation (localPlayer , 0 , 0 , fRotationZ );
164+
165+ strPlayerAction = " RIGHT" ;
166+ else
167+ if (strPlayerAction ~= " NONE" ) then
168+ setPedAnimation (localPlayer , " parachute" , " fall_skydive" , - 2 , true , true , false , false );
169+ end
170+
171+ strPlayerAction = " NONE" ;
172+ end
173+
174+ if (fEasingProgress > 0 ) then
175+ local fPitch = interpolateBetween (0 , 0 , 0 , 50 , 0 , 0 , fEasingProgress , " Linear" );
176+
177+ fEasingProgress = fEasingProgress - 0.01 ;
178+
179+ if (fEasingProgress < 0 ) then
180+ fEasingProgress = 0 ;
181+ end
182+
183+ setElementRotation (localPlayer , fPitch , 0 , fRotationZ );
184+ end
185+
186+ if (fEasingProgress2 > 0 ) then
187+ local fPitch = interpolateBetween (0 , 0 , 0 , 50 , 0 , 0 , fEasingProgress2 , " Linear" );
188+
189+ fEasingProgress2 = fEasingProgress2 - 0.01 ;
190+
191+ if (fEasingProgress2 < 0 ) then
192+ fEasingProgress2 = 0 ;
193+ end
194+
195+ setElementRotation (localPlayer , fPitch * - 1 , 0 , fRotationZ );
196+ end
197+ end
198+
199+ -- Handle player velocity/rotation (Not sure how yet, block commented out)
200+
201+ --[[
202+
203+ -- X
204+ fParachuteX = fParachuteX / 4.267;
205+ fParachuteX = fParachuteX - fParachuteRoll;
206+ fParachuteX = fParachuteX / 20;
207+ --fParachuteX = fParachuteX * para_time_step;
208+ fParachuteRoll = fParachuteRoll + fParachuteX;
209+
210+ fParachuteX = fParachuteRoll / 5;
211+ --fParachuteX = fParachuteX * para_time_step;
212+ fParachuteYaw = fParachuteX;
213+
214+ if(fParachuteYaw > 180) then
215+ fParachuteYaw = fParachuteYaw - 360;
216+ end
217+
218+ if(fParachuteYaw < -180) then
219+ fParachuteYaw = fParachuteYaw + 360;
220+ end
221+
222+ -- Y
223+ fParachuteX = fParachuteX / 4.267;
224+ fParachuteX = fParachuteX - fParachutePitch;
225+ fParachuteX = fParachuteX / 20;
226+ --fParachuteX = fParachuteX * para_time_step;
227+ fParachutePitch = fParachutePitch + fParachuteX;
228+ --]]
229+
230+ -- player opening parachute
231+ if (getControlState (localPlayer , " fire" ) and strParachuteState == " READY" ) then
232+ strPlayerState = " ACTION" ;
233+ setPedAnimation (localPlayer , " parachute" , " para_open" , - 2 , false , false , false , true );
234+ setPedAnimationSpeed (localPlayer , " para_open" , 8 );
235+
236+ if (isElement (uParachuteSound )) then destroyElement (uParachuteSound ) end
237+ uParachuteSound = playSFX (" genrl" , 137 , 21 , true );
238+
239+ setTimer (function ()
240+ if (isElement (uParachuteSound )) then destroyElement (uParachuteSound ) end
241+ uParachuteSound = playSFX (" genrl" , 137 , 66 , false );
242+ end , 1100 , 1 );
243+ end
244+
245+ -- player hit the ground without opening parachute
246+ if ((fPZ - fGroundPosition ) < 2 ) then
247+ strPlayerState = " HITGROUND" ;
248+ strParachuteState = " NOTREADY" ;
249+ setPedAnimation (localPlayer , " parachute" , " fall_skydive_die" , - 1 , false , false , false , false );
250+ setTimer (cleanupParachute , 2000 , 1 );
251+ end
252+ end
253+
254+ -- player opening parachute
255+ if (strPlayerState == " ACTION" ) then
256+ -- is it too late?
257+ if ((fPZ - fGroundPosition ) < 2 ) then
258+ strPlayerState = " HITGROUND" ;
259+ strParachuteState = " NOTREADY" ;
260+ setPedAnimation (localPlayer , " parachute" , " fall_skydive_die" , - 1 , false , false , false , false );
261+ setTimer (cleanupParachute , 2000 , 1 );
262+ else
263+ if (strParachuteState ~= " OPENING" ) then
264+ strParachuteState = " OPENING" ;
265+
266+ -- prepare parachute opening animation
267+ uParachuteObject = createObject (1310 , 0 , 0 , 0 );
268+ attachElements (uParachuteObject , localPlayer );
269+ setObjectScale (uParachuteObject , 0 );
270+ -- SCM: PLAY_OBJECT_ANIM parac para_open_o PARACHUTE 1000.0 0 1
271+ iParachuteAnim = getTickCount ();
272+
273+ setTimer (function ()
274+ strPlayerState = " GLIDING" ;
275+ strParachuteState = " OPENED" ;
276+ end , 1000 , 1 );
277+ end
278+ end
279+ end
280+
281+ -- do parachute opening animation
282+ if (iParachuteAnim ) then
283+ if ((getTickCount () - iParachuteAnim ) < 500 ) then
284+ setObjectScale (uParachuteObject , (getTickCount () - iParachuteAnim ) / 500 );
285+ else
286+ setObjectScale (uParachuteObject , 1 );
287+ iParachuteAnim = false ;
288+
289+ -- create parachute collision
290+ uParachuteCollision = createObject (3060 , 0 , 0 , 0 );
291+ setElementAlpha (uParachuteCollision , 0 );
292+ attachElements (uParachuteCollision , uParachuteObject );
293+ -- SCM: SET_OBJECT_DYNAMIC para_col TRUE
294+ -- SCM: SET_OBJECT_RECORDS_COLLISIONS para_col TRUE
295+ end
296+ end
297+
298+ -- player is gliding with parachute
299+ if (strPlayerState == " GLIDING" ) then
300+ setPedAnimation (localPlayer , " parachute" , " para_float" , - 2 , true , false , false , true );
301+ -- SCM: PLAY_OBJECT_ANIM parac para_float_o PARACHUTE 1.0 1 1
302+
303+ if (fControlForwards ~= 0 and strPlayerAction ~= " FORWARDS" ) then
304+ strPlayerAction = " FORWARDS" ;
305+ setPedAnimation (localPlayer , " parachute" , " para_float" , - 2 , true , false , false , true );
306+ -- SCM: PLAY_OBJECT_ANIM parac para_float_o PARACHUTE 1.0 1 1
307+ elseif (fControlBackwards ~= 0 and strPlayerAction ~= " BACKWARDS" ) then
308+ strPlayerAction = " BACKWARDS" ;
309+ setPedAnimation (localPlayer , " parachute" , " para_decel" , - 2 , true , false , false , true );
310+ -- SCM: PLAY_OBJECT_ANIM parac para_decel_o PARACHUTE 1.0 1 1
311+ elseif (fControlLeft ~= 0 and strPlayerAction ~= " LEFT" ) then
312+ strPlayerAction = " LEFT" ;
313+ setPedAnimation (localPlayer , " parachute" , " para_steerl" , - 2 , true , false , false , true );
314+ -- SCM: PLAY_OBJECT_ANIM parac para_steerL_o PARACHUTE 1.0 1 1
315+ elseif (fControlRight ~= 0 and strPlayerAction ~= " RIGHT" ) then
316+ strPlayerAction = " RIGHT" ;
317+ setPedAnimation (localPlayer , " parachute" , " para_steerr" , - 2 , true , false , false , true );
318+ -- SCM: PLAY_OBJECT_ANIM parac para_steerR_o PARACHUTE 1.0 1 1
319+ else
320+ if (strPlayerAction ~= " NONE" ) then
321+ setPedAnimation (localPlayer , " parachute" , " para_float" , - 2 , true , false , false , true );
322+ -- SCM: PLAY_OBJECT_ANIM parac para_float_o PARACHUTE 1.0 1 1
323+ end
324+
325+ strPlayerAction = " NONE" ;
326+ end
327+
328+ -- SCM:
329+ -- IF HAS_OBJECT_COLLIDED_WITH_ANYTHING para_col
330+ -- PLAY_OBJECT_ANIM parac para_rip_loop_o PARACHUTE 8.0 1 1
331+
332+ --[[ setElementVelocity(localPlayer, x, y, z);
333+ setElementRotation(localPlayer, 0, 0, yaw);
334+
335+ -- player landed
336+ if (fVZ > -0.1) then
337+ strPlayerState = "LANDED";
338+ strParachuteState = "CLOSING";
339+ end--]]
340+ end
341+
342+ -- player landed
343+ if (strPlayerState == " LANDED" ) then
344+
345+ end
346+ end
347+ end
348+ addEventHandler (" onClientRender" , root , handleParachuteLogic );
349+
350+ function cleanupParachute (bLandedGood )
351+ iParachuteAnim = false ;
352+ bHasParachute = false ;
353+ strParachuteState = " NONE" ;
354+ strPlayerState = " GROUND" ;
355+ strPlayerAction = " NONE" ;
356+
357+ if (isElement (uParachuteCollision )) then destroyElement (uParachuteCollision ) end ;
358+ if (isElement (uParachuteObject )) then destroyElement (uParachuteObject ) end ;
359+ if (isElement (uParachuteSound )) then destroyElement (uParachuteSound ) end ;
360+
361+ toggleControl (" next_weapon" , true );
362+ toggleControl (" previous_weapon" , true );
363+
364+ if (not bLandedGood ) then
365+ -- todo: remove weapon from player via server
366+ -- takeWeapon(client, 46);
367+ end
368+ end
0 commit comments