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"
18 static int Joy_ff_enabled = 0;
19 static int Joy_ff_acquired = 0;
20 static SDL_Haptic *haptic = NULL;
21 static int joy_ff_handling_scaler = 0;
22 static int Joy_ff_directional_hit_effect_enabled = 1;
23 static int Joy_rumble = 0;
31 static haptic_effect_t pHitEffect1;
32 static haptic_effect_t pHitEffect2;
33 static haptic_effect_t pAfterburn1;
34 static haptic_effect_t pAfterburn2;
35 static haptic_effect_t pShootEffect;
36 static haptic_effect_t pSecShootEffect;
37 static haptic_effect_t pSpring;
38 static haptic_effect_t pDock;
39 //static haptic_effect_t pDeathroll1;
40 //static haptic_effect_t pDeathroll2;
41 //static haptic_effect_t pExplode;
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);
47 extern SDL_Joystick *sdljoy;
54 ff_enabled = os_config_read_uint(NULL, "EnableJoystickFF", 1);
60 // mprintf(("Initializing Haptic...\n"));
62 if (SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) {
66 if ( !SDL_JoystickIsHaptic(sdljoy) ) {
67 SDL_QuitSubSystem(SDL_INIT_HAPTIC);
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(("Haptic device:\n"));
85 mprintf((" Rumble: %s\n", Joy_rumble ? "Yes" : "No"));
86 mprintf((" Axes: %d\n", SDL_HapticNumAxes(haptic)));
87 mprintf((" Max effects: %d\n", SDL_HapticNumEffects(haptic)));
88 mprintf((" Simultaneous effects: %d\n", SDL_HapticNumEffectsPlaying(haptic)));
90 joy_ff_create_effects();
97 Joy_ff_directional_hit_effect_enabled = os_config_read_uint(NULL, "EnableHitEffect", 1);
102 void joy_ff_shutdown()
104 if ( !Joy_ff_enabled ) {
110 SDL_HapticClose(haptic);
113 SDL_QuitSubSystem(SDL_INIT_HAPTIC);
119 static void joy_ff_create_effects()
121 // clear all SDL errors
124 unsigned int supported = 0;
126 supported = SDL_HapticQuery(haptic);
128 if ( !(supported & SDL_HAPTIC_CONSTANT) ) {
129 mprintf((" Constant Force: not supported\n"));
132 if ( !(supported & SDL_HAPTIC_SINE) ) {
133 mprintf((" Sine Wave: not supported\n"));
134 Warning(LOCATION, "Sine Wave: not supported");
137 if ( !(supported & SDL_HAPTIC_SAWTOOTHDOWN) ) {
138 mprintf((" Sawtooth Down: not supported\n"));
141 if ( !(supported & SDL_HAPTIC_SPRING) ) {
142 mprintf((" Spring: not supported\n"));
143 // Error(LOCATION, "Spring: not supported");
146 if ( !(supported & SDL_HAPTIC_SQUARE) ) {
147 mprintf((" Square: not supported\n"));
150 if ( !(supported & SDL_HAPTIC_TRIANGLE) ) {
151 mprintf((" Triangle: not supported\n"));
155 if (supported & SDL_HAPTIC_CONSTANT) {
157 memset(&pHitEffect1, 0, sizeof(haptic_effect_t));
159 pHitEffect1.eff.type = SDL_HAPTIC_CONSTANT;
160 pHitEffect1.eff.constant.direction.type = SDL_HAPTIC_POLAR;
161 pHitEffect1.eff.constant.direction.dir[0] = 0;
162 pHitEffect1.eff.constant.length = 300;
163 pHitEffect1.eff.constant.level = 0x7FFF;
164 pHitEffect1.eff.constant.attack_length = 0;
165 pHitEffect1.eff.constant.attack_level = 0x7FFF;
166 pHitEffect1.eff.constant.fade_length = 120;
167 pHitEffect1.eff.constant.fade_level = 1;
169 pHitEffect1.id = SDL_HapticNewEffect(haptic, &pHitEffect1.eff);
171 if (pHitEffect1.id < 0) {
172 mprintf((" Hit effect 1 failed to load:\n %s\n", SDL_GetError()));
174 pHitEffect1.loaded = 1;
178 if (supported & SDL_HAPTIC_SINE) {
180 memset(&pHitEffect2, 0, sizeof(haptic_effect_t));
182 pHitEffect2.eff.type = SDL_HAPTIC_SINE;
183 pHitEffect2.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
184 pHitEffect2.eff.periodic.direction.dir[0] = 9000;
185 pHitEffect2.eff.periodic.length = 300;
186 pHitEffect2.eff.periodic.period = 100;
187 pHitEffect2.eff.periodic.magnitude = 0x7FFF;
188 pHitEffect2.eff.periodic.attack_length = 100;
189 pHitEffect2.eff.periodic.fade_length = 100;
191 pHitEffect2.id = SDL_HapticNewEffect(haptic, &pHitEffect2.eff);
193 if (pHitEffect2.id < 0) {
194 mprintf((" Hit effect 2 failed to load:\n %s\n", SDL_GetError()));
196 pHitEffect2.loaded = 1;
200 if (supported & SDL_HAPTIC_SAWTOOTHDOWN) {
202 memset(&pShootEffect, 0, sizeof(haptic_effect_t));
204 pShootEffect.eff.type = SDL_HAPTIC_SAWTOOTHDOWN;
205 pShootEffect.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
206 pShootEffect.eff.periodic.direction.dir[0] = 0;
207 pShootEffect.eff.periodic.length = 160;
208 pShootEffect.eff.periodic.period = 20;
209 pShootEffect.eff.periodic.magnitude = 0x7FFF;
210 pShootEffect.eff.periodic.fade_length = 120;
212 pShootEffect.id = SDL_HapticNewEffect(haptic, &pShootEffect.eff);
214 if (pShootEffect.id < 0) {
215 mprintf((" Fire primary effect failed to load:\n %s\n", SDL_GetError()));
217 pShootEffect.loaded = 1;
221 if (supported & SDL_HAPTIC_CONSTANT) {
223 memset(&pSecShootEffect, 0, sizeof(haptic_effect_t));
225 pSecShootEffect.eff.type = SDL_HAPTIC_CONSTANT;
226 pSecShootEffect.eff.constant.direction.type = SDL_HAPTIC_POLAR;
227 pSecShootEffect.eff.constant.direction.dir[0] = 0;
228 pSecShootEffect.eff.constant.length = 200;
229 pSecShootEffect.eff.constant.level = 0x7FFF;
230 pSecShootEffect.eff.constant.attack_length = 50;
231 pSecShootEffect.eff.constant.attack_level = 0x7FFF;
232 pSecShootEffect.eff.constant.fade_length = 100;
233 pSecShootEffect.eff.constant.fade_level = 1;
235 pSecShootEffect.id = SDL_HapticNewEffect(haptic, &pSecShootEffect.eff);
237 if (pSecShootEffect.id < 0) {
238 mprintf((" Fire secondary effect failed to load:\n %s\n", SDL_GetError()));
240 pSecShootEffect.loaded = 1;
244 if (supported & SDL_HAPTIC_SPRING) {
246 memset(&pSpring, 0, sizeof(haptic_effect_t));
248 pSpring.eff.type = SDL_HAPTIC_SPRING;
249 pSpring.eff.condition.length = SDL_HAPTIC_INFINITY;
251 for (int i = 0; i < SDL_HapticNumAxes(haptic); i++) {
252 pSpring.eff.condition.right_sat[i] = 0x7FFF;
253 pSpring.eff.condition.left_sat[i] = 0x7FFF;
254 pSpring.eff.condition.right_coeff[i] = 0x147;
255 pSpring.eff.condition.left_coeff[i] = 0x147;
258 pSpring.id = SDL_HapticNewEffect(haptic, &pSpring.eff);
260 if (pSpring.id < 0) {
261 mprintf((" Spring effect failed to load:\n %s\n", SDL_GetError()));
264 joy_ff_start_effect(&pSpring, "Spring");
268 if (supported & SDL_HAPTIC_SINE) {
270 memset(&pAfterburn1, 0, sizeof(haptic_effect_t));
272 pAfterburn1.eff.type = SDL_HAPTIC_SINE;
273 pAfterburn1.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
274 pAfterburn1.eff.periodic.direction.dir[0] = 0;
275 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
276 pAfterburn1.eff.periodic.period = 20;
277 pAfterburn1.eff.periodic.magnitude = 0x6665;
279 pAfterburn1.id = SDL_HapticNewEffect(haptic, &pAfterburn1.eff);
281 if (pAfterburn1.id < 0) {
282 mprintf((" Afterburn effect 1 failed to load:\n %s\n", SDL_GetError()));
284 pAfterburn1.loaded = 1;
288 memset(&pAfterburn2, 0, sizeof(haptic_effect_t));
290 pAfterburn2.eff.type = SDL_HAPTIC_SINE;
291 pAfterburn2.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
292 pAfterburn2.eff.periodic.direction.dir[0] = 9000;
293 pAfterburn2.eff.periodic.length = 125;
294 pAfterburn2.eff.periodic.period = 100;
295 pAfterburn2.eff.periodic.magnitude = 0x3332;
297 pAfterburn2.id = SDL_HapticNewEffect(haptic, &pAfterburn2.eff);
299 if (pAfterburn2.id < 0) {
300 mprintf((" Afterburn effect 2 failed to load:\n %s\n", SDL_GetError()));
302 pAfterburn2.loaded = 1;
307 // if (supported & SDL_HAPTIC_SQUARE) {
308 if (supported & SDL_HAPTIC_TRIANGLE) {
310 memset(&pDock, 0, sizeof(haptic_effect_t));
312 // pDock.eff.type = SDL_HAPTIC_SQUARE;
313 pDock.eff.type = SDL_HAPTIC_TRIANGLE;
314 pDock.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
315 pDock.eff.periodic.direction.dir[0] = 9000;
316 pDock.eff.periodic.length = 125;
317 pDock.eff.periodic.period = 100;
318 pDock.eff.periodic.magnitude = 0x3332;
320 pDock.id = SDL_HapticNewEffect(haptic, &pDock.eff);
323 mprintf((" Dock effect failed to load:\n %s\n", SDL_GetError()));
330 if (supported & SDL_HAPTIC_SAWTOOTHDOWN) {
332 memset(&pExplode, 0, sizeof(haptic_effect_t));
334 pExplode.eff.type = SDL_HAPTIC_SAWTOOTHDOWN;
335 pExplode.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
336 pExplode.eff.periodic.direction.dir[0] = 9000;
337 pExplode.eff.periodic.length = 500;
338 pExplode.eff.periodic.period = 20;
339 pExplode.eff.periodic.magnitude = 0x7FFF;
340 pExplode.eff.periodic.attack_length = 0;
341 pExplode.eff.periodic.fade_length = 500;
343 pExplode.id = SDL_HapticNewEffect(haptic, &pExplode.eff);
345 if (pExplode.id < 0) {
346 mprintf((" Explosion effect failed to load:\n %s\n", SDL_GetError()));
352 if (supported & SDL_HAPTIC_SINE) {
354 memset(&pDeathroll1, 0, sizeof(haptic_effect_t));
356 pDeathroll1.eff.type = SDL_HAPTIC_SINE;
357 pDeathroll1.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
358 pDeathroll1.eff.periodic.direction.dir[0] = 0;
359 pDeathroll1.eff.periodic.length = SDL_HAPTIC_INFINITY;
360 pDeathroll1.eff.periodic.period = 200;
361 pDeathroll1.eff.periodic.magnitude = 0x7FFF;
362 pDeathroll1.eff.periodic.attack_length = 200;
364 pDeathroll1.id = SDL_HapticNewEffect(haptic, &pDeathroll1.eff);
366 if (pDeathroll1.id < 0) {
367 mprintf((" Deathroll effect 1 failed to load:\n %s\n", SDL_GetError()));
369 pDeathroll1.loaded = 1;
373 memset(&pDeathroll2, 0, sizeof(haptic_effect_t));
375 pDeathroll2.eff.type = SDL_HAPTIC_SINE;
376 pDeathroll2.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
377 pDeathroll2.eff.periodic.direction.dir[0] = 9000;
378 pDeathroll2.eff.periodic.length = SDL_HAPTIC_INFINITY;
379 pDeathroll2.eff.periodic.period = 200;
380 pDeathroll2.eff.periodic.magnitude = 0x7FFF;
381 pDeathroll2.eff.periodic.attack_length = 200;
383 pDeathroll2.id = SDL_HapticNewEffect(haptic, &pDeathroll2.eff);
385 if (pDeathroll2.id < 0) {
386 mprintf((" Deathroll effect 2 failed to load:\n %s\n", SDL_GetError()));
388 pDeathroll2.loaded = 1;
394 static void joy_ff_start_effect(haptic_effect_t *eff, const char *name)
396 if ( !eff->loaded ) {
400 // nprintf(("Joystick", "FF: Starting effect %s\n", name));
402 if ( SDL_HapticRunEffect(haptic, eff->id, 1) ) {
403 mprintf(("HapticERROR: Unable to run %s:\n %s\n", name, SDL_GetError()));
407 void joy_ff_stop_effects()
409 if ( !Joy_ff_enabled ) {
413 SDL_HapticStopAll(haptic);
416 void joy_ff_mission_init(vector v)
420 joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
423 void joy_reacquire_ff()
425 if ( !Joy_ff_enabled ) {
429 if (Joy_ff_acquired) {
433 joy_ff_start_effect(&pSpring, "Spring");
438 void joy_unacquire_ff()
440 if ( !Joy_ff_enabled ) {
444 if ( !Joy_ff_acquired ) {
448 joy_ff_stop_effects();
453 void joy_ff_play_vector_effect(vector *v, float scaler)
458 // nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->xyz.x, v->xyz.y, v->xyz.z, scaler));
459 vm_vec_copy_scale(&vf, v, scaler);
463 if (vf.xyz.y + vf.xyz.z < 0.0f) {
464 y = -vm_vec_mag(&vf);
469 joy_ff_play_dir_effect(-x, -y);
472 void joy_ff_play_dir_effect(float x, float y)
477 if ( !Joy_ff_enabled ) {
481 if ( !Joy_ff_acquired ) {
485 // allow for at least one of the effects to work
486 if ( !pHitEffect1.loaded && !pHitEffect2.loaded ) {
490 if (joy_ff_effect_playing(&pHitEffect1) || joy_ff_effect_playing(&pHitEffect2)) {
491 nprintf(("Joystick", "FF: HitEffect already playing. Skipping\n"));
495 if (Joy_ff_directional_hit_effect_enabled) {
498 } else if (x < -8000.0f) {
504 } else if (y < -8000.0f) {
508 imag = (int) fl_sqrt(x * x + y * y);
513 degs = (float)atan2(x, y);
514 idegs = (int) (degs * 18000.0f / PI) + 90;
519 while (idegs >= 36000) {
523 if (pHitEffect1.loaded) {
524 pHitEffect1.eff.constant.direction.dir[0] = idegs;
525 pHitEffect1.eff.constant.level = fl2i(0x7FFF * (imag / 10000.0f));
527 if ( SDL_HapticUpdateEffect(haptic, pHitEffect1.id, &pHitEffect1.eff) < 0 ) {
528 mprintf(("HapticERROR: Unable to update pHitEffect1:\n %s\n", SDL_GetError()));
536 if (pHitEffect2.loaded) {
537 pHitEffect2.eff.periodic.direction.dir[0] = idegs;
538 pHitEffect2.eff.periodic.magnitude = fl2i(0x7FFF * (imag / 10000.0f));
540 if ( SDL_HapticUpdateEffect(haptic, pHitEffect2.id, &pHitEffect2.eff) < 0 ) {
541 mprintf(("HapticERROR: Unable to update pHitEffect2:\n %s\n", SDL_GetError()));
546 joy_ff_start_effect(&pHitEffect1, "HitEffect1");
547 joy_ff_start_effect(&pHitEffect2, "HitEffect2");
550 static int primary_ff_level = 10000;
552 void joy_ff_play_primary_shoot(int gain)
554 if ( !Joy_ff_enabled ) {
558 if ( !Joy_ff_acquired ) {
562 if ( !pShootEffect.loaded && !Joy_rumble ) {
568 } else if (gain > 10000) {
572 if (pShootEffect.loaded) {
573 SDL_HapticStopEffect(haptic, pShootEffect.id);
575 if (gain != primary_ff_level) {
576 pShootEffect.eff.periodic.direction.dir[0] = 0;
577 pShootEffect.eff.periodic.length = 160;
578 pShootEffect.eff.periodic.magnitude = fl2i(0x7FFF * (gain / 10000.0f));
579 pShootEffect.eff.periodic.fade_length = 120;
581 if ( SDL_HapticUpdateEffect(haptic, pShootEffect.id, &pShootEffect.eff) < 0 ) {
582 mprintf(("HapticERROR: Unable to update pShootEffect:\n %s\n", SDL_GetError()));
585 primary_ff_level = gain;
588 joy_ff_start_effect(&pShootEffect, "ShootEffect");
589 } else if (Joy_rumble) {
590 SDL_HapticRumblePlay(haptic, (gain / 10000.0f) * 0.5f, 100);
594 static int secondary_ff_level = 10000;
596 void joy_ff_play_secondary_shoot(int gain)
598 if ( !Joy_ff_enabled ) {
602 if ( !Joy_ff_acquired ) {
606 if ( !pSecShootEffect.loaded && !Joy_rumble ) {
610 gain = gain * 100 + 2500;
614 } else if (gain > 10000) {
618 if (pSecShootEffect.loaded) {
619 SDL_HapticStopEffect(haptic, pSecShootEffect.id);
621 if (gain != secondary_ff_level) {
622 pSecShootEffect.eff.constant.level = fl2i(0x7FFF * (gain / 10000.0f));
623 pSecShootEffect.eff.constant.length = (150000 + gain * 25) / 1000;
625 if ( SDL_HapticUpdateEffect(haptic, pSecShootEffect.id, &pSecShootEffect.eff) < 0 ) {
626 mprintf(("HapticERROR: Unable to update pSecShootEffect:\n %s\n", SDL_GetError()));
629 secondary_ff_level = gain;
630 nprintf(("Joystick", "FF: Secondary force = 0x%04x\n", pSecShootEffect.eff.constant.level));
633 joy_ff_start_effect(&pSecShootEffect, "SecShootEffect");
634 } else if (Joy_rumble) {
635 SDL_HapticRumblePlay(haptic, (gain / 10000.0f) * 0.5f, (150000 + gain * 25) / 1000);
639 void joy_ff_adjust_handling(int speed)
644 if ( !Joy_ff_enabled ) {
648 if ( !Joy_ff_acquired ) {
652 if ( !pSpring.loaded ) {
656 v = speed * joy_ff_handling_scaler * 2 / 3;
657 // v += joy_ff_handling_scaler * joy_ff_handling_scaler * 6 / 7 + 250;
658 v += joy_ff_handling_scaler * 45 - 500;
662 } else if (v > 10000) {
666 coeff = fl2i(0x7FFF * (v / 10000.0f));
668 for (int i = 0; i < SDL_HapticNumAxes(haptic); i++) {
669 pSpring.eff.condition.right_coeff[i] = coeff;
670 pSpring.eff.condition.left_coeff[i] = coeff;
673 // nprintf(("Joystick", "FF: New handling force = 0x%04x\n", coeff));
675 SDL_HapticUpdateEffect(haptic, pSpring.id, &pSpring.eff);
678 static int joy_ff_effect_playing(haptic_effect_t *eff)
680 if ( !eff->loaded ) {
683 return (SDL_HapticGetEffectStatus(haptic, eff->id) > 0);
689 if ( !Joy_ff_enabled ) {
693 if ( !Joy_ff_acquired ) {
697 if ( !pDock.loaded ) {
701 SDL_HapticStopEffect(haptic, pDock.id);
703 pDock.eff.periodic.magnitude = 0x7fff;
705 if ( SDL_HapticUpdateEffect(haptic, pDock.id, &pDock.eff) < 0 ) {
706 mprintf(("HapticERROR: Unable to update pDock:\n %s\n", SDL_GetError()));
709 joy_ff_start_effect(&pDock, "Dock");
712 void joy_ff_play_reload_effect()
714 if ( !Joy_ff_enabled ) {
718 if ( !Joy_ff_acquired ) {
722 if ( !pDock.loaded ) {
726 SDL_HapticStopEffect(haptic, pDock.id);
728 pDock.eff.periodic.magnitude = 0x3fff;
730 if ( SDL_HapticUpdateEffect(haptic, pDock.id, &pDock.eff) < 0 ) {
731 mprintf(("HapticERROR: Unable to update pDock:\n %s\n", SDL_GetError()));
734 joy_ff_start_effect(&pDock, "Dock (Reload)");
737 static int Joy_ff_afterburning = 0;
739 void joy_ff_afterburn_on()
741 if ( !Joy_ff_enabled ) {
745 if ( !Joy_ff_acquired ) {
749 if (Joy_ff_afterburning) {
753 if ( !(pAfterburn1.loaded && pAfterburn2.loaded) ) {
757 SDL_HapticStopEffect(haptic, pAfterburn1.id);
759 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
760 pAfterburn1.eff.periodic.period = 20;
761 pAfterburn1.eff.periodic.magnitude = 0x3fff;
762 pAfterburn1.eff.periodic.attack_length = 0;
764 if ( SDL_HapticUpdateEffect(haptic, pAfterburn1.id, &pAfterburn1.eff) < 0 ) {
765 mprintf(("HapticERROR: Unable to update pAfterburn1:\n %s\n", SDL_GetError()));
768 SDL_HapticStopEffect(haptic, pAfterburn2.id);
770 pAfterburn2.eff.periodic.length = SDL_HAPTIC_INFINITY;
771 pAfterburn2.eff.periodic.period = 100;
772 pAfterburn2.eff.periodic.magnitude = 0x3fff;
773 pAfterburn2.eff.periodic.attack_length = 0;
775 if ( SDL_HapticUpdateEffect(haptic, pAfterburn2.id, &pAfterburn2.eff) < 0 ) {
776 mprintf(("HapticERROR: Unable to update pAfterburn2:\n %s\n", SDL_GetError()));
779 joy_ff_start_effect(&pAfterburn1, "Afterburn1");
780 joy_ff_start_effect(&pAfterburn2, "Afterburn2");
782 // nprintf(("Joystick", "FF: Afterburn started\n"));
784 Joy_ff_afterburning = 1;
787 void joy_ff_afterburn_off()
789 if ( !Joy_ff_enabled ) {
793 if ( !Joy_ff_acquired ) {
797 if ( !Joy_ff_afterburning ) {
801 if (pAfterburn1.loaded) {
802 SDL_HapticStopEffect(haptic, pAfterburn1.id);
805 if (pAfterburn2.loaded) {
806 SDL_HapticStopEffect(haptic, pAfterburn2.id);
809 Joy_ff_afterburning = 0;
811 // nprintf(("Joystick", "FF: Afterburn stopped\n"));
814 void joy_ff_explode()
816 if ( !Joy_ff_enabled ) {
820 if ( !Joy_ff_acquired ) {
824 if (pDeathroll1.loaded) {
825 SDL_HapticStopEffect(haptic, pDeathroll1.id);
828 if (pDeathroll2.loaded) {
829 SDL_HapticStopEffect(haptic, pDeathroll2.id);
832 if (pExplode.loaded) {
833 SDL_HapticStopEffect(haptic, pExplode.id);
836 joy_ff_start_effect(&pExplode, "Explode");
839 if (pAfterburn1.loaded) {
840 SDL_HapticStopEffect(haptic, pAfterburn1.id);
843 if (pAfterburn2.loaded) {
844 SDL_HapticStopEffect(haptic, pAfterburn2.id);
847 if (pShootEffect.loaded) {
848 SDL_HapticStopEffect(haptic, pShootEffect.id);
850 pShootEffect.eff.periodic.direction.dir[0] = 9000;
851 pShootEffect.eff.periodic.length = 500;
852 pShootEffect.eff.periodic.magnitude = 0x7FFF;
853 pShootEffect.eff.periodic.fade_length = 500;
855 if ( SDL_HapticUpdateEffect(haptic, pShootEffect.id, &pShootEffect.eff) < 0 ) {
856 mprintf(("HapticERROR: Unable to update pShootEffect:\n %s\n", SDL_GetError()));
859 joy_ff_start_effect(&pShootEffect, "ShootEffect (Explode)");
862 Joy_ff_afterburning = 0;
865 void joy_ff_fly_by(int mag)
869 if ( !Joy_ff_enabled ) {
873 if ( !Joy_ff_acquired ) {
877 if (Joy_ff_afterburning) {
881 if ( !(pAfterburn1.loaded && pAfterburn2.loaded) ) {
885 gain = mag * 120 + 4000;
889 } else if (gain > 10000) {
893 SDL_HapticStopEffect(haptic, pAfterburn1.id);
895 pAfterburn1.eff.periodic.length = (6000 * mag + 400000) / 1000;
896 pAfterburn1.eff.periodic.period = 20;
897 pAfterburn1.eff.periodic.magnitude = fl2i(0x7FFF * (gain / 10000.0f));
898 pAfterburn1.eff.periodic.attack_length = 0;
900 if ( SDL_HapticUpdateEffect(haptic, pAfterburn1.id, &pAfterburn1.eff) < 0 ) {
901 mprintf(("HapticERROR: Unable to update pAfterburn1:\n %s\n", SDL_GetError()));
905 SDL_HapticStopEffect(haptic, pAfterburn2.id);
907 pAfterburn2.eff.periodic.length = (6000 * mag + 400000) / 1000;
908 pAfterburn2.eff.periodic.period = 100;
909 pAfterburn2.eff.periodic.magnitude = fl2i(0x7FFF * (gain / 10000.0f));
910 pAfterburn2.eff.periodic.attack_length = 0;
912 if ( SDL_HapticUpdateEffect(haptic, pAfterburn2.id, &pAfterburn2.eff) < 0 ) {
913 mprintf(("HapticERROR: Unable to update pAfterburn2:\n %s\n", SDL_GetError()));
916 joy_ff_start_effect(&pAfterburn1, "Afterburn1 (Fly by)");
917 joy_ff_start_effect(&pAfterburn2, "Afterburn2 (Fly by)");
920 void joy_ff_deathroll()
922 if ( !Joy_ff_enabled ) {
926 if ( !Joy_ff_acquired ) {
930 if (pDeathroll1.loaded) {
931 SDL_HapticStopEffect(haptic, pDeathroll1.id);
934 if (pDeathroll2.loaded) {
935 SDL_HapticStopEffect(haptic, pDeathroll2.id);
938 joy_ff_start_effect(&pDeathroll1, "Deathroll1");
939 joy_ff_start_effect(&pDeathroll2, "Deathroll2");
942 if (pAfterburn1.loaded && pAfterburn2.loaded) {
943 SDL_HapticStopEffect(haptic, pAfterburn1.id);
945 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
946 pAfterburn1.eff.periodic.period = 200;
947 pAfterburn1.eff.periodic.magnitude = 0x7FFF;
948 pAfterburn1.eff.periodic.attack_length = 200;
950 if ( SDL_HapticUpdateEffect(haptic, pAfterburn1.id, &pAfterburn1.eff) < 0 ) {
951 mprintf(("HapticERROR: Unable to update pAfterburn1:\n %s\n", SDL_GetError()));
954 SDL_HapticStopEffect(haptic, pAfterburn2.id);
956 pAfterburn2.eff.periodic.length = SDL_HAPTIC_INFINITY;
957 pAfterburn2.eff.periodic.period = 200;
958 pAfterburn2.eff.periodic.magnitude = 0x7FFF;
959 pAfterburn2.eff.periodic.attack_length = 200;
961 if ( SDL_HapticUpdateEffect(haptic, pAfterburn2.id, &pAfterburn2.eff) < 0 ) {
962 mprintf(("HapticERROR: Unable to update pAfterburn2:\n %s\n", SDL_GetError()));
965 joy_ff_start_effect(&pAfterburn1, "Afterburn1 (Death Roll)");
966 joy_ff_start_effect(&pAfterburn2, "Afterburn2 (Death Roll)");
968 Joy_ff_afterburning = 1;