2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
13 #include "osregistry.h"
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;
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;
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);
51 SDL_Joystick *sdljoy = nullptr;
53 ff_enabled = os_config_read_uint("Controls", "EnableJoystickFF", 0);
55 if ( joystick_is_controller() ) {
56 SDL_GameController *sdlcon = SDL_GameControllerFromInstanceID(joystick_get_id());
57 sdljoy = SDL_GameControllerGetJoystick(sdlcon);
59 sdljoy = SDL_JoystickFromInstanceID(joystick_get_id());
62 if ( !ff_enabled || !SDL_JoystickIsHaptic(sdljoy) ) {
66 if (SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) {
67 mprintf((" ERROR: Unable to initialize haptic subsystem\n"));
71 haptic = SDL_HapticOpenFromJoystick(sdljoy);
74 mprintf((" ERROR: Unable to open haptic joystick\n"));
75 SDL_QuitSubSystem(SDL_INIT_HAPTIC);
79 if ( SDL_HapticRumbleSupported(haptic) ) {
80 SDL_HapticRumbleInit(haptic);
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)));
89 joy_ff_create_effects();
94 Joy_ff_directional_hit_effect_enabled = os_config_read_uint("Controls", "EnableHitEffect", 1);
99 void joy_ff_shutdown()
101 if ( !Joy_ff_enabled ) {
105 SDL_HapticStopAll(haptic);
109 SDL_HapticClose(haptic);
112 SDL_QuitSubSystem(SDL_INIT_HAPTIC);
118 static void joy_ff_create_effects()
120 // clear all SDL errors
123 unsigned int supported = 0;
125 supported = SDL_HapticQuery(haptic);
127 if ( !(supported & SDL_HAPTIC_CONSTANT) ) {
128 mprintf((" Constant Force : not supported\n"));
131 if ( !(supported & SDL_HAPTIC_SINE) ) {
132 mprintf((" Sine Wave : not supported\n"));
135 if ( !(supported & SDL_HAPTIC_SAWTOOTHDOWN) ) {
136 mprintf((" Sawtooth Down : not supported\n"));
139 if ( !(supported & SDL_HAPTIC_SPRING) ) {
140 mprintf((" Spring : not supported\n"));
143 if ( !(supported & SDL_HAPTIC_SQUARE) ) {
144 mprintf((" Square : not supported\n"));
147 if ( !(supported & SDL_HAPTIC_TRIANGLE) ) {
148 mprintf((" Triangle : not supported\n"));
152 if (supported & SDL_HAPTIC_SPRING) {
154 memset(&pSpring, 0, sizeof(haptic_effect_t));
156 pSpring.eff.type = SDL_HAPTIC_SPRING;
157 pSpring.eff.condition.type = SDL_HAPTIC_SPRING;
158 pSpring.eff.condition.length = SDL_HAPTIC_INFINITY;
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;
167 pSpring.id = SDL_HapticNewEffect(haptic, &pSpring.eff);
169 if (pSpring.id < 0) {
170 mprintf((" Spring effect failed to load:\n %s\n", SDL_GetError()));
176 if (supported & SDL_HAPTIC_SAWTOOTHDOWN) {
178 memset(&pShootEffect, 0, sizeof(haptic_effect_t));
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;
189 pShootEffect.id = SDL_HapticNewEffect(haptic, &pShootEffect.eff);
191 if (pShootEffect.id < 0) {
192 mprintf((" Fire primary effect failed to load:\n %s\n", SDL_GetError()));
194 pShootEffect.loaded = 1;
198 if (supported & SDL_HAPTIC_CONSTANT) {
200 memset(&pSecShootEffect, 0, sizeof(haptic_effect_t));
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;
213 pSecShootEffect.id = SDL_HapticNewEffect(haptic, &pSecShootEffect.eff);
215 if (pSecShootEffect.id < 0) {
216 mprintf((" Fire secondary effect failed to load:\n %s\n", SDL_GetError()));
218 pSecShootEffect.loaded = 1;
222 if (supported & SDL_HAPTIC_SINE) {
224 memset(&pAfterburn1, 0, sizeof(haptic_effect_t));
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;
234 pAfterburn1.id = SDL_HapticNewEffect(haptic, &pAfterburn1.eff);
236 if (pAfterburn1.id < 0) {
237 mprintf((" Afterburn effect 1 failed to load:\n %s\n", SDL_GetError()));
239 pAfterburn1.loaded = 1;
243 memset(&pAfterburn2, 0, sizeof(haptic_effect_t));
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;
253 pAfterburn2.id = SDL_HapticNewEffect(haptic, &pAfterburn2.eff);
255 if (pAfterburn2.id < 0) {
256 mprintf((" Afterburn effect 2 failed to load:\n %s\n", SDL_GetError()));
258 pAfterburn2.loaded = 1;
262 if (supported & SDL_HAPTIC_CONSTANT) {
264 memset(&pHitEffect1, 0, sizeof(haptic_effect_t));
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;
277 pHitEffect1.id = SDL_HapticNewEffect(haptic, &pHitEffect1.eff);
279 if (pHitEffect1.id < 0) {
280 mprintf((" Hit effect 1 failed to load:\n %s\n", SDL_GetError()));
282 pHitEffect1.loaded = 1;
286 if (supported & SDL_HAPTIC_SINE) {
288 memset(&pHitEffect2, 0, sizeof(haptic_effect_t));
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;
300 pHitEffect2.id = SDL_HapticNewEffect(haptic, &pHitEffect2.eff);
302 if (pHitEffect2.id < 0) {
303 mprintf((" Hit effect 2 failed to load:\n %s\n", SDL_GetError()));
305 pHitEffect2.loaded = 1;
309 // if (supported & SDL_HAPTIC_SQUARE) {
310 if (supported & SDL_HAPTIC_TRIANGLE) {
312 memset(&pDock, 0, sizeof(haptic_effect_t));
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;
324 pDock.id = SDL_HapticNewEffect(haptic, &pDock.eff);
327 mprintf((" Dock effect failed to load:\n %s\n", SDL_GetError()));
334 static bool joy_ff_can_play()
336 return (Joy_ff_enabled && Joy_ff_acquired);
339 static void joy_ff_update_effect(haptic_effect_t *eff, const char *name)
341 if ( !eff->loaded ) {
345 if ( SDL_HapticUpdateEffect(haptic, eff->id, &eff->eff) < 0 ) {
346 mprintf(("HapticERROR: Unable to update %s:\n %s\n", name, SDL_GetError()));
350 static void joy_ff_start_effect(haptic_effect_t *eff, const char *name)
352 if ( !eff->loaded ) {
356 // nprintf(("Joystick", "FF: Starting effect %s\n", name));
358 if ( SDL_HapticRunEffect(haptic, eff->id, 1) ) {
359 mprintf(("HapticERROR: Unable to run %s:\n %s\n", name, SDL_GetError()));
363 void joy_ff_stop_effects()
365 if ( !Joy_ff_enabled ) {
369 SDL_HapticStopAll(haptic);
372 void joy_ff_mission_init(vector v)
376 joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
378 joy_ff_adjust_handling(0);
379 joy_ff_start_effect(&pSpring, "Spring");
381 Joy_ff_afterburning = 0;
383 // reset afterburn effects to default values
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;
390 joy_ff_update_effect(&pAfterburn1, "pAfterburn1 (init)");
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;
397 joy_ff_update_effect(&pAfterburn2, "pAfterburn2 (init)");
399 // reset primary shoot effect to default values
401 pShootEffect.eff.periodic.direction.dir[0] = 0;
402 pShootEffect.eff.periodic.length = 160;
403 pShootEffect.eff.periodic.fade_length = 120;
405 joy_ff_update_effect(&pShootEffect, "pShootEffect (init)");
408 void joy_reacquire_ff()
410 if ( !Joy_ff_enabled ) {
414 if (Joy_ff_acquired) {
418 joy_ff_start_effect(&pSpring, "Spring");
423 void joy_unacquire_ff()
425 if ( !Joy_ff_enabled ) {
429 if ( !Joy_ff_acquired ) {
433 joy_ff_stop_effects();
438 void joy_ff_play_vector_effect(vector *v, float scaler)
443 if ( !joy_ff_can_play() ) {
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);
452 if (vf.xyz.y + vf.xyz.z < 0.0f) {
453 y = -vm_vec_mag(&vf);
458 joy_ff_play_dir_effect(-x, -y);
461 void joy_ff_play_dir_effect(float x, float y)
463 static int hit_timeout = 1;
468 if ( !joy_ff_can_play() ) {
472 // allow for at least one of the effects to work
473 if ( !pHitEffect1.loaded && !pHitEffect2.loaded ) {
477 if ( !timestamp_elapsed(hit_timeout) ) {
478 nprintf(("Joystick", "FF: HitEffect already playing. Skipping\n"));
482 hit_timeout = timestamp(pHitEffect1.eff.condition.length);
484 if (Joy_ff_directional_hit_effect_enabled) {
487 } else if (x < -8000.0f) {
493 } else if (y < -8000.0f) {
497 mag = fl_sqrt(x * x + y * y) / 10000.0f;
498 CAP(mag, 0.0f, 1.0f);
500 imag = (Sint16)(32767.0f * mag);
502 degs = (float)atan2(x, y);
503 idegs = (int) (degs * 18000.0f / PI) + 90;
509 while (idegs >= 36000) {
513 if (pHitEffect1.loaded) {
514 pHitEffect1.eff.constant.direction.dir[0] = idegs;
515 pHitEffect1.eff.constant.level = imag;
517 joy_ff_update_effect(&pHitEffect1, "pHitEffect1");
524 if (pHitEffect2.loaded) {
525 pHitEffect2.eff.periodic.direction.dir[0] = idegs;
526 pHitEffect2.eff.periodic.magnitude = imag;
528 joy_ff_update_effect(&pHitEffect2, "pHitEffect2");
532 joy_ff_start_effect(&pHitEffect1, "HitEffect1");
533 joy_ff_start_effect(&pHitEffect2, "HitEffect2");
536 void joy_ff_play_primary_shoot(int gain)
538 if ( !joy_ff_can_play() ) {
542 if ( !pShootEffect.loaded && !Joy_rumble ) {
548 if (pShootEffect.loaded) {
549 SDL_HapticStopEffect(haptic, pShootEffect.id);
551 static int primary_ff_level = 10000;
553 if (gain != primary_ff_level) {
554 pShootEffect.eff.periodic.magnitude = (Sint16)(32767.0f * (gain / 10000.0f));
556 joy_ff_update_effect(&pShootEffect, "pShootEffect");
558 primary_ff_level = gain;
561 joy_ff_start_effect(&pShootEffect, "ShootEffect");
562 } else if (Joy_rumble) {
563 static int rumble_timeout = 1;
565 if ( timestamp_elapsed(rumble_timeout) ) {
566 SDL_HapticRumblePlay(haptic, gain / 10000.0f, 100);
568 rumble_timeout = timestamp(100);
573 void joy_ff_play_secondary_shoot(int gain)
575 if ( !joy_ff_can_play() ) {
579 if ( !pSecShootEffect.loaded && !Joy_rumble ) {
583 gain = gain * 100 + 2500;
587 if (pSecShootEffect.loaded) {
588 SDL_HapticStopEffect(haptic, pSecShootEffect.id);
590 static int secondary_ff_level = 10000;
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;
596 joy_ff_update_effect(&pSecShootEffect, "pSecShootEffect");
598 secondary_ff_level = gain;
599 nprintf(("Joystick", "FF: Secondary force = 0x%04x\n", pSecShootEffect.eff.constant.level));
602 joy_ff_start_effect(&pSecShootEffect, "SecShootEffect");
603 } else if (Joy_rumble) {
604 static int rumble_timeout = 1;
606 if ( timestamp_elapsed(rumble_timeout) ) {
607 int duration = (150000 + gain * 25) / 1000;
609 SDL_HapticRumblePlay(haptic, gain / 10000.0f, duration);
611 rumble_timeout = timestamp(duration);
616 void joy_ff_adjust_handling(int speed)
621 if ( !joy_ff_can_play() ) {
625 if ( !pSpring.loaded ) {
629 static int last_speed = -1000;
631 if (speed == last_speed) {
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;
643 coeff = (short)(32767.0f * (v / 10000.0f));
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;
650 // nprintf(("Joystick", "FF: New handling force = 0x%04x\n", coeff));
652 joy_ff_update_effect(&pSpring, "pSpring");
655 static int joy_ff_effect_playing(haptic_effect_t *eff)
657 if ( !eff->loaded ) {
660 return (SDL_HapticGetEffectStatus(haptic, eff->id) > 0);
666 if ( !joy_ff_can_play() ) {
670 if ( !pDock.loaded ) {
674 SDL_HapticStopEffect(haptic, pDock.id);
676 pDock.eff.periodic.magnitude = 0x3332;
678 joy_ff_update_effect(&pDock, "pDock");
680 joy_ff_start_effect(&pDock, "Dock");
683 void joy_ff_play_reload_effect()
685 if ( !joy_ff_can_play() ) {
689 if ( !pDock.loaded ) {
693 SDL_HapticStopEffect(haptic, pDock.id);
695 pDock.eff.periodic.magnitude = 0x1999;
697 joy_ff_update_effect(&pDock, "pDock (reload)");
699 joy_ff_start_effect(&pDock, "Dock (Reload)");
702 void joy_ff_afterburn_on()
704 if ( !joy_ff_can_play() ) {
708 if (Joy_ff_afterburning) {
712 if ( !(pAfterburn1.loaded && pAfterburn2.loaded) ) {
716 SDL_HapticStopEffect(haptic, pAfterburn1.id);
718 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
719 pAfterburn1.eff.periodic.magnitude = 0x3332;
721 joy_ff_update_effect(&pAfterburn1, "pAfterburn1");
723 SDL_HapticStopEffect(haptic, pAfterburn2.id);
725 pAfterburn2.eff.periodic.length = SDL_HAPTIC_INFINITY;
726 pAfterburn2.eff.periodic.magnitude = 0x1999;
728 joy_ff_update_effect(&pAfterburn2, "pAfterburn2");
730 joy_ff_start_effect(&pAfterburn1, "Afterburn1");
731 joy_ff_start_effect(&pAfterburn2, "Afterburn2");
733 // nprintf(("Joystick", "FF: Afterburn started\n"));
735 Joy_ff_afterburning = 1;
738 void joy_ff_afterburn_off()
740 if ( !joy_ff_can_play() ) {
744 if ( !Joy_ff_afterburning ) {
748 if (pAfterburn1.loaded) {
749 SDL_HapticStopEffect(haptic, pAfterburn1.id);
752 if (pAfterburn2.loaded) {
753 SDL_HapticStopEffect(haptic, pAfterburn2.id);
756 Joy_ff_afterburning = 0;
758 // nprintf(("Joystick", "FF: Afterburn stopped\n"));
761 void joy_ff_explode()
763 if ( !joy_ff_can_play() ) {
767 if (pAfterburn1.loaded) {
768 SDL_HapticStopEffect(haptic, pAfterburn1.id);
771 if (pAfterburn2.loaded) {
772 SDL_HapticStopEffect(haptic, pAfterburn2.id);
775 if (pShootEffect.loaded) {
776 SDL_HapticStopEffect(haptic, pShootEffect.id);
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;
783 joy_ff_update_effect(&pShootEffect, "pShootEffect (explode)");
785 joy_ff_start_effect(&pShootEffect, "ShootEffect (Explode)");
786 } else if (Joy_rumble) {
787 static int rumble_timeout = 1;
789 if ( timestamp_elapsed(rumble_timeout) ) {
790 SDL_HapticRumblePlay(haptic, 1.0f, 500);
792 rumble_timeout = timestamp(500);
796 Joy_ff_afterburning = 0;
799 void joy_ff_fly_by(int mag)
803 if ( !joy_ff_can_play() ) {
807 if (Joy_ff_afterburning) {
811 if ( !(pAfterburn1.loaded && pAfterburn2.loaded) ) {
815 gain = mag * 120 + 4000;
819 SDL_HapticStopEffect(haptic, pAfterburn1.id);
821 pAfterburn1.eff.periodic.length = (6000 * mag + 400000) / 1000;
822 pAfterburn1.eff.periodic.magnitude = (Sint16)(26212.0f * (gain / 10000.0f));
824 joy_ff_update_effect(&pAfterburn1, "pAfterburn1 (flyby)");
826 SDL_HapticStopEffect(haptic, pAfterburn2.id);
828 pAfterburn2.eff.periodic.length = (6000 * mag + 400000) / 1000;
829 pAfterburn2.eff.periodic.magnitude = (Sint16)(13106.0f * (gain / 10000.0f));
831 joy_ff_update_effect(&pAfterburn2, "pAfterburn2 (flyby)");
833 joy_ff_start_effect(&pAfterburn1, "Afterburn1 (Fly by)");
834 joy_ff_start_effect(&pAfterburn2, "Afterburn2 (Fly by)");
837 void joy_ff_deathroll()
839 if ( !joy_ff_can_play() ) {
843 if (pAfterburn1.loaded && pAfterburn2.loaded) {
844 SDL_HapticStopEffect(haptic, pAfterburn1.id);
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;
851 joy_ff_update_effect(&pAfterburn1, "pAfterburn1 (deathroll)");
853 SDL_HapticStopEffect(haptic, pAfterburn2.id);
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;
860 joy_ff_update_effect(&pAfterburn2, "pAfterburn2 (deathroll)");
862 joy_ff_start_effect(&pAfterburn1, "Afterburn1 (Death Roll)");
863 joy_ff_start_effect(&pAfterburn2, "Afterburn2 (Death Roll)");
865 Joy_ff_afterburning = 1;