]> icculus.org git repositories - taylor/freespace2.git/blob - src/io/joy_ff.cpp
fix issue with looping audio streams
[taylor/freespace2.git] / src / io / joy_ff.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on the 
6  * source.
7  *
8 */ 
9
10
11 #include "pstypes.h"
12 #include "vecmat.h"
13 #include "osregistry.h"
14 #include "joy_ff.h"
15 #include "osapi.h"
16 #include "timer.h"
17 #include "joy.h"
18
19
20 static int Joy_ff_enabled = 0;
21 static int Joy_ff_acquired = 0;
22 static SDL_Haptic *haptic = NULL;
23 static int joy_ff_handling_scaler = 0;
24 static uint Joy_ff_directional_hit_effect_enabled = 1;
25 static int Joy_rumble = 0;
26 static int Joy_ff_afterburning = 0;
27
28 typedef struct {
29         SDL_HapticEffect eff;
30         int id;
31         int loaded;
32 } haptic_effect_t;
33
34 static haptic_effect_t pHitEffect1;
35 static haptic_effect_t pHitEffect2;
36 static haptic_effect_t pAfterburn1;
37 static haptic_effect_t pAfterburn2;
38 static haptic_effect_t pShootEffect;
39 static haptic_effect_t pSecShootEffect;
40 static haptic_effect_t pSpring;
41 static haptic_effect_t pDock;
42
43 static void joy_ff_create_effects();
44 //static int joy_ff_effect_playing(haptic_effect_t *eff);
45 static void joy_ff_start_effect(haptic_effect_t *eff, const char *name);
46
47
48 int joy_ff_init()
49 {
50         uint ff_enabled = 0;
51         SDL_Joystick *sdljoy = nullptr;
52
53         ff_enabled = os_config_read_uint("Controls", "EnableJoystickFF", 0);
54
55         if ( joystick_is_controller() ) {
56                 SDL_GameController *sdlcon = SDL_GameControllerFromInstanceID(joystick_get_id());
57                 sdljoy = SDL_GameControllerGetJoystick(sdlcon);
58         } else {
59                 sdljoy = SDL_JoystickFromInstanceID(joystick_get_id());
60         }
61
62         if ( !ff_enabled || !SDL_JoystickIsHaptic(sdljoy) ) {
63                 return 0;
64         }
65
66         if (SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) {
67                 mprintf(("  ERROR: Unable to initialize haptic subsystem\n"));
68                 return -1;
69         }
70
71         haptic = SDL_HapticOpenFromJoystick(sdljoy);
72
73         if (haptic == NULL) {
74                 mprintf(("  ERROR: Unable to open haptic joystick\n"));
75                 SDL_QuitSubSystem(SDL_INIT_HAPTIC);
76                 return -1;
77         }
78
79         if ( SDL_HapticRumbleSupported(haptic) ) {
80                 SDL_HapticRumbleInit(haptic);
81                 Joy_rumble = 1;
82         }
83
84         mprintf(("  Rumble       : %s\n", Joy_rumble ? "Yes" : "No"));
85         mprintf(("  Haptic Axes  : %d\n", SDL_HapticNumAxes(haptic)));
86         mprintf(("  Max effects     : %d\n", SDL_HapticNumEffects(haptic)));
87         mprintf(("  Running effects : %d\n", SDL_HapticNumEffectsPlaying(haptic)));
88
89         joy_ff_create_effects();
90
91         Joy_ff_enabled = 1;
92         Joy_ff_acquired = 1;
93
94         Joy_ff_directional_hit_effect_enabled = os_config_read_uint("Controls", "EnableHitEffect", 1);
95
96         return 0;
97 }
98
99 void joy_ff_shutdown()
100 {
101         if ( !Joy_ff_enabled ) {
102                 return;
103         }
104
105         SDL_HapticStopAll(haptic);
106
107         Joy_rumble = 0;
108
109         SDL_HapticClose(haptic);
110         haptic = NULL;
111
112         SDL_QuitSubSystem(SDL_INIT_HAPTIC);
113
114         Joy_ff_acquired = 0;
115         Joy_ff_enabled = 0;
116 }
117
118 static void joy_ff_create_effects()
119 {
120         // clear all SDL errors
121         SDL_ClearError();
122
123         unsigned int supported = 0;
124
125         supported = SDL_HapticQuery(haptic);
126
127         if ( !(supported & SDL_HAPTIC_CONSTANT) ) {
128                 mprintf(("  Constant Force  : not supported\n"));
129         }
130
131         if ( !(supported & SDL_HAPTIC_SINE) ) {
132                 mprintf(("  Sine Wave       : not supported\n"));
133         }
134
135         if ( !(supported & SDL_HAPTIC_SAWTOOTHDOWN) ) {
136                 mprintf(("  Sawtooth Down   : not supported\n"));
137         }
138
139         if ( !(supported & SDL_HAPTIC_SPRING) ) {
140                 mprintf(("  Spring          : not supported\n"));
141         }
142 /*
143         if ( !(supported & SDL_HAPTIC_SQUARE) ) {
144                 mprintf(("  Square          : not supported\n"));
145         }
146 */
147         if ( !(supported & SDL_HAPTIC_TRIANGLE) ) {
148                 mprintf(("  Triangle        : not supported\n"));
149         }
150
151
152         if (supported & SDL_HAPTIC_SPRING) {
153                 // pSpring
154                 memset(&pSpring, 0, sizeof(haptic_effect_t));
155
156                 pSpring.eff.type = SDL_HAPTIC_SPRING;
157                 pSpring.eff.condition.type = SDL_HAPTIC_SPRING;
158                 pSpring.eff.condition.length = SDL_HAPTIC_INFINITY;
159
160                 for (int i = 0; i < 3; i++) {
161                         pSpring.eff.condition.right_sat[i] = 0x7FFF;
162                         pSpring.eff.condition.left_sat[i] = 0x7FFF;
163                         pSpring.eff.condition.right_coeff[i] = 0x147;
164                         pSpring.eff.condition.left_coeff[i] = 0x147;
165                 }
166
167                 pSpring.id = SDL_HapticNewEffect(haptic, &pSpring.eff);
168
169                 if (pSpring.id < 0) {
170                         mprintf(("    Spring effect failed to load:\n      %s\n", SDL_GetError()));
171                 } else {
172                         pSpring.loaded = 1;
173                 }
174         }
175
176         if (supported & SDL_HAPTIC_SAWTOOTHDOWN) {
177                 // pShootEffect
178                 memset(&pShootEffect, 0, sizeof(haptic_effect_t));
179
180                 pShootEffect.eff.type = SDL_HAPTIC_SAWTOOTHDOWN;
181                 pShootEffect.eff.periodic.type = SDL_HAPTIC_SAWTOOTHDOWN;
182                 pShootEffect.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
183                 pShootEffect.eff.periodic.direction.dir[0] = 0;
184                 pShootEffect.eff.periodic.length = 160;
185                 pShootEffect.eff.periodic.period = 20;
186                 pShootEffect.eff.periodic.magnitude = 0x7FFF;
187                 pShootEffect.eff.periodic.fade_length = 120;
188
189                 pShootEffect.id = SDL_HapticNewEffect(haptic, &pShootEffect.eff);
190
191                 if (pShootEffect.id < 0) {
192                         mprintf(("    Fire primary effect failed to load:\n      %s\n", SDL_GetError()));
193                 } else {
194                         pShootEffect.loaded = 1;
195                 }
196         }
197
198         if (supported & SDL_HAPTIC_CONSTANT) {
199                 // pSecShootEffect
200                 memset(&pSecShootEffect, 0, sizeof(haptic_effect_t));
201
202                 pSecShootEffect.eff.type = SDL_HAPTIC_CONSTANT;
203                 pSecShootEffect.eff.constant.type = SDL_HAPTIC_CONSTANT;
204                 pSecShootEffect.eff.constant.direction.type = SDL_HAPTIC_POLAR;
205                 pSecShootEffect.eff.constant.direction.dir[0] = 0;
206                 pSecShootEffect.eff.constant.length = 200;
207                 pSecShootEffect.eff.constant.level = 0x7FFF;
208                 pSecShootEffect.eff.constant.attack_length = 50;
209                 pSecShootEffect.eff.constant.attack_level = 0x7FFF;
210                 pSecShootEffect.eff.constant.fade_length = 100;
211                 pSecShootEffect.eff.constant.fade_level = 1;
212
213                 pSecShootEffect.id = SDL_HapticNewEffect(haptic, &pSecShootEffect.eff);
214
215                 if (pSecShootEffect.id < 0) {
216                         mprintf(("    Fire secondary effect failed to load:\n      %s\n", SDL_GetError()));
217                 } else {
218                         pSecShootEffect.loaded = 1;
219                 }
220         }
221
222         if (supported & SDL_HAPTIC_SINE) {
223                 // pAfterburn1
224                 memset(&pAfterburn1, 0, sizeof(haptic_effect_t));
225
226                 pAfterburn1.eff.type = SDL_HAPTIC_SINE;
227                 pAfterburn1.eff.periodic.type = SDL_HAPTIC_SINE;
228                 pAfterburn1.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
229                 pAfterburn1.eff.periodic.direction.dir[0] = 0;
230                 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
231                 pAfterburn1.eff.periodic.period = 20;
232                 pAfterburn1.eff.periodic.magnitude = 0x3332;
233
234                 pAfterburn1.id = SDL_HapticNewEffect(haptic, &pAfterburn1.eff);
235
236                 if (pAfterburn1.id < 0) {
237                         mprintf(("    Afterburn effect 1 failed to load:\n      %s\n", SDL_GetError()));
238                 } else {
239                         pAfterburn1.loaded = 1;
240                 }
241
242                 // pAfterburn2
243                 memset(&pAfterburn2, 0, sizeof(haptic_effect_t));
244
245                 pAfterburn2.eff.type = SDL_HAPTIC_SINE;
246                 pAfterburn2.eff.periodic.type = SDL_HAPTIC_SINE;
247                 pAfterburn2.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
248                 pAfterburn2.eff.periodic.direction.dir[0] = 9000;
249                 pAfterburn2.eff.periodic.length = 125;
250                 pAfterburn2.eff.periodic.period = 100;
251                 pAfterburn2.eff.periodic.magnitude = 0x1999;
252
253                 pAfterburn2.id = SDL_HapticNewEffect(haptic, &pAfterburn2.eff);
254
255                 if (pAfterburn2.id < 0) {
256                         mprintf(("    Afterburn effect 2 failed to load:\n      %s\n", SDL_GetError()));
257                 } else {
258                         pAfterburn2.loaded = 1;
259                 }
260         }
261
262         if (supported & SDL_HAPTIC_CONSTANT) {
263                 // pHitEffect1
264                 memset(&pHitEffect1, 0, sizeof(haptic_effect_t));
265
266                 pHitEffect1.eff.type = SDL_HAPTIC_CONSTANT;
267                 pHitEffect1.eff.constant.type = SDL_HAPTIC_CONSTANT;
268                 pHitEffect1.eff.constant.direction.type = SDL_HAPTIC_POLAR;
269                 pHitEffect1.eff.constant.direction.dir[0] = 0;
270                 pHitEffect1.eff.constant.length = 300;
271                 pHitEffect1.eff.constant.level = 0x7FFF;
272                 pHitEffect1.eff.constant.attack_length = 0;
273                 pHitEffect1.eff.constant.attack_level = 0x7FFF;
274                 pHitEffect1.eff.constant.fade_length = 120;
275                 pHitEffect1.eff.constant.fade_level = 1;
276
277                 pHitEffect1.id = SDL_HapticNewEffect(haptic, &pHitEffect1.eff);
278
279                 if (pHitEffect1.id < 0) {
280                         mprintf(("    Hit effect 1 failed to load:\n      %s\n", SDL_GetError()));
281                 } else {
282                         pHitEffect1.loaded = 1;
283                 }
284         }
285
286         if (supported & SDL_HAPTIC_SINE) {
287                 // pHitEffect2
288                 memset(&pHitEffect2, 0, sizeof(haptic_effect_t));
289
290                 pHitEffect2.eff.type = SDL_HAPTIC_SINE;
291                 pHitEffect2.eff.periodic.type = SDL_HAPTIC_SINE;
292                 pHitEffect2.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
293                 pHitEffect2.eff.periodic.direction.dir[0] = 9000;
294                 pHitEffect2.eff.periodic.length = 300;
295                 pHitEffect2.eff.periodic.period = 100;
296                 pHitEffect2.eff.periodic.magnitude = 0x7FFF;
297                 pHitEffect2.eff.periodic.attack_length = 100;
298                 pHitEffect2.eff.periodic.fade_length = 100;
299
300                 pHitEffect2.id = SDL_HapticNewEffect(haptic, &pHitEffect2.eff);
301
302                 if (pHitEffect2.id < 0) {
303                         mprintf(("    Hit effect 2 failed to load:\n      %s\n", SDL_GetError()));
304                 } else {
305                         pHitEffect2.loaded = 1;
306                 }
307         }
308
309 //      if (supported & SDL_HAPTIC_SQUARE) {
310         if (supported & SDL_HAPTIC_TRIANGLE) {
311                 // pDock
312                 memset(&pDock, 0, sizeof(haptic_effect_t));
313
314         //      pDock.eff.type = SDL_HAPTIC_SQUARE;
315         //      pDock.eff.periodic.type = SDL_HAPTIC_SQUARE;
316                 pDock.eff.type = SDL_HAPTIC_TRIANGLE;
317                 pDock.eff.periodic.type = SDL_HAPTIC_TRIANGLE;
318                 pDock.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
319                 pDock.eff.periodic.direction.dir[0] = 9000;
320                 pDock.eff.periodic.length = 125;
321                 pDock.eff.periodic.period = 100;
322                 pDock.eff.periodic.magnitude = 0x3332;
323
324                 pDock.id = SDL_HapticNewEffect(haptic, &pDock.eff);
325
326                 if (pDock.id < 0) {
327                         mprintf(("    Dock effect failed to load:\n      %s\n", SDL_GetError()));
328                 } else {
329                         pDock.loaded = 1;
330                 }
331         }
332 }
333
334 static bool joy_ff_can_play()
335 {
336         return (Joy_ff_enabled && Joy_ff_acquired);
337 }
338
339 static void joy_ff_update_effect(haptic_effect_t *eff, const char *name)
340 {
341         if ( !eff->loaded ) {
342                 return;
343         }
344
345         if ( SDL_HapticUpdateEffect(haptic, eff->id, &eff->eff) < 0 ) {
346                 mprintf(("HapticERROR:  Unable to update %s:\n  %s\n", name, SDL_GetError()));
347         }
348 }
349
350 static void joy_ff_start_effect(haptic_effect_t *eff, const char *name)
351 {
352         if ( !eff->loaded ) {
353                 return;
354         }
355
356 //      nprintf(("Joystick", "FF: Starting effect %s\n", name));
357
358         if ( SDL_HapticRunEffect(haptic, eff->id, 1) ) {
359                 mprintf(("HapticERROR:  Unable to run %s:\n  %s\n", name, SDL_GetError()));
360         }
361 }
362
363 void joy_ff_stop_effects()
364 {
365         if ( !Joy_ff_enabled ) {
366                 return;
367         }
368
369         SDL_HapticStopAll(haptic);
370 }
371
372 void joy_ff_mission_init(vector v)
373 {
374         v.xyz.z = 0.0f;
375
376         joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
377
378         joy_ff_adjust_handling(0);
379         joy_ff_start_effect(&pSpring, "Spring");
380
381         Joy_ff_afterburning = 0;
382
383         // reset afterburn effects to default values
384
385         pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
386         pAfterburn1.eff.periodic.period = 20;
387         pAfterburn1.eff.periodic.magnitude = 0x3332;
388         pAfterburn1.eff.periodic.attack_length = 0;
389
390         joy_ff_update_effect(&pAfterburn1, "pAfterburn1 (init)");
391
392         pAfterburn2.eff.periodic.length = SDL_HAPTIC_INFINITY;
393         pAfterburn2.eff.periodic.period = 100;
394         pAfterburn2.eff.periodic.magnitude = 0x1999;
395         pAfterburn2.eff.periodic.attack_length = 0;
396
397         joy_ff_update_effect(&pAfterburn2, "pAfterburn2 (init)");
398
399         // reset primary shoot effect to default values
400
401         pShootEffect.eff.periodic.direction.dir[0] = 0;
402         pShootEffect.eff.periodic.length = 160;
403         pShootEffect.eff.periodic.fade_length = 120;
404
405         joy_ff_update_effect(&pShootEffect, "pShootEffect (init)");
406 }
407
408 void joy_reacquire_ff()
409 {
410         if ( !Joy_ff_enabled ) {
411                 return;
412         }
413
414         if (Joy_ff_acquired) {
415                 return;
416         }
417
418         joy_ff_start_effect(&pSpring, "Spring");
419
420         Joy_ff_acquired = 1;
421 }
422
423 void joy_unacquire_ff()
424 {
425         if ( !Joy_ff_enabled ) {
426                 return;
427         }
428
429         if ( !Joy_ff_acquired ) {
430                 return;
431         }
432
433         joy_ff_stop_effects();
434
435         Joy_ff_acquired = 0;
436 }
437
438 void joy_ff_play_vector_effect(vector *v, float scaler)
439 {
440         vector vf;
441         float x, y;
442
443         if ( !joy_ff_can_play() ) {
444                 return;
445         }
446
447 //      nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->xyz.x, v->xyz.y, v->xyz.z, scaler));
448         vm_vec_copy_scale(&vf, v, scaler);
449         x = vf.xyz.x;
450         vf.xyz.x = 0.0f;
451
452         if (vf.xyz.y + vf.xyz.z < 0.0f) {
453                 y = -vm_vec_mag(&vf);
454         } else {
455                 y = vm_vec_mag(&vf);
456         }
457
458         joy_ff_play_dir_effect(-x, -y);
459 }
460
461 void joy_ff_play_dir_effect(float x, float y)
462 {
463         static int hit_timeout = 1;
464         int idegs;
465         float degs, mag;
466         Sint16 imag;
467
468         if ( !joy_ff_can_play() ) {
469                 return;
470         }
471
472         // allow for at least one of the effects to work
473         if ( !pHitEffect1.loaded && !pHitEffect2.loaded ) {
474                 return;
475         }
476
477         if ( !timestamp_elapsed(hit_timeout) ) {
478                 nprintf(("Joystick", "FF: HitEffect already playing.  Skipping\n"));
479                 return;
480         }
481
482         hit_timeout = timestamp(pHitEffect1.eff.condition.length);
483
484         if (Joy_ff_directional_hit_effect_enabled) {
485                 if (x > 8000.0f) {
486                         x = 8000.0f;
487                 } else if (x < -8000.0f) {
488                         x = -8000.0f;
489                 }
490
491                 if (y > 8000.0f) {
492                         y = 8000.0f;
493                 } else if (y < -8000.0f) {
494                         y = -8000.0f;
495                 }
496
497                 mag = fl_sqrt(x * x + y * y) / 10000.0f;
498                 CAP(mag, 0.0f, 1.0f);
499
500                 imag = (Sint16)(32767.0f * mag);
501
502                 degs = (float)atan2(x, y);
503                 idegs = (int) (degs * 18000.0f / PI) + 90;
504
505                 while (idegs < 0) {
506                         idegs += 36000;
507                 }
508
509                 while (idegs >= 36000) {
510                         idegs -= 36000;
511                 }
512
513                 if (pHitEffect1.loaded) {
514                         pHitEffect1.eff.constant.direction.dir[0] = idegs;
515                         pHitEffect1.eff.constant.level = imag;
516
517                         joy_ff_update_effect(&pHitEffect1, "pHitEffect1");
518                 }
519
520                 idegs += 9000;
521                 if (idegs >= 36000)
522                         idegs -= 36000;
523
524                 if (pHitEffect2.loaded) {
525                         pHitEffect2.eff.periodic.direction.dir[0] = idegs;
526                         pHitEffect2.eff.periodic.magnitude = imag;
527
528                         joy_ff_update_effect(&pHitEffect2, "pHitEffect2");
529                 }
530         }
531
532         joy_ff_start_effect(&pHitEffect1, "HitEffect1");
533         joy_ff_start_effect(&pHitEffect2, "HitEffect2");
534 }
535
536 void joy_ff_play_primary_shoot(int gain)
537 {
538         if ( !joy_ff_can_play() ) {
539                 return;
540         }
541
542         if ( !pShootEffect.loaded && !Joy_rumble ) {
543                 return;
544         }
545
546         CAP(gain, 1, 10000);
547
548         if (pShootEffect.loaded) {
549                 SDL_HapticStopEffect(haptic, pShootEffect.id);
550
551                 static int primary_ff_level = 10000;
552
553                 if (gain != primary_ff_level) {
554                         pShootEffect.eff.periodic.magnitude = (Sint16)(32767.0f * (gain / 10000.0f));
555
556                         joy_ff_update_effect(&pShootEffect, "pShootEffect");
557
558                         primary_ff_level = gain;
559                 }
560
561                 joy_ff_start_effect(&pShootEffect, "ShootEffect");
562         } else if (Joy_rumble) {
563                 static int rumble_timeout = 1;
564
565                 if ( timestamp_elapsed(rumble_timeout) ) {
566                         SDL_HapticRumblePlay(haptic, gain / 10000.0f, 100);
567
568                         rumble_timeout = timestamp(100);
569                 }
570         }
571 }
572
573 void joy_ff_play_secondary_shoot(int gain)
574 {
575         if ( !joy_ff_can_play() ) {
576                 return;
577         }
578
579         if ( !pSecShootEffect.loaded && !Joy_rumble ) {
580                 return;
581         }
582
583         gain = gain * 100 + 2500;
584
585         CAP(gain, 1, 10000);
586
587         if (pSecShootEffect.loaded) {
588                 SDL_HapticStopEffect(haptic, pSecShootEffect.id);
589
590                 static int secondary_ff_level = 10000;
591
592                 if (gain != secondary_ff_level) {
593                         pSecShootEffect.eff.constant.level = (Sint16)(32767.0f * (gain / 10000.0f));
594                         pSecShootEffect.eff.constant.length = (150000 + gain * 25) / 1000;
595
596                         joy_ff_update_effect(&pSecShootEffect, "pSecShootEffect");
597
598                         secondary_ff_level = gain;
599                         nprintf(("Joystick", "FF: Secondary force = 0x%04x\n", pSecShootEffect.eff.constant.level));
600                 }
601
602                 joy_ff_start_effect(&pSecShootEffect, "SecShootEffect");
603         } else if (Joy_rumble) {
604                 static int rumble_timeout = 1;
605
606                 if ( timestamp_elapsed(rumble_timeout) ) {
607                         int duration = (150000 + gain * 25) / 1000;
608
609                         SDL_HapticRumblePlay(haptic, gain / 10000.0f, duration);
610
611                         rumble_timeout = timestamp(duration);
612                 }
613         }
614 }
615
616 void joy_ff_adjust_handling(int speed)
617 {
618         int v;
619         short coeff = 0;
620
621         if ( !joy_ff_can_play() ) {
622                 return;
623         }
624
625         if ( !pSpring.loaded ) {
626                 return;
627         }
628
629         static int last_speed = -1000;
630
631         if (speed == last_speed) {
632                 return;
633         }
634
635         last_speed = speed;
636
637         v = speed * joy_ff_handling_scaler * 2 / 3;
638 //      v += joy_ff_handling_scaler * joy_ff_handling_scaler * 6 / 7 + 250;
639         v += joy_ff_handling_scaler * 45 - 500;
640
641         CAP(v, 0, 10000);
642
643         coeff = (short)(32767.0f * (v / 10000.0f));
644
645         for (int i = 0; i < SDL_HapticNumAxes(haptic); i++) {
646                 pSpring.eff.condition.right_coeff[i] = coeff;
647                 pSpring.eff.condition.left_coeff[i] = coeff;
648         }
649
650 //      nprintf(("Joystick", "FF: New handling force = 0x%04x\n", coeff));
651
652         joy_ff_update_effect(&pSpring, "pSpring");
653 }
654 /*
655 static int joy_ff_effect_playing(haptic_effect_t *eff)
656 {
657         if ( !eff->loaded ) {
658                 return 0;
659         } else {
660                 return (SDL_HapticGetEffectStatus(haptic, eff->id) > 0);
661         }
662 }
663 */
664 void joy_ff_docked()
665 {
666         if ( !joy_ff_can_play() ) {
667                 return;
668         }
669
670         if ( !pDock.loaded ) {
671                 return;
672         }
673
674         SDL_HapticStopEffect(haptic, pDock.id);
675
676         pDock.eff.periodic.magnitude = 0x3332;
677
678         joy_ff_update_effect(&pDock, "pDock");
679
680         joy_ff_start_effect(&pDock, "Dock");
681 }
682
683 void joy_ff_play_reload_effect()
684 {
685         if ( !joy_ff_can_play() ) {
686                 return;
687         }
688
689         if ( !pDock.loaded ) {
690                 return;
691         }
692
693         SDL_HapticStopEffect(haptic, pDock.id);
694
695         pDock.eff.periodic.magnitude = 0x1999;
696
697         joy_ff_update_effect(&pDock, "pDock (reload)");
698
699         joy_ff_start_effect(&pDock, "Dock (Reload)");
700 }
701
702 void joy_ff_afterburn_on()
703 {
704         if ( !joy_ff_can_play() ) {
705                 return;
706         }
707
708         if (Joy_ff_afterburning) {
709                 return;
710         }
711
712         if ( !(pAfterburn1.loaded && pAfterburn2.loaded) ) {
713                 return;
714         }
715
716         SDL_HapticStopEffect(haptic, pAfterburn1.id);
717
718         pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
719         pAfterburn1.eff.periodic.magnitude = 0x3332;
720
721         joy_ff_update_effect(&pAfterburn1, "pAfterburn1");
722
723         SDL_HapticStopEffect(haptic, pAfterburn2.id);
724
725         pAfterburn2.eff.periodic.length = SDL_HAPTIC_INFINITY;
726         pAfterburn2.eff.periodic.magnitude = 0x1999;
727
728         joy_ff_update_effect(&pAfterburn2, "pAfterburn2");
729
730         joy_ff_start_effect(&pAfterburn1, "Afterburn1");
731         joy_ff_start_effect(&pAfterburn2, "Afterburn2");
732
733 //      nprintf(("Joystick", "FF: Afterburn started\n"));
734
735         Joy_ff_afterburning = 1;
736 }
737
738 void joy_ff_afterburn_off()
739 {
740         if ( !joy_ff_can_play() ) {
741                 return;
742         }
743
744         if ( !Joy_ff_afterburning ) {
745                 return;
746         }
747
748         if (pAfterburn1.loaded) {
749                 SDL_HapticStopEffect(haptic, pAfterburn1.id);
750         }
751
752         if (pAfterburn2.loaded) {
753                 SDL_HapticStopEffect(haptic, pAfterburn2.id);
754         }
755
756         Joy_ff_afterburning = 0;
757
758 //      nprintf(("Joystick", "FF: Afterburn stopped\n"));
759 }
760
761 void joy_ff_explode()
762 {
763         if ( !joy_ff_can_play() ) {
764                 return;
765         }
766
767         if (pAfterburn1.loaded) {
768                 SDL_HapticStopEffect(haptic, pAfterburn1.id);
769         }
770
771         if (pAfterburn2.loaded) {
772                 SDL_HapticStopEffect(haptic, pAfterburn2.id);
773         }
774
775         if (pShootEffect.loaded) {
776                 SDL_HapticStopEffect(haptic, pShootEffect.id);
777
778                 pShootEffect.eff.periodic.direction.dir[0] = 9000;
779                 pShootEffect.eff.periodic.length = 500;
780                 pShootEffect.eff.periodic.magnitude = 0x7FFF;
781                 pShootEffect.eff.periodic.fade_length = 500;
782
783                 joy_ff_update_effect(&pShootEffect, "pShootEffect (explode)");
784
785                 joy_ff_start_effect(&pShootEffect, "ShootEffect (Explode)");
786         } else if (Joy_rumble) {
787                 static int rumble_timeout = 1;
788
789                 if ( timestamp_elapsed(rumble_timeout) ) {
790                         SDL_HapticRumblePlay(haptic, 1.0f, 500);
791
792                         rumble_timeout = timestamp(500);
793                 }
794         }
795
796         Joy_ff_afterburning = 0;
797 }
798
799 void joy_ff_fly_by(int mag)
800 {
801         int gain;
802
803         if ( !joy_ff_can_play() ) {
804                 return;
805         }
806
807         if (Joy_ff_afterburning) {
808                 return;
809         }
810
811         if ( !(pAfterburn1.loaded && pAfterburn2.loaded) ) {
812                 return;
813         }
814
815         gain = mag * 120 + 4000;
816
817         CAP(gain, 1, 10000);
818
819         SDL_HapticStopEffect(haptic, pAfterburn1.id);
820
821         pAfterburn1.eff.periodic.length = (6000 * mag + 400000) / 1000;
822         pAfterburn1.eff.periodic.magnitude = (Sint16)(26212.0f * (gain / 10000.0f));
823
824         joy_ff_update_effect(&pAfterburn1, "pAfterburn1 (flyby)");
825
826         SDL_HapticStopEffect(haptic, pAfterburn2.id);
827
828         pAfterburn2.eff.periodic.length = (6000 * mag + 400000) / 1000;
829         pAfterburn2.eff.periodic.magnitude = (Sint16)(13106.0f * (gain / 10000.0f));
830
831         joy_ff_update_effect(&pAfterburn2, "pAfterburn2 (flyby)");
832
833         joy_ff_start_effect(&pAfterburn1, "Afterburn1 (Fly by)");
834         joy_ff_start_effect(&pAfterburn2, "Afterburn2 (Fly by)");
835 }
836
837 void joy_ff_deathroll()
838 {
839         if ( !joy_ff_can_play() ) {
840                 return;
841         }
842
843         if (pAfterburn1.loaded && pAfterburn2.loaded) {
844                 SDL_HapticStopEffect(haptic, pAfterburn1.id);
845
846                 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
847                 pAfterburn1.eff.periodic.period = 200;
848                 pAfterburn1.eff.periodic.magnitude = 0x7FFF;
849                 pAfterburn1.eff.periodic.attack_length = 200;
850
851                 joy_ff_update_effect(&pAfterburn1, "pAfterburn1 (deathroll)");
852
853                 SDL_HapticStopEffect(haptic, pAfterburn2.id);
854
855                 pAfterburn2.eff.periodic.length = SDL_HAPTIC_INFINITY;
856                 pAfterburn2.eff.periodic.period = 200;
857                 pAfterburn2.eff.periodic.magnitude = 0x7FFF;
858                 pAfterburn2.eff.periodic.attack_length = 200;
859
860                 joy_ff_update_effect(&pAfterburn2, "pAfterburn2 (deathroll)");
861
862                 joy_ff_start_effect(&pAfterburn1, "Afterburn1 (Death Roll)");
863                 joy_ff_start_effect(&pAfterburn2, "Afterburn2 (Death Roll)");
864
865                 Joy_ff_afterburning = 1;
866         }
867 }