]> icculus.org git repositories - taylor/freespace2.git/blob - src/io/joy_ff.cpp
Initial revision
[taylor/freespace2.git] / src / io / joy_ff.cpp
1 /*
2  * $Logfile: /Freespace2/code/Io/Joy_ff.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Code for joystick Force Feedback.
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:09  root
11  * Initial revision
12  *
13  * 
14  * 4     1/06/99 2:24p Dave
15  * Stubs and release build fixes.
16  * 
17  * 3     10/09/98 2:57p Dave
18  * Starting splitting up OS stuff.
19  * 
20  * 2     10/07/98 10:53a Dave
21  * Initial checkin.
22  * 
23  * 1     10/07/98 10:49a Dave
24  * 
25  * 13    5/20/98 5:47p Sandeep
26  * 
27  * 12    5/20/98 3:52p Allender
28  * fixed compiler warnings
29  * 
30  * 11    5/20/98 11:06a Hoffoss
31  * Fixed typo.
32  * 
33  * 10    5/20/98 10:57a Hoffoss
34  * Made directional hit effect FF a toggle in launcher, and made calibrate
35  * not freeze launcher until process ends.
36  * 
37  * 9     5/18/98 4:53p Hoffoss
38  * Some force feedback tweaks and pilot initializations there should have
39  * been happening, but weren't, and not are!
40  * 
41  * 8     5/17/98 6:28p Hoffoss
42  * Added some stuff.
43  * 
44  * 7     5/17/98 5:45p Hoffoss
45  * Adjusted ship handling Force Feedback to not be as strong.  Poor
46  * Sandeep's wrist can't handle it. :)
47  * 
48  * 6     5/08/98 5:31p Hoffoss
49  * Isolated the joystick force feedback code more from dependence on other
50  * libraries.
51  * 
52  * 5     5/08/98 12:27p Hoffoss
53  * Polished up the error handling and such a bit.
54  * 
55  * 4     5/08/98 9:54a Hoffoss
56  * Generalized some of the effect stuff to try some swapping of effects.
57  * (going to start next, but checking this in in case I want to revert
58  * back to this state).
59  * 
60  * 3     5/07/98 12:24a Hoffoss
61  * Finished up sidewinder force feedback support.
62  * 
63  * 2     5/04/98 11:08p Hoffoss
64  * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
65  * Updated references everywhere to it.
66  * 
67  * $NoKeywords: $
68  */
69
70 #include "vecmat.h"
71 #include "sw_force.h"
72 #include "osregistry.h"
73 #include "joy_ff.h"
74 #include "osapi.h"
75
76 typedef struct {
77         DIEFFECT effect;
78         DIPERIODIC periodic_struct;
79         DIENVELOPE envelope_struct;
80         DWORD axes[2];
81         LONG direction[2];
82         GUID guid;
83 } di_periodic_effect_struct;
84
85 int joy_ff_handling_scaler;
86 int Joy_ff_enabled = 0;
87 int Joy_ff_directional_hit_effect_enabled = 1;
88
89 LPDIRECTINPUT pDi;
90 LPDIRECTINPUTDEVICE2 pDiDevice;
91
92 int joy_ff_create_effects();
93 void joy_ff_stop_effects();
94
95 LPDIRECTINPUTEFFECT pHitEffect1;
96 LPDIRECTINPUTEFFECT pHitEffect2;
97 LPDIRECTINPUTEFFECT pAfterburn1;
98 LPDIRECTINPUTEFFECT pAfterburn2;
99 LPDIRECTINPUTEFFECT pShootEffect;
100 LPDIRECTINPUTEFFECT pSecShootEffect;
101 LPDIRECTINPUTEFFECT pSpring;
102 LPDIRECTINPUTEFFECT pDock;
103 LPDIRECTINPUTEFFECT pDeathroll1;
104 LPDIRECTINPUTEFFECT pDeathroll2;
105 LPDIRECTINPUTEFFECT pExplode;
106
107 di_periodic_effect_struct Struct_deathroll1;
108 di_periodic_effect_struct Struct_deathroll2;
109 di_periodic_effect_struct Struct_explode;
110 di_periodic_effect_struct Struct_afterburn1;
111 di_periodic_effect_struct Struct_afterburn2;
112 di_periodic_effect_struct Struct_dock;
113
114 di_condition_effect_struct Spring_cond_effect;
115
116 void joy_ff_afterburn_off();
117 void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang = 0, int mag = 10000, int att = 0, int fade = 0);
118
119 int joy_ff_init()
120 {
121         int ff_enabled;
122
123         Joy_ff_enabled = 0;             // Assume no force feedback
124         ff_enabled = os_config_read_uint(NULL, "EnableJoystickFF", 0);
125
126         if (ff_enabled) {
127                 HRESULT hr;
128                 TCHAR g_szOutput[256];
129                 TCHAR szCodeString[256];
130
131                 Joy_ff_directional_hit_effect_enabled = os_config_read_uint(NULL, "EnableHitEffect", 1);
132
133                 hr = SWFF_OpenDefaultFFJoystick((HWND) os_get_window(), &pDi, &pDiDevice);
134                 if (FAILED(hr)) {
135                         nprintf(("Sandeep", "No FF On Joystick, not using FF\n"));
136                         SWFF_ErrorCodeToString(hr, &szCodeString[0]);
137                         wsprintf(g_szOutput, "Make sure JOYSTICKID1 has Force Feedback\n"
138                                 "Code = %lx: %s\n", hr, szCodeString);
139
140                         nprintf(("Sandeep", g_szOutput));
141                         return -1;
142                         
143                 }
144
145                 nprintf(("Sandeep", "There is FF on this joystick! (The peasants cheer)\n"));
146                 SWFF_DestroyAllEffects(pDiDevice);
147                 if (joy_ff_create_effects())
148                         return -1;
149                 Joy_ff_enabled = 1;
150         } 
151
152         return 0;
153 }
154
155 void joy_ff_shutdown()
156 {
157         if (Joy_ff_enabled) {
158                 pSpring->Stop();
159                 joy_ff_stop_effects();
160                 SWFF_DestroyAllEffects(pDiDevice);
161                 pDiDevice->Unacquire();
162                 pDiDevice->Release();
163                 pDi->Release();
164         }
165 }
166
167 HRESULT joy_ff_handle_error(HRESULT hr, char *eff_name = NULL)
168 {
169         if (FAILED(hr)) {
170                 TCHAR szCodeString[256];
171
172                 SWFF_ErrorCodeToString(hr, szCodeString);
173                 if (eff_name)
174                         nprintf(("Joystick", "FF: Error for %s: %s\n", eff_name, szCodeString));
175                 else
176                         nprintf(("Joystick", "FF: Error: %s\n", szCodeString));
177         }
178
179         return hr;
180 }
181
182 int joy_ff_create_std_periodic(LPDIRECTINPUTEFFECT *eff, int type, int dur, int per, int ang = 0, int mag = 10000, int att = 0, int fade = 0)
183 {
184         joy_ff_handle_error(SWFF_CreatePeriodicEffect(pDiDevice, eff, type, dur, per, ang, mag, 0, att, 0, fade, 0, -1));
185         if (!*eff)
186                 return -1;
187
188         return 0;
189 }
190
191 void joy_ff_start_effect(LPDIRECTINPUTEFFECT eff, char *name)
192 {
193         HRESULT hr;
194
195         nprintf(("Joystick", "FF: Starting effect %s\n", name));
196         hr = joy_ff_handle_error(eff->Start(1, 0));
197         if (hr == DIERR_INPUTLOST) {
198                 joy_reacquire_ff();
199                 joy_ff_handle_error(eff->Start(1, 0));
200         }
201 }
202
203 int joy_ff_create_effects()
204 {       
205         joy_ff_handle_error(SWFF_CreateConstantForceEffect(
206                 pDiDevice,
207                 &pHitEffect1,
208                 300000,                                         // Duration
209                 0,                                                              // Angle
210                 10000,                                          // Magnitude
211                 0,                                                              // Attack time
212                 10000,                                          // Attack level
213                 120000,                                         // Fade time
214                 1,                                                              // Fade level
215                 -1), "HitEffect1");
216
217         if (pHitEffect1)
218                 nprintf(("Joystick", "FF: Hit effect 1 loaded\n"));
219         else {  // bail out early if we can't even load this (because rest probably won't either and failing is slow.
220                 nprintf(("Joystick", "FF: Hit effect 1 failed to load\n"));
221                 return -1;
222         }
223
224         joy_ff_create_std_periodic(
225                 &pHitEffect2,
226                 SINE,
227                 300000,                                         // Duration
228                 100000,                                         // Period
229                 9000,                                                   // Angle
230                 10000,                                          // Magnitude
231                 100000,                                         // Attack time
232                 100000);                                                // Fade time
233
234         if (pHitEffect2)
235                 nprintf(("Joystick", "FF: Hit effect 2 loaded\n"));
236         else
237                 nprintf(("Joystick", "FF: Hit effect 2 failed to load\n"));
238
239         joy_ff_handle_error(SWFF_CreatePeriodicEffect(
240                 pDiDevice,
241                 &pShootEffect,
242                 SAWTOOTH_DOWN,
243                 160000,                                         // Duration
244                 20000,                                          // Period
245                 0,                                                              // Angle
246                 10000,                                          // Magnitude
247                 0,
248                 0,
249                 0,
250                 120000,
251                 0, -1), "ShootEffect");
252
253         if (pShootEffect)
254                 nprintf(("Joystick", "FF: Fire primary effect loaded\n"));
255         else
256                 nprintf(("Joystick", "FF: Fire primary effect failed to load\n"));
257
258         joy_ff_handle_error(SWFF_CreateConstantForceEffect(
259                 pDiDevice,
260                 &pSecShootEffect,
261                 200000,                                         // Duration
262                 0,                                                              // Angle
263                 10000,                                          // Magnitude
264                 50000,                                          // Attack time
265                 10000,                                          // Attack level
266                 100000,                                         // Fade time
267                 1,                                                              // Fade level
268                 -1), "SecShootEffect");
269
270         if (pSecShootEffect)
271                 nprintf(("Joystick", "FF: Fire Secondary effect loaded\n"));
272         else
273                 nprintf(("Joystick", "FF: Fire Secondary effect failed to load\n"));
274
275         joy_ff_handle_error(SWFF_CreateConditionEffectStruct(&Spring_cond_effect,
276                 pDiDevice,
277                 &pSpring,
278                 SPRING,
279                 INFINITE,                       // uS
280                 100,                                    // X Coefficient
281                 0,                                              // X Offset
282                 100,                                    // Y Coefficient
283                 0,                                              // Y Offset
284                 -1),                                    // button play mask
285                 "Spring");
286
287         if (pSpring) {
288                 nprintf(("Joystick", "FF: Spring effect loaded\n"));
289                 joy_ff_start_effect(pSpring, "Spring");
290
291         } else
292                 nprintf(("Joystick", "FF: Spring effect failed to load\n"));
293
294         init_periodic_effect_struct(
295                 &Struct_afterburn1,
296                 SINE,
297                 INFINITE,                                       // Duration
298                 20000,                                          // Period
299                 0,                                                              // Angle
300                 8000);                                          // Magnitude
301
302         pDiDevice->CreateEffect(Struct_afterburn1.guid, &Struct_afterburn1.effect, &pAfterburn1, NULL);
303         if (pAfterburn1)
304                 nprintf(("Joystick", "FF: Afterburner effect 1 loaded\n"));
305         else
306                 nprintf(("Joystick", "FF: Afterburner effect 1 failed to load\n"));
307
308         init_periodic_effect_struct(
309                 &Struct_afterburn2,
310                 SINE,
311                 INFINITE,                                       // Duration
312                 120000,                                         // Period
313                 9000,                                                   // Angle
314                 4400);                                          // Magnitude
315
316         pDiDevice->CreateEffect(Struct_afterburn2.guid, &Struct_afterburn2.effect, &pAfterburn2, NULL);
317         if (pAfterburn2)
318                 nprintf(("Joystick", "FF: Afterburner effect 2 loaded\n"));
319         else
320                 nprintf(("Joystick", "FF: Afterburner effect 2 failed to load\n"));
321
322         init_periodic_effect_struct(
323                 &Struct_dock,
324                 SQUARE_HIGH,
325                 125000,                                         // Duration
326                 100000,                                         // Period
327                 9000,                                                   // Angle
328                 4000);                                          // Magnitude
329
330         pDiDevice->CreateEffect(Struct_dock.guid, &Struct_dock.effect, &pDock, NULL);
331         if (pDock)
332                 nprintf(("Joystick", "FF: Dock effect loaded\n"));
333         else
334                 nprintf(("Joystick", "FF: Dock effect failed to load\n"));
335
336         init_periodic_effect_struct(
337                 &Struct_explode,
338                 SAWTOOTH_DOWN,
339                 500000,                                         // Duration
340                 20000,                                          // Period
341                 9000,                                                   // Angle
342                 10000,                                          // Magnitude
343                 0,                                                              // Attack time
344                 500000);                                                // Fade time
345
346         pDiDevice->CreateEffect(Struct_explode.guid, &Struct_explode.effect, &pExplode, NULL);
347         if (pExplode)
348                 nprintf(("Joystick", "FF: Explosion effect loaded\n"));
349         else
350                 nprintf(("Joystick", "FF: Explosion effect failed to load\n"));
351
352         init_periodic_effect_struct(
353                 &Struct_deathroll1,
354                 SINE,
355                 INFINITE,                                       // Duration
356                 200000,                                         // Period
357                 0,                                                              // Angle
358                 10000,                                          // Magnitude
359                 2000000,                                                // Attack time
360                 0);                                                     // Fade time
361
362         pDiDevice->CreateEffect(Struct_deathroll1.guid, &Struct_deathroll1.effect, &pDeathroll1, NULL);
363         if (pDeathroll1)
364                 nprintf(("Joystick", "FF: Deathroll effect 1 loaded\n"));
365         else
366                 nprintf(("Joystick", "FF: Deathroll effect 1 failed to load\n"));
367
368         init_periodic_effect_struct(
369                 &Struct_deathroll2,
370                 SINE,
371                 INFINITE,                                       // Duration
372                 200000,                                         // Period
373                 9000,                                                   // Angle
374                 10000,                                          // Magnitude
375                 2000000,                                                // Attack time
376                 0);                                                     // Fade time
377
378         pDiDevice->CreateEffect(Struct_deathroll2.guid, &Struct_deathroll2.effect, &pDeathroll2, NULL);
379         if (pDeathroll2)
380                 nprintf(("Joystick", "FF: Deathroll effect 2 loaded\n"));
381         else
382                 nprintf(("Joystick", "FF: Deathroll effect 2 failed to load\n"));
383
384         return 0;
385 }
386
387 void joy_ff_stop_effects()
388 {
389         joy_ff_afterburn_off();
390 }
391
392 void joy_ff_mission_init(vector v)
393 {
394         v.z = 0.0f;
395 //      joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) - 1.3f) * 10.5f);
396         joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
397 //      joy_ff_handling_scaler = (int) (vm_vec_mag(&v) * 7.5f);
398 }
399
400 void joy_ff_adjust_handling(int speed)
401 {
402         int v;
403
404         v = speed * joy_ff_handling_scaler * 2 / 3;
405 //      v += joy_ff_handling_scaler * joy_ff_handling_scaler * 6 / 7 + 250;
406         v += joy_ff_handling_scaler * 45 - 500;
407         if (v > 10000)
408                 v = 10000;
409
410         if (pSpring) {
411                 if (Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient != v) {
412                         HRESULT hr;
413
414                         Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient = v;
415                         Spring_cond_effect.DIConditionStruct[0].lNegativeCoefficient = v;
416                         Spring_cond_effect.DIConditionStruct[1].lPositiveCoefficient = v;
417                         Spring_cond_effect.DIConditionStruct[1].lNegativeCoefficient = v;
418                         nprintf(("Joystick", "FF: New handling force = %d\n", v));
419
420                         hr = joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
421                         if (hr == DIERR_INPUTLOST) {
422                                 joy_reacquire_ff();
423                                 joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
424                         }
425                 }
426         }
427 }
428
429 void joy_ff_change_effect(di_periodic_effect_struct *s, LPDIRECTINPUTEFFECT eff, int gain = -1, int dur = 0, int flags = -1)
430 {
431         int reload = 0;
432
433         if ((gain >= 0) && ((int) s->effect.dwGain != gain)) {
434                 s->effect.dwGain = gain;
435                 nprintf(("Joystick", "FF: Gain reset to %d\n", gain));
436                 reload = 1;
437         }
438
439         if (dur && ((int) s->effect.dwDuration != dur)) {
440                 s->effect.dwDuration = dur;
441                 nprintf(("Joystick", "FF: Duration reset to %d\n", dur));
442                 reload = 1;
443         }
444
445         if (flags == -1) {
446                 flags = DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_DIRECTION | DIEP_ENVELOPE | DIEP_TYPESPECIFICPARAMS;
447                 nprintf(("Joystick", "FF: Doing full reload of effect\n"));
448         }
449
450         if (flags != (DIEP_DURATION | DIEP_GAIN))
451                 reload = 1;
452
453         if (reload) {
454                 HRESULT hr;
455
456                 nprintf(("Joystick", "FF: Swapping in a new effect\n"));
457                 hr = joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
458                 if (hr == DIERR_INPUTLOST) {
459                         joy_reacquire_ff();
460                         joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
461                 }
462
463         } else
464                 nprintf(("Joystick", "FF: Swap effect requested, but nothing changed\n"));
465 }
466
467 int joy_ff_effect_playing(LPDIRECTINPUTEFFECT eff)
468 {
469         DWORD flags;
470
471         eff->GetEffectStatus(&flags);
472         return (flags & DIEGES_PLAYING);
473 }
474
475 void joy_ff_docked()
476 {
477         if (pDock) {
478                 pDock->Stop();
479                 if (joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock") == DIERR_INPUTLOST) {
480                         joy_reacquire_ff();
481                         joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock");
482                 }
483
484                 joy_ff_start_effect(pDock, "Dock");
485         }
486 }
487
488 void joy_ff_play_reload_effect()
489 {
490         if (pDock) {
491                 pDock->Stop();
492                 if (joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)") == DIERR_INPUTLOST) {
493                         joy_reacquire_ff();
494                         joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)");
495                 }
496
497                 joy_ff_start_effect(pDock, "Dock (Reload)");
498         }
499 }
500
501 int Joy_ff_afterburning = 0;
502
503 void joy_ff_afterburn_on()
504 {
505         if (pAfterburn1) {
506                 pAfterburn1->Stop();
507                 joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
508                 joy_ff_start_effect(pAfterburn1, "Afterburn1");
509         }
510
511         if (pAfterburn2) {
512                 pAfterburn2->Stop();
513                 joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
514                 joy_ff_start_effect(pAfterburn2, "Afterburn2");
515         }
516
517         nprintf(("Joystick", "FF: Afterburn started\n"));
518         Joy_ff_afterburning = 1;
519 }
520
521 void joy_ff_afterburn_off()
522 {
523         if (!Joy_ff_afterburning)
524                 return;
525
526         if (pAfterburn1) {
527                 pAfterburn1->Stop();
528         }
529
530         if (pAfterburn2) {
531                 pAfterburn2->Stop();
532         }
533
534         Joy_ff_afterburning = 0;
535         nprintf(("Joystick", "FF: Afterburn stopped\n"));
536 }
537
538 void joy_ff_deathroll()
539 {
540         if (pDeathroll1) {
541                 pDeathroll1->Stop();
542                 joy_ff_start_effect(pDeathroll1, "Deathroll1");
543         }
544
545         if (pDeathroll2) {
546                 pDeathroll2->Stop();
547                 joy_ff_start_effect(pDeathroll2, "Deathroll2");
548         }
549 }
550
551 void joy_ff_explode()
552 {
553         if (pDeathroll1)
554                 pDeathroll1->Stop();
555
556         if (pDeathroll2)
557                 pDeathroll2->Stop();
558
559         if (pExplode) {
560                 pExplode->Stop();
561                 joy_ff_start_effect(pExplode, "Explode");
562         }
563 }
564
565 void joy_ff_fly_by(int mag)
566 {
567         int gain;
568
569         if (Joy_ff_afterburning)
570                 return;
571
572         gain = mag * 120 + 4000;
573         if (gain > 10000)
574                 gain = 10000;
575
576         if (pAfterburn1) {
577                 pAfterburn1->Stop();
578                 joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
579                 joy_ff_start_effect(pAfterburn1, "Afterburn1 (Fly by)");
580         }
581
582         if (pAfterburn2) {
583                 pAfterburn2->Stop();
584                 joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
585                 joy_ff_start_effect(pAfterburn2, "Afterburn2 (Fly by)");
586         }
587 }
588
589 void joy_reacquire_ff()
590 {
591         if (!Joy_ff_enabled)
592                 return;
593
594         nprintf(("Joystick", "FF: Reacquiring\n"));
595         pDiDevice->Acquire();
596         joy_ff_start_effect(pSpring, "Spring");
597 }
598
599 void joy_unacquire_ff()
600 {
601 }
602
603 void joy_ff_play_dir_effect(float x, float y)
604 {
605         int idegs, imag;
606         float degs;
607
608         if (!Joy_ff_enabled)
609                 return;
610
611         if (!pHitEffect1 || !pHitEffect2)
612                 return;
613
614         if (joy_ff_effect_playing(pHitEffect1) || joy_ff_effect_playing(pHitEffect2)) {
615                 nprintf(("Joystick", "FF: HitEffect already playing.  Skipping\n"));
616                 return;
617         }
618
619         if (Joy_ff_directional_hit_effect_enabled) {
620                 if (x > 8000.0f)
621                         x = 8000.0f;
622                 else if (x < -8000.0f)
623                         x = -8000.0f;
624
625                 if (y > 8000.0f)
626                         y = 8000.0f;
627                 else if (y < -8000.0f)
628                         y = -8000.0f;
629
630                 imag = (int) fl_sqrt(x * x + y * y);
631                 if (imag > 10000)
632                         imag = 10000;
633
634                 degs = (float)atan2(x, y);
635                 idegs = (int) (degs * 18000.0f / PI) + 90;
636                 while (idegs < 0)
637                         idegs += 36000;
638
639                 while (idegs >= 36000)
640                         idegs -= 36000;
641
642                 if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1") == DIERR_INPUTLOST) {
643                         joy_reacquire_ff();
644                         joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1");
645                 }
646
647                 idegs += 9000;
648                 if (idegs >= 36000)
649                         idegs -= 36000;
650
651                 if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2") == DIERR_INPUTLOST) {
652                         joy_reacquire_ff();
653                         joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2");
654                 }
655         }
656
657         joy_ff_start_effect(pHitEffect1, "HitEffect1");
658         joy_ff_start_effect(pHitEffect2, "HitEffect2");
659         //nprintf(("Joystick", "FF: Dir: %d, Mag = %d\n", idegs, imag));
660 }
661
662 void joy_ff_play_vector_effect(vector *v, float scaler)
663 {
664         vector vf;
665         float x, y;
666
667         nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->x, v->y, v->z, scaler));
668         vm_vec_copy_scale(&vf, v, scaler);
669         x = vf.x;
670         vf.x = 0.0f;
671
672         if (vf.y + vf.z < 0)
673                 y = -vm_vec_mag(&vf);
674         else
675                 y = vm_vec_mag(&vf);
676
677         joy_ff_play_dir_effect(-x, -y);
678 }
679
680 static int secondary_ff_level = 0;
681
682 void joy_ff_play_secondary_shoot(int gain)
683 {
684         if (!Joy_ff_enabled)
685                 return;
686
687         if (!pSecShootEffect)
688                 return;
689
690         gain = gain * 100 + 2500;
691         if (gain > 10000)
692                 gain = 10000;
693
694         if (gain != secondary_ff_level) {
695                 if (joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect") == DIERR_INPUTLOST) {
696                         joy_reacquire_ff();
697                         joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect");
698                 }
699
700                 if (joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect") == DIERR_INPUTLOST) {
701                         joy_reacquire_ff();
702                         joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect");
703                 }
704
705                 secondary_ff_level = gain;
706                 nprintf(("Joystick", "FF: Secondary force = %d\n", gain));
707         }
708
709         pSecShootEffect->Stop();
710         joy_ff_start_effect(pSecShootEffect, "SecShootEffect");
711 }
712
713 static int primary_ff_level = 0;
714
715 void joy_ff_play_primary_shoot(int gain)
716 {
717         if (!Joy_ff_enabled)
718                 return;
719
720         if (!pShootEffect)
721                 return;
722
723         if (gain > 10000)
724                 gain = 10000;
725
726         if (gain != primary_ff_level) {
727                 if (joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect") == DIERR_INPUTLOST) {
728                         joy_reacquire_ff();
729                         joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect");
730                 }
731
732                 primary_ff_level = gain;
733         }
734
735         pShootEffect->Stop();
736         joy_ff_start_effect(pShootEffect, "ShootEffect");
737 }
738
739 void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang, int mag, int att, int fade)
740 {
741         // type-specific stuff
742         DWORD dwPhase = 0;
743         GUID guid = GUID_Square;
744
745         switch (type) {
746                 case SINE:
747                         guid = GUID_Sine;
748                         break;
749                 case COSINE:
750                         guid = GUID_Sine;
751                         dwPhase = 9000;
752                         break;
753                 case SQUARE_HIGH:
754                         guid = GUID_Square;
755                         break;
756                 case SQUARE_LOW:
757                         guid = GUID_Square;
758                         dwPhase = 18000;
759                         break;
760                 case TRIANGLE_UP:
761                         guid = GUID_Triangle;
762                         break;
763                 case TRIANGLE_DOWN:
764                         guid = GUID_Triangle;
765                         dwPhase = 18000;
766                         break;
767                 case SAWTOOTH_UP:
768                         guid = GUID_SawtoothUp;
769                         break;
770                 case SAWTOOTH_DOWN:
771                         guid = GUID_SawtoothDown;
772                         break;
773                 default:
774                         Int3();  // illegal
775                         break;
776         }
777
778         effect->guid = guid;
779         effect->periodic_struct.dwMagnitude = mag;
780         effect->periodic_struct.lOffset = 0;
781         effect->periodic_struct.dwPhase = dwPhase;
782         effect->periodic_struct.dwPeriod = per;
783
784         effect->envelope_struct.dwSize = sizeof(DIENVELOPE);
785         effect->envelope_struct.dwAttackTime = att;
786         effect->envelope_struct.dwAttackLevel = 0;
787         effect->envelope_struct.dwFadeTime = fade;
788         effect->envelope_struct.dwFadeLevel = 0;
789
790         effect->axes[0] = DIJOFS_X;
791         effect->axes[1] = DIJOFS_Y;
792
793         effect->direction[0] = ang;
794         effect->direction[1] = 0;
795
796         effect->effect.dwSize                                           = sizeof(DIEFFECT);
797         effect->effect.dwFlags                                          = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
798         effect->effect.dwDuration                                       = dur;
799         effect->effect.dwSamplePeriod                           = HZ_TO_uS(100);
800         effect->effect.dwGain                                           = 10000;
801         effect->effect.dwTriggerButton                  = DIEB_NOTRIGGER;
802         effect->effect.dwTriggerRepeatInterval  = 0;
803         effect->effect.cAxes                                                    = 2;
804         effect->effect.rgdwAxes                                         = effect->axes;
805         effect->effect.rglDirection                             = effect->direction;
806         effect->effect.lpEnvelope                                       = &effect->envelope_struct;
807         effect->effect.cbTypeSpecificParams             = sizeof(effect->periodic_struct);
808         effect->effect.lpvTypeSpecificParams    = &effect->periodic_struct;
809 }