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