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