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"
19 static int Joy_ff_enabled = 0;
20 static int Joy_ff_acquired = 0;
21 static SDL_Haptic *haptic = NULL;
22 static int joy_ff_handling_scaler = 0;
23 static int Joy_ff_directional_hit_effect_enabled = 1;
24 static int Joy_rumble = 0;
25 static int Joy_ff_afterburning = 0;
33 static haptic_effect_t pHitEffect1;
34 static haptic_effect_t pHitEffect2;
35 static haptic_effect_t pAfterburn1;
36 static haptic_effect_t pAfterburn2;
37 static haptic_effect_t pShootEffect;
38 static haptic_effect_t pSecShootEffect;
39 static haptic_effect_t pSpring;
40 static haptic_effect_t pDock;
42 static void joy_ff_create_effects();
43 //static int joy_ff_effect_playing(haptic_effect_t *eff);
44 static void joy_ff_start_effect(haptic_effect_t *eff, const char *name);
46 extern SDL_Joystick *sdljoy;
53 ff_enabled = os_config_read_uint("Controls", "EnableJoystickFF", 0);
55 if ( !ff_enabled || !SDL_JoystickIsHaptic(sdljoy) ) {
59 if (SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) {
60 mprintf((" ERROR: Unable to initialize haptic subsystem\n"));
64 haptic = SDL_HapticOpenFromJoystick(sdljoy);
67 mprintf((" ERROR: Unable to open haptic joystick\n"));
68 SDL_QuitSubSystem(SDL_INIT_HAPTIC);
72 if ( SDL_HapticRumbleSupported(haptic) ) {
73 SDL_HapticRumbleInit(haptic);
77 mprintf((" Rumble : %s\n", Joy_rumble ? "Yes" : "No"));
78 mprintf((" Haptic Axes : %d\n", SDL_HapticNumAxes(haptic)));
79 mprintf((" Max effects : %d\n", SDL_HapticNumEffects(haptic)));
80 mprintf((" Running effects : %d\n", SDL_HapticNumEffectsPlaying(haptic)));
82 joy_ff_create_effects();
87 Joy_ff_directional_hit_effect_enabled = os_config_read_uint("Controls", "EnableHitEffect", 1);
92 void joy_ff_shutdown()
94 if ( !Joy_ff_enabled ) {
98 SDL_HapticStopAll(haptic);
102 SDL_HapticClose(haptic);
105 SDL_QuitSubSystem(SDL_INIT_HAPTIC);
111 static void joy_ff_create_effects()
113 // clear all SDL errors
116 unsigned int supported = 0;
118 supported = SDL_HapticQuery(haptic);
120 if ( !(supported & SDL_HAPTIC_CONSTANT) ) {
121 mprintf((" Constant Force : not supported\n"));
124 if ( !(supported & SDL_HAPTIC_SINE) ) {
125 mprintf((" Sine Wave : not supported\n"));
128 if ( !(supported & SDL_HAPTIC_SAWTOOTHDOWN) ) {
129 mprintf((" Sawtooth Down : not supported\n"));
132 if ( !(supported & SDL_HAPTIC_SPRING) ) {
133 mprintf((" Spring : not supported\n"));
136 if ( !(supported & SDL_HAPTIC_SQUARE) ) {
137 mprintf((" Square : not supported\n"));
140 if ( !(supported & SDL_HAPTIC_TRIANGLE) ) {
141 mprintf((" Triangle : not supported\n"));
145 if (supported & SDL_HAPTIC_SPRING) {
147 memset(&pSpring, 0, sizeof(haptic_effect_t));
149 pSpring.eff.type = SDL_HAPTIC_SPRING;
150 pSpring.eff.condition.type = SDL_HAPTIC_SPRING;
151 pSpring.eff.condition.length = SDL_HAPTIC_INFINITY;
153 for (int i = 0; i < 3; i++) {
154 pSpring.eff.condition.right_sat[i] = 0x7FFF;
155 pSpring.eff.condition.left_sat[i] = 0x7FFF;
156 pSpring.eff.condition.right_coeff[i] = 0x147;
157 pSpring.eff.condition.left_coeff[i] = 0x147;
160 pSpring.id = SDL_HapticNewEffect(haptic, &pSpring.eff);
162 if (pSpring.id < 0) {
163 mprintf((" Spring effect failed to load:\n %s\n", SDL_GetError()));
169 if (supported & SDL_HAPTIC_SAWTOOTHDOWN) {
171 memset(&pShootEffect, 0, sizeof(haptic_effect_t));
173 pShootEffect.eff.type = SDL_HAPTIC_SAWTOOTHDOWN;
174 pShootEffect.eff.periodic.type = SDL_HAPTIC_SAWTOOTHDOWN;
175 pShootEffect.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
176 pShootEffect.eff.periodic.direction.dir[0] = 0;
177 pShootEffect.eff.periodic.length = 160;
178 pShootEffect.eff.periodic.period = 20;
179 pShootEffect.eff.periodic.magnitude = 0x7FFF;
180 pShootEffect.eff.periodic.fade_length = 120;
182 pShootEffect.id = SDL_HapticNewEffect(haptic, &pShootEffect.eff);
184 if (pShootEffect.id < 0) {
185 mprintf((" Fire primary effect failed to load:\n %s\n", SDL_GetError()));
187 pShootEffect.loaded = 1;
191 if (supported & SDL_HAPTIC_CONSTANT) {
193 memset(&pSecShootEffect, 0, sizeof(haptic_effect_t));
195 pSecShootEffect.eff.type = SDL_HAPTIC_CONSTANT;
196 pSecShootEffect.eff.constant.type = SDL_HAPTIC_CONSTANT;
197 pSecShootEffect.eff.constant.direction.type = SDL_HAPTIC_POLAR;
198 pSecShootEffect.eff.constant.direction.dir[0] = 0;
199 pSecShootEffect.eff.constant.length = 200;
200 pSecShootEffect.eff.constant.level = 0x7FFF;
201 pSecShootEffect.eff.constant.attack_length = 50;
202 pSecShootEffect.eff.constant.attack_level = 0x7FFF;
203 pSecShootEffect.eff.constant.fade_length = 100;
204 pSecShootEffect.eff.constant.fade_level = 1;
206 pSecShootEffect.id = SDL_HapticNewEffect(haptic, &pSecShootEffect.eff);
208 if (pSecShootEffect.id < 0) {
209 mprintf((" Fire secondary effect failed to load:\n %s\n", SDL_GetError()));
211 pSecShootEffect.loaded = 1;
215 if (supported & SDL_HAPTIC_SINE) {
217 memset(&pAfterburn1, 0, sizeof(haptic_effect_t));
219 pAfterburn1.eff.type = SDL_HAPTIC_SINE;
220 pAfterburn1.eff.periodic.type = SDL_HAPTIC_SINE;
221 pAfterburn1.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
222 pAfterburn1.eff.periodic.direction.dir[0] = 0;
223 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
224 pAfterburn1.eff.periodic.period = 20;
225 pAfterburn1.eff.periodic.magnitude = 0x3332;
227 pAfterburn1.id = SDL_HapticNewEffect(haptic, &pAfterburn1.eff);
229 if (pAfterburn1.id < 0) {
230 mprintf((" Afterburn effect 1 failed to load:\n %s\n", SDL_GetError()));
232 pAfterburn1.loaded = 1;
236 memset(&pAfterburn2, 0, sizeof(haptic_effect_t));
238 pAfterburn2.eff.type = SDL_HAPTIC_SINE;
239 pAfterburn2.eff.periodic.type = SDL_HAPTIC_SINE;
240 pAfterburn2.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
241 pAfterburn2.eff.periodic.direction.dir[0] = 9000;
242 pAfterburn2.eff.periodic.length = 125;
243 pAfterburn2.eff.periodic.period = 100;
244 pAfterburn2.eff.periodic.magnitude = 0x1999;
246 pAfterburn2.id = SDL_HapticNewEffect(haptic, &pAfterburn2.eff);
248 if (pAfterburn2.id < 0) {
249 mprintf((" Afterburn effect 2 failed to load:\n %s\n", SDL_GetError()));
251 pAfterburn2.loaded = 1;
255 if (supported & SDL_HAPTIC_CONSTANT) {
257 memset(&pHitEffect1, 0, sizeof(haptic_effect_t));
259 pHitEffect1.eff.type = SDL_HAPTIC_CONSTANT;
260 pHitEffect1.eff.constant.type = SDL_HAPTIC_CONSTANT;
261 pHitEffect1.eff.constant.direction.type = SDL_HAPTIC_POLAR;
262 pHitEffect1.eff.constant.direction.dir[0] = 0;
263 pHitEffect1.eff.constant.length = 300;
264 pHitEffect1.eff.constant.level = 0x7FFF;
265 pHitEffect1.eff.constant.attack_length = 0;
266 pHitEffect1.eff.constant.attack_level = 0x7FFF;
267 pHitEffect1.eff.constant.fade_length = 120;
268 pHitEffect1.eff.constant.fade_level = 1;
270 pHitEffect1.id = SDL_HapticNewEffect(haptic, &pHitEffect1.eff);
272 if (pHitEffect1.id < 0) {
273 mprintf((" Hit effect 1 failed to load:\n %s\n", SDL_GetError()));
275 pHitEffect1.loaded = 1;
279 if (supported & SDL_HAPTIC_SINE) {
281 memset(&pHitEffect2, 0, sizeof(haptic_effect_t));
283 pHitEffect2.eff.type = SDL_HAPTIC_SINE;
284 pHitEffect2.eff.periodic.type = SDL_HAPTIC_SINE;
285 pHitEffect2.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
286 pHitEffect2.eff.periodic.direction.dir[0] = 9000;
287 pHitEffect2.eff.periodic.length = 300;
288 pHitEffect2.eff.periodic.period = 100;
289 pHitEffect2.eff.periodic.magnitude = 0x7FFF;
290 pHitEffect2.eff.periodic.attack_length = 100;
291 pHitEffect2.eff.periodic.fade_length = 100;
293 pHitEffect2.id = SDL_HapticNewEffect(haptic, &pHitEffect2.eff);
295 if (pHitEffect2.id < 0) {
296 mprintf((" Hit effect 2 failed to load:\n %s\n", SDL_GetError()));
298 pHitEffect2.loaded = 1;
302 // if (supported & SDL_HAPTIC_SQUARE) {
303 if (supported & SDL_HAPTIC_TRIANGLE) {
305 memset(&pDock, 0, sizeof(haptic_effect_t));
307 // pDock.eff.type = SDL_HAPTIC_SQUARE;
308 // pDock.eff.periodic.type = SDL_HAPTIC_SQUARE;
309 pDock.eff.type = SDL_HAPTIC_TRIANGLE;
310 pDock.eff.periodic.type = SDL_HAPTIC_TRIANGLE;
311 pDock.eff.periodic.direction.type = SDL_HAPTIC_POLAR;
312 pDock.eff.periodic.direction.dir[0] = 9000;
313 pDock.eff.periodic.length = 125;
314 pDock.eff.periodic.period = 100;
315 pDock.eff.periodic.magnitude = 0x3332;
317 pDock.id = SDL_HapticNewEffect(haptic, &pDock.eff);
320 mprintf((" Dock effect failed to load:\n %s\n", SDL_GetError()));
327 static bool joy_ff_can_play()
329 return (Joy_ff_enabled && Joy_ff_acquired);
332 static void joy_ff_update_effect(haptic_effect_t *eff, const char *name)
334 if ( !eff->loaded ) {
338 if ( SDL_HapticUpdateEffect(haptic, eff->id, &eff->eff) < 0 ) {
339 mprintf(("HapticERROR: Unable to update %s:\n %s\n", name, SDL_GetError()));
343 static void joy_ff_start_effect(haptic_effect_t *eff, const char *name)
345 if ( !eff->loaded ) {
349 // nprintf(("Joystick", "FF: Starting effect %s\n", name));
351 if ( SDL_HapticRunEffect(haptic, eff->id, 1) ) {
352 mprintf(("HapticERROR: Unable to run %s:\n %s\n", name, SDL_GetError()));
356 void joy_ff_stop_effects()
358 if ( !Joy_ff_enabled ) {
362 SDL_HapticStopAll(haptic);
365 void joy_ff_mission_init(vector v)
369 joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
371 joy_ff_adjust_handling(0);
372 joy_ff_start_effect(&pSpring, "Spring");
374 Joy_ff_afterburning = 0;
376 // reset afterburn effects to default values
378 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
379 pAfterburn1.eff.periodic.period = 20;
380 pAfterburn1.eff.periodic.magnitude = 0x3332;
381 pAfterburn1.eff.periodic.attack_length = 0;
383 joy_ff_update_effect(&pAfterburn1, "pAfterburn1 (init)");
385 pAfterburn2.eff.periodic.length = SDL_HAPTIC_INFINITY;
386 pAfterburn2.eff.periodic.period = 100;
387 pAfterburn2.eff.periodic.magnitude = 0x1999;
388 pAfterburn2.eff.periodic.attack_length = 0;
390 joy_ff_update_effect(&pAfterburn2, "pAfterburn2 (init)");
392 // reset primary shoot effect to default values
394 pShootEffect.eff.periodic.direction.dir[0] = 0;
395 pShootEffect.eff.periodic.length = 160;
396 pShootEffect.eff.periodic.fade_length = 120;
398 joy_ff_update_effect(&pShootEffect, "pShootEffect (init)");
401 void joy_reacquire_ff()
403 if ( !Joy_ff_enabled ) {
407 if (Joy_ff_acquired) {
411 joy_ff_start_effect(&pSpring, "Spring");
416 void joy_unacquire_ff()
418 if ( !Joy_ff_enabled ) {
422 if ( !Joy_ff_acquired ) {
426 joy_ff_stop_effects();
431 void joy_ff_play_vector_effect(vector *v, float scaler)
436 if ( !joy_ff_can_play() ) {
440 // nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->xyz.x, v->xyz.y, v->xyz.z, scaler));
441 vm_vec_copy_scale(&vf, v, scaler);
445 if (vf.xyz.y + vf.xyz.z < 0.0f) {
446 y = -vm_vec_mag(&vf);
451 joy_ff_play_dir_effect(-x, -y);
454 void joy_ff_play_dir_effect(float x, float y)
456 static int hit_timeout = 1;
461 if ( !joy_ff_can_play() ) {
465 // allow for at least one of the effects to work
466 if ( !pHitEffect1.loaded && !pHitEffect2.loaded ) {
470 if ( !timestamp_elapsed(hit_timeout) ) {
471 nprintf(("Joystick", "FF: HitEffect already playing. Skipping\n"));
475 hit_timeout = timestamp(pHitEffect1.eff.condition.length);
477 if (Joy_ff_directional_hit_effect_enabled) {
480 } else if (x < -8000.0f) {
486 } else if (y < -8000.0f) {
490 mag = fl_sqrt(x * x + y * y) / 10000.0f;
491 CAP(mag, 0.0f, 1.0f);
493 imag = (Sint16)(32767.0f * mag);
495 degs = (float)atan2(x, y);
496 idegs = (int) (degs * 18000.0f / PI) + 90;
502 while (idegs >= 36000) {
506 if (pHitEffect1.loaded) {
507 pHitEffect1.eff.constant.direction.dir[0] = idegs;
508 pHitEffect1.eff.constant.level = imag;
510 joy_ff_update_effect(&pHitEffect1, "pHitEffect1");
517 if (pHitEffect2.loaded) {
518 pHitEffect2.eff.periodic.direction.dir[0] = idegs;
519 pHitEffect2.eff.periodic.magnitude = imag;
521 joy_ff_update_effect(&pHitEffect2, "pHitEffect2");
525 joy_ff_start_effect(&pHitEffect1, "HitEffect1");
526 joy_ff_start_effect(&pHitEffect2, "HitEffect2");
529 void joy_ff_play_primary_shoot(int gain)
531 if ( !joy_ff_can_play() ) {
535 if ( !pShootEffect.loaded && !Joy_rumble ) {
541 if (pShootEffect.loaded) {
542 SDL_HapticStopEffect(haptic, pShootEffect.id);
544 static int primary_ff_level = 10000;
546 if (gain != primary_ff_level) {
547 pShootEffect.eff.periodic.magnitude = (Sint16)(32767.0f * (gain / 10000.0f));
549 joy_ff_update_effect(&pShootEffect, "pShootEffect");
551 primary_ff_level = gain;
554 joy_ff_start_effect(&pShootEffect, "ShootEffect");
555 } else if (Joy_rumble) {
556 static int rumble_timeout = 1;
558 if ( timestamp_elapsed(rumble_timeout) ) {
559 SDL_HapticRumblePlay(haptic, gain / 10000.0f, 100);
561 rumble_timeout = timestamp(100);
566 void joy_ff_play_secondary_shoot(int gain)
568 if ( !joy_ff_can_play() ) {
572 if ( !pSecShootEffect.loaded && !Joy_rumble ) {
576 gain = gain * 100 + 2500;
580 if (pSecShootEffect.loaded) {
581 SDL_HapticStopEffect(haptic, pSecShootEffect.id);
583 static int secondary_ff_level = 10000;
585 if (gain != secondary_ff_level) {
586 pSecShootEffect.eff.constant.level = (Sint16)(32767.0f * (gain / 10000.0f));
587 pSecShootEffect.eff.constant.length = (150000 + gain * 25) / 1000;
589 joy_ff_update_effect(&pSecShootEffect, "pSecShootEffect");
591 secondary_ff_level = gain;
592 nprintf(("Joystick", "FF: Secondary force = 0x%04x\n", pSecShootEffect.eff.constant.level));
595 joy_ff_start_effect(&pSecShootEffect, "SecShootEffect");
596 } else if (Joy_rumble) {
597 static int rumble_timeout = 1;
599 if ( timestamp_elapsed(rumble_timeout) ) {
600 int duration = (150000 + gain * 25) / 1000;
602 SDL_HapticRumblePlay(haptic, gain / 10000.0f, duration);
604 rumble_timeout = timestamp(duration);
609 void joy_ff_adjust_handling(int speed)
614 if ( !joy_ff_can_play() ) {
618 if ( !pSpring.loaded ) {
622 static int last_speed = -1000;
624 if (speed == last_speed) {
630 v = speed * joy_ff_handling_scaler * 2 / 3;
631 // v += joy_ff_handling_scaler * joy_ff_handling_scaler * 6 / 7 + 250;
632 v += joy_ff_handling_scaler * 45 - 500;
636 coeff = (short)(32767.0f * (v / 10000.0f));
638 for (int i = 0; i < SDL_HapticNumAxes(haptic); i++) {
639 pSpring.eff.condition.right_coeff[i] = coeff;
640 pSpring.eff.condition.left_coeff[i] = coeff;
643 // nprintf(("Joystick", "FF: New handling force = 0x%04x\n", coeff));
645 joy_ff_update_effect(&pSpring, "pSpring");
648 static int joy_ff_effect_playing(haptic_effect_t *eff)
650 if ( !eff->loaded ) {
653 return (SDL_HapticGetEffectStatus(haptic, eff->id) > 0);
659 if ( !joy_ff_can_play() ) {
663 if ( !pDock.loaded ) {
667 SDL_HapticStopEffect(haptic, pDock.id);
669 pDock.eff.periodic.magnitude = 0x3332;
671 joy_ff_update_effect(&pDock, "pDock");
673 joy_ff_start_effect(&pDock, "Dock");
676 void joy_ff_play_reload_effect()
678 if ( !joy_ff_can_play() ) {
682 if ( !pDock.loaded ) {
686 SDL_HapticStopEffect(haptic, pDock.id);
688 pDock.eff.periodic.magnitude = 0x1999;
690 joy_ff_update_effect(&pDock, "pDock (reload)");
692 joy_ff_start_effect(&pDock, "Dock (Reload)");
695 void joy_ff_afterburn_on()
697 if ( !joy_ff_can_play() ) {
701 if (Joy_ff_afterburning) {
705 if ( !(pAfterburn1.loaded && pAfterburn2.loaded) ) {
709 SDL_HapticStopEffect(haptic, pAfterburn1.id);
711 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
712 pAfterburn1.eff.periodic.magnitude = 0x3332;
714 joy_ff_update_effect(&pAfterburn1, "pAfterburn1");
716 SDL_HapticStopEffect(haptic, pAfterburn2.id);
718 pAfterburn2.eff.periodic.length = SDL_HAPTIC_INFINITY;
719 pAfterburn2.eff.periodic.magnitude = 0x1999;
721 joy_ff_update_effect(&pAfterburn2, "pAfterburn2");
723 joy_ff_start_effect(&pAfterburn1, "Afterburn1");
724 joy_ff_start_effect(&pAfterburn2, "Afterburn2");
726 // nprintf(("Joystick", "FF: Afterburn started\n"));
728 Joy_ff_afterburning = 1;
731 void joy_ff_afterburn_off()
733 if ( !joy_ff_can_play() ) {
737 if ( !Joy_ff_afterburning ) {
741 if (pAfterburn1.loaded) {
742 SDL_HapticStopEffect(haptic, pAfterburn1.id);
745 if (pAfterburn2.loaded) {
746 SDL_HapticStopEffect(haptic, pAfterburn2.id);
749 Joy_ff_afterburning = 0;
751 // nprintf(("Joystick", "FF: Afterburn stopped\n"));
754 void joy_ff_explode()
756 if ( !joy_ff_can_play() ) {
760 if (pAfterburn1.loaded) {
761 SDL_HapticStopEffect(haptic, pAfterburn1.id);
764 if (pAfterburn2.loaded) {
765 SDL_HapticStopEffect(haptic, pAfterburn2.id);
768 if (pShootEffect.loaded) {
769 SDL_HapticStopEffect(haptic, pShootEffect.id);
771 pShootEffect.eff.periodic.direction.dir[0] = 9000;
772 pShootEffect.eff.periodic.length = 500;
773 pShootEffect.eff.periodic.magnitude = 0x7FFF;
774 pShootEffect.eff.periodic.fade_length = 500;
776 joy_ff_update_effect(&pShootEffect, "pShootEffect (explode)");
778 joy_ff_start_effect(&pShootEffect, "ShootEffect (Explode)");
779 } else if (Joy_rumble) {
780 static int rumble_timeout = 1;
782 if ( timestamp_elapsed(rumble_timeout) ) {
783 SDL_HapticRumblePlay(haptic, 1.0f, 500);
785 rumble_timeout = timestamp(500);
789 Joy_ff_afterburning = 0;
792 void joy_ff_fly_by(int mag)
796 if ( !joy_ff_can_play() ) {
800 if (Joy_ff_afterburning) {
804 if ( !(pAfterburn1.loaded && pAfterburn2.loaded) ) {
808 gain = mag * 120 + 4000;
812 SDL_HapticStopEffect(haptic, pAfterburn1.id);
814 pAfterburn1.eff.periodic.length = (6000 * mag + 400000) / 1000;
815 pAfterburn1.eff.periodic.magnitude = (Sint16)(26212.0f * (gain / 10000.0f));
817 joy_ff_update_effect(&pAfterburn1, "pAfterburn1 (flyby)");
819 SDL_HapticStopEffect(haptic, pAfterburn2.id);
821 pAfterburn2.eff.periodic.length = (6000 * mag + 400000) / 1000;
822 pAfterburn2.eff.periodic.magnitude = (Sint16)(13106.0f * (gain / 10000.0f));
824 joy_ff_update_effect(&pAfterburn2, "pAfterburn2 (flyby)");
826 joy_ff_start_effect(&pAfterburn1, "Afterburn1 (Fly by)");
827 joy_ff_start_effect(&pAfterburn2, "Afterburn2 (Fly by)");
830 void joy_ff_deathroll()
832 if ( !joy_ff_can_play() ) {
836 if (pAfterburn1.loaded && pAfterburn2.loaded) {
837 SDL_HapticStopEffect(haptic, pAfterburn1.id);
839 pAfterburn1.eff.periodic.length = SDL_HAPTIC_INFINITY;
840 pAfterburn1.eff.periodic.period = 200;
841 pAfterburn1.eff.periodic.magnitude = 0x7FFF;
842 pAfterburn1.eff.periodic.attack_length = 200;
844 joy_ff_update_effect(&pAfterburn1, "pAfterburn1 (deathroll)");
846 SDL_HapticStopEffect(haptic, pAfterburn2.id);
848 pAfterburn2.eff.periodic.length = SDL_HAPTIC_INFINITY;
849 pAfterburn2.eff.periodic.period = 200;
850 pAfterburn2.eff.periodic.magnitude = 0x7FFF;
851 pAfterburn2.eff.periodic.attack_length = 200;
853 joy_ff_update_effect(&pAfterburn2, "pAfterburn2 (deathroll)");
855 joy_ff_start_effect(&pAfterburn1, "Afterburn1 (Death Roll)");
856 joy_ff_start_effect(&pAfterburn2, "Afterburn2 (Death Roll)");
858 Joy_ff_afterburning = 1;