mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 04:31:34 -10:00
836 lines
21 KiB
C++
836 lines
21 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* $Logfile: /Freespace2/code/Io/Joy_ff.cpp $
|
|
* $Revision: 2.5 $
|
|
* $Date: 2005-04-05 05:53:18 $
|
|
* $Author: taylor $
|
|
*
|
|
* Code for joystick Force Feedback.
|
|
*
|
|
* $Log: not supported by cvs2svn $
|
|
* Revision 2.4 2004/07/26 20:47:33 Kazan
|
|
* remove MCD complete
|
|
*
|
|
* Revision 2.3 2004/07/12 16:32:51 Kazan
|
|
* MCD - define _MCD_CHECK to use memory tracking
|
|
*
|
|
* Revision 2.2 2002/08/01 01:41:06 penguin
|
|
* The big include file move
|
|
*
|
|
* Revision 2.1 2002/07/07 19:55:59 penguin
|
|
* Back-port to MSVC
|
|
*
|
|
* Revision 2.0 2002/06/03 04:02:24 penguin
|
|
* Warpcore CVS sync
|
|
*
|
|
* Revision 1.1 2002/05/02 18:03:08 mharris
|
|
* Initial checkin - converted filenames and includes to lower case
|
|
*
|
|
*
|
|
* 4 1/06/99 2:24p Dave
|
|
* Stubs and release build fixes.
|
|
*
|
|
* 3 10/09/98 2:57p Dave
|
|
* Starting splitting up OS stuff.
|
|
*
|
|
* 2 10/07/98 10:53a Dave
|
|
* Initial checkin.
|
|
*
|
|
* 1 10/07/98 10:49a Dave
|
|
*
|
|
* 13 5/20/98 5:47p Sandeep
|
|
*
|
|
* 12 5/20/98 3:52p Allender
|
|
* fixed compiler warnings
|
|
*
|
|
* 11 5/20/98 11:06a Hoffoss
|
|
* Fixed typo.
|
|
*
|
|
* 10 5/20/98 10:57a Hoffoss
|
|
* Made directional hit effect FF a toggle in launcher, and made calibrate
|
|
* not freeze launcher until process ends.
|
|
*
|
|
* 9 5/18/98 4:53p Hoffoss
|
|
* Some force feedback tweaks and pilot initializations there should have
|
|
* been happening, but weren't, and not are!
|
|
*
|
|
* 8 5/17/98 6:28p Hoffoss
|
|
* Added some stuff.
|
|
*
|
|
* 7 5/17/98 5:45p Hoffoss
|
|
* Adjusted ship handling Force Feedback to not be as strong. Poor
|
|
* Sandeep's wrist can't handle it. :)
|
|
*
|
|
* 6 5/08/98 5:31p Hoffoss
|
|
* Isolated the joystick force feedback code more from dependence on other
|
|
* libraries.
|
|
*
|
|
* 5 5/08/98 12:27p Hoffoss
|
|
* Polished up the error handling and such a bit.
|
|
*
|
|
* 4 5/08/98 9:54a Hoffoss
|
|
* Generalized some of the effect stuff to try some swapping of effects.
|
|
* (going to start next, but checking this in in case I want to revert
|
|
* back to this state).
|
|
*
|
|
* 3 5/07/98 12:24a Hoffoss
|
|
* Finished up sidewinder force feedback support.
|
|
*
|
|
* 2 5/04/98 11:08p Hoffoss
|
|
* Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
|
|
* Updated references everywhere to it.
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include "math/vecmat.h"
|
|
#include "io/sw_force.h"
|
|
#include "osapi/osregistry.h"
|
|
#include "io/joy_ff.h"
|
|
#include "osapi/osapi.h"
|
|
|
|
|
|
|
|
typedef struct {
|
|
DIEFFECT effect;
|
|
DIPERIODIC periodic_struct;
|
|
DIENVELOPE envelope_struct;
|
|
DWORD axes[2];
|
|
LONG direction[2];
|
|
GUID guid;
|
|
} di_periodic_effect_struct;
|
|
|
|
int joy_ff_handling_scaler;
|
|
int Joy_ff_enabled = 0;
|
|
int Joy_ff_directional_hit_effect_enabled = 1;
|
|
|
|
LPDIRECTINPUT pDi;
|
|
LPDIRECTINPUTDEVICE2 pDiDevice;
|
|
|
|
int joy_ff_create_effects();
|
|
void joy_ff_stop_effects();
|
|
|
|
LPDIRECTINPUTEFFECT pHitEffect1;
|
|
LPDIRECTINPUTEFFECT pHitEffect2;
|
|
LPDIRECTINPUTEFFECT pAfterburn1;
|
|
LPDIRECTINPUTEFFECT pAfterburn2;
|
|
LPDIRECTINPUTEFFECT pShootEffect;
|
|
LPDIRECTINPUTEFFECT pSecShootEffect;
|
|
LPDIRECTINPUTEFFECT pSpring;
|
|
LPDIRECTINPUTEFFECT pDock;
|
|
LPDIRECTINPUTEFFECT pDeathroll1;
|
|
LPDIRECTINPUTEFFECT pDeathroll2;
|
|
LPDIRECTINPUTEFFECT pExplode;
|
|
|
|
di_periodic_effect_struct Struct_deathroll1;
|
|
di_periodic_effect_struct Struct_deathroll2;
|
|
di_periodic_effect_struct Struct_explode;
|
|
di_periodic_effect_struct Struct_afterburn1;
|
|
di_periodic_effect_struct Struct_afterburn2;
|
|
di_periodic_effect_struct Struct_dock;
|
|
|
|
di_condition_effect_struct Spring_cond_effect;
|
|
|
|
void joy_ff_afterburn_off();
|
|
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);
|
|
|
|
int joy_ff_init()
|
|
{
|
|
int ff_enabled;
|
|
|
|
Joy_ff_enabled = 0; // Assume no force feedback
|
|
ff_enabled = os_config_read_uint(NULL, "EnableJoystickFF", 0);
|
|
|
|
if (ff_enabled) {
|
|
HRESULT hr;
|
|
TCHAR g_szOutput[256];
|
|
TCHAR szCodeString[256];
|
|
|
|
Joy_ff_directional_hit_effect_enabled = os_config_read_uint(NULL, "EnableHitEffect", 1);
|
|
|
|
hr = SWFF_OpenDefaultFFJoystick((HWND) os_get_window(), &pDi, &pDiDevice);
|
|
if (FAILED(hr)) {
|
|
nprintf(("Sandeep", "No FF On Joystick, not using FF\n"));
|
|
SWFF_ErrorCodeToString(hr, &szCodeString[0]);
|
|
wsprintf(g_szOutput, "Make sure JOYSTICKID1 has Force Feedback\n"
|
|
"Code = %lx: %s\n", hr, szCodeString);
|
|
|
|
nprintf(("Sandeep", g_szOutput));
|
|
return -1;
|
|
|
|
}
|
|
|
|
nprintf(("Sandeep", "There is FF on this joystick! (The peasants cheer)\n"));
|
|
SWFF_DestroyAllEffects(pDiDevice);
|
|
if (joy_ff_create_effects())
|
|
return -1;
|
|
Joy_ff_enabled = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void joy_ff_shutdown()
|
|
{
|
|
if (Joy_ff_enabled) {
|
|
pSpring->Stop();
|
|
joy_ff_stop_effects();
|
|
SWFF_DestroyAllEffects(pDiDevice);
|
|
pDiDevice->Unacquire();
|
|
pDiDevice->Release();
|
|
pDi->Release();
|
|
}
|
|
}
|
|
|
|
HRESULT joy_ff_handle_error(HRESULT hr, char *eff_name = NULL)
|
|
{
|
|
if (FAILED(hr)) {
|
|
TCHAR szCodeString[256];
|
|
|
|
SWFF_ErrorCodeToString(hr, szCodeString);
|
|
if (eff_name)
|
|
nprintf(("Joystick", "FF: Error for %s: %s\n", eff_name, szCodeString));
|
|
else
|
|
nprintf(("Joystick", "FF: Error: %s\n", szCodeString));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
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)
|
|
{
|
|
joy_ff_handle_error(SWFF_CreatePeriodicEffect(pDiDevice, eff, type, dur, per, ang, mag, 0, att, 0, fade, 0, -1));
|
|
if (!*eff)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void joy_ff_start_effect(LPDIRECTINPUTEFFECT eff, char *name)
|
|
{
|
|
HRESULT hr;
|
|
|
|
nprintf(("Joystick", "FF: Starting effect %s\n", name));
|
|
hr = joy_ff_handle_error(eff->Start(1, 0));
|
|
if (hr == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(eff->Start(1, 0));
|
|
}
|
|
}
|
|
|
|
int joy_ff_create_effects()
|
|
{
|
|
joy_ff_handle_error(SWFF_CreateConstantForceEffect(
|
|
pDiDevice,
|
|
&pHitEffect1,
|
|
300000, // Duration
|
|
0, // Angle
|
|
10000, // Magnitude
|
|
0, // Attack time
|
|
10000, // Attack level
|
|
120000, // Fade time
|
|
1, // Fade level
|
|
-1), "HitEffect1");
|
|
|
|
if (pHitEffect1)
|
|
nprintf(("Joystick", "FF: Hit effect 1 loaded\n"));
|
|
else { // bail out early if we can't even load this (because rest probably won't either and failing is slow.
|
|
nprintf(("Joystick", "FF: Hit effect 1 failed to load\n"));
|
|
return -1;
|
|
}
|
|
|
|
joy_ff_create_std_periodic(
|
|
&pHitEffect2,
|
|
SINE,
|
|
300000, // Duration
|
|
100000, // Period
|
|
9000, // Angle
|
|
10000, // Magnitude
|
|
100000, // Attack time
|
|
100000); // Fade time
|
|
|
|
if (pHitEffect2)
|
|
nprintf(("Joystick", "FF: Hit effect 2 loaded\n"));
|
|
else
|
|
nprintf(("Joystick", "FF: Hit effect 2 failed to load\n"));
|
|
|
|
joy_ff_handle_error(SWFF_CreatePeriodicEffect(
|
|
pDiDevice,
|
|
&pShootEffect,
|
|
SAWTOOTH_DOWN,
|
|
160000, // Duration
|
|
20000, // Period
|
|
0, // Angle
|
|
10000, // Magnitude
|
|
0,
|
|
0,
|
|
0,
|
|
120000,
|
|
0, -1), "ShootEffect");
|
|
|
|
if (pShootEffect)
|
|
nprintf(("Joystick", "FF: Fire primary effect loaded\n"));
|
|
else
|
|
nprintf(("Joystick", "FF: Fire primary effect failed to load\n"));
|
|
|
|
joy_ff_handle_error(SWFF_CreateConstantForceEffect(
|
|
pDiDevice,
|
|
&pSecShootEffect,
|
|
200000, // Duration
|
|
0, // Angle
|
|
10000, // Magnitude
|
|
50000, // Attack time
|
|
10000, // Attack level
|
|
100000, // Fade time
|
|
1, // Fade level
|
|
-1), "SecShootEffect");
|
|
|
|
if (pSecShootEffect)
|
|
nprintf(("Joystick", "FF: Fire Secondary effect loaded\n"));
|
|
else
|
|
nprintf(("Joystick", "FF: Fire Secondary effect failed to load\n"));
|
|
|
|
joy_ff_handle_error(SWFF_CreateConditionEffectStruct(&Spring_cond_effect,
|
|
pDiDevice,
|
|
&pSpring,
|
|
SPRING,
|
|
INFINITE, // uS
|
|
100, // X Coefficient
|
|
0, // X Offset
|
|
100, // Y Coefficient
|
|
0, // Y Offset
|
|
-1), // button play mask
|
|
"Spring");
|
|
|
|
if (pSpring) {
|
|
nprintf(("Joystick", "FF: Spring effect loaded\n"));
|
|
joy_ff_start_effect(pSpring, "Spring");
|
|
|
|
} else
|
|
nprintf(("Joystick", "FF: Spring effect failed to load\n"));
|
|
|
|
init_periodic_effect_struct(
|
|
&Struct_afterburn1,
|
|
SINE,
|
|
INFINITE, // Duration
|
|
20000, // Period
|
|
0, // Angle
|
|
8000); // Magnitude
|
|
|
|
pDiDevice->CreateEffect(Struct_afterburn1.guid, &Struct_afterburn1.effect, &pAfterburn1, NULL);
|
|
if (pAfterburn1)
|
|
nprintf(("Joystick", "FF: Afterburner effect 1 loaded\n"));
|
|
else
|
|
nprintf(("Joystick", "FF: Afterburner effect 1 failed to load\n"));
|
|
|
|
init_periodic_effect_struct(
|
|
&Struct_afterburn2,
|
|
SINE,
|
|
INFINITE, // Duration
|
|
120000, // Period
|
|
9000, // Angle
|
|
4400); // Magnitude
|
|
|
|
pDiDevice->CreateEffect(Struct_afterburn2.guid, &Struct_afterburn2.effect, &pAfterburn2, NULL);
|
|
if (pAfterburn2)
|
|
nprintf(("Joystick", "FF: Afterburner effect 2 loaded\n"));
|
|
else
|
|
nprintf(("Joystick", "FF: Afterburner effect 2 failed to load\n"));
|
|
|
|
init_periodic_effect_struct(
|
|
&Struct_dock,
|
|
SQUARE_HIGH,
|
|
125000, // Duration
|
|
100000, // Period
|
|
9000, // Angle
|
|
4000); // Magnitude
|
|
|
|
pDiDevice->CreateEffect(Struct_dock.guid, &Struct_dock.effect, &pDock, NULL);
|
|
if (pDock)
|
|
nprintf(("Joystick", "FF: Dock effect loaded\n"));
|
|
else
|
|
nprintf(("Joystick", "FF: Dock effect failed to load\n"));
|
|
|
|
init_periodic_effect_struct(
|
|
&Struct_explode,
|
|
SAWTOOTH_DOWN,
|
|
500000, // Duration
|
|
20000, // Period
|
|
9000, // Angle
|
|
10000, // Magnitude
|
|
0, // Attack time
|
|
500000); // Fade time
|
|
|
|
pDiDevice->CreateEffect(Struct_explode.guid, &Struct_explode.effect, &pExplode, NULL);
|
|
if (pExplode)
|
|
nprintf(("Joystick", "FF: Explosion effect loaded\n"));
|
|
else
|
|
nprintf(("Joystick", "FF: Explosion effect failed to load\n"));
|
|
|
|
init_periodic_effect_struct(
|
|
&Struct_deathroll1,
|
|
SINE,
|
|
INFINITE, // Duration
|
|
200000, // Period
|
|
0, // Angle
|
|
10000, // Magnitude
|
|
2000000, // Attack time
|
|
0); // Fade time
|
|
|
|
pDiDevice->CreateEffect(Struct_deathroll1.guid, &Struct_deathroll1.effect, &pDeathroll1, NULL);
|
|
if (pDeathroll1)
|
|
nprintf(("Joystick", "FF: Deathroll effect 1 loaded\n"));
|
|
else
|
|
nprintf(("Joystick", "FF: Deathroll effect 1 failed to load\n"));
|
|
|
|
init_periodic_effect_struct(
|
|
&Struct_deathroll2,
|
|
SINE,
|
|
INFINITE, // Duration
|
|
200000, // Period
|
|
9000, // Angle
|
|
10000, // Magnitude
|
|
2000000, // Attack time
|
|
0); // Fade time
|
|
|
|
pDiDevice->CreateEffect(Struct_deathroll2.guid, &Struct_deathroll2.effect, &pDeathroll2, NULL);
|
|
if (pDeathroll2)
|
|
nprintf(("Joystick", "FF: Deathroll effect 2 loaded\n"));
|
|
else
|
|
nprintf(("Joystick", "FF: Deathroll effect 2 failed to load\n"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
void joy_ff_stop_effects()
|
|
{
|
|
joy_ff_afterburn_off();
|
|
}
|
|
|
|
void joy_ff_mission_init(vec3d v)
|
|
{
|
|
v.xyz.z = 0.0f;
|
|
// joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) - 1.3f) * 10.5f);
|
|
joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
|
|
// joy_ff_handling_scaler = (int) (vm_vec_mag(&v) * 7.5f);
|
|
}
|
|
|
|
void joy_ff_adjust_handling(int speed)
|
|
{
|
|
int v;
|
|
|
|
v = speed * joy_ff_handling_scaler * 2 / 3;
|
|
// v += joy_ff_handling_scaler * joy_ff_handling_scaler * 6 / 7 + 250;
|
|
v += joy_ff_handling_scaler * 45 - 500;
|
|
if (v > 10000)
|
|
v = 10000;
|
|
|
|
if (pSpring) {
|
|
if (Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient != v) {
|
|
HRESULT hr;
|
|
|
|
Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient = v;
|
|
Spring_cond_effect.DIConditionStruct[0].lNegativeCoefficient = v;
|
|
Spring_cond_effect.DIConditionStruct[1].lPositiveCoefficient = v;
|
|
Spring_cond_effect.DIConditionStruct[1].lNegativeCoefficient = v;
|
|
nprintf(("Joystick", "FF: New handling force = %d\n", v));
|
|
|
|
hr = joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
|
|
if (hr == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void joy_ff_change_effect(di_periodic_effect_struct *s, LPDIRECTINPUTEFFECT eff, int gain = -1, int dur = 0, int flags = -1)
|
|
{
|
|
int reload = 0;
|
|
|
|
if ((gain >= 0) && ((int) s->effect.dwGain != gain)) {
|
|
s->effect.dwGain = gain;
|
|
nprintf(("Joystick", "FF: Gain reset to %d\n", gain));
|
|
reload = 1;
|
|
}
|
|
|
|
if (dur && ((int) s->effect.dwDuration != dur)) {
|
|
s->effect.dwDuration = dur;
|
|
nprintf(("Joystick", "FF: Duration reset to %d\n", dur));
|
|
reload = 1;
|
|
}
|
|
|
|
if (flags == -1) {
|
|
flags = DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_DIRECTION | DIEP_ENVELOPE | DIEP_TYPESPECIFICPARAMS;
|
|
nprintf(("Joystick", "FF: Doing full reload of effect\n"));
|
|
}
|
|
|
|
if (flags != (DIEP_DURATION | DIEP_GAIN))
|
|
reload = 1;
|
|
|
|
if (reload) {
|
|
HRESULT hr;
|
|
|
|
nprintf(("Joystick", "FF: Swapping in a new effect\n"));
|
|
hr = joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
|
|
if (hr == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
|
|
}
|
|
|
|
} else
|
|
nprintf(("Joystick", "FF: Swap effect requested, but nothing changed\n"));
|
|
}
|
|
|
|
int joy_ff_effect_playing(LPDIRECTINPUTEFFECT eff)
|
|
{
|
|
DWORD flags;
|
|
|
|
eff->GetEffectStatus(&flags);
|
|
return (flags & DIEGES_PLAYING);
|
|
}
|
|
|
|
void joy_ff_docked()
|
|
{
|
|
if (pDock) {
|
|
pDock->Stop();
|
|
if (joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock") == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock");
|
|
}
|
|
|
|
joy_ff_start_effect(pDock, "Dock");
|
|
}
|
|
}
|
|
|
|
void joy_ff_play_reload_effect()
|
|
{
|
|
if (pDock) {
|
|
pDock->Stop();
|
|
if (joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)") == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)");
|
|
}
|
|
|
|
joy_ff_start_effect(pDock, "Dock (Reload)");
|
|
}
|
|
}
|
|
|
|
int Joy_ff_afterburning = 0;
|
|
|
|
void joy_ff_afterburn_on()
|
|
{
|
|
if (pAfterburn1) {
|
|
pAfterburn1->Stop();
|
|
joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
|
|
joy_ff_start_effect(pAfterburn1, "Afterburn1");
|
|
}
|
|
|
|
if (pAfterburn2) {
|
|
pAfterburn2->Stop();
|
|
joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
|
|
joy_ff_start_effect(pAfterburn2, "Afterburn2");
|
|
}
|
|
|
|
nprintf(("Joystick", "FF: Afterburn started\n"));
|
|
Joy_ff_afterburning = 1;
|
|
}
|
|
|
|
void joy_ff_afterburn_off()
|
|
{
|
|
if (!Joy_ff_afterburning)
|
|
return;
|
|
|
|
if (pAfterburn1) {
|
|
pAfterburn1->Stop();
|
|
}
|
|
|
|
if (pAfterburn2) {
|
|
pAfterburn2->Stop();
|
|
}
|
|
|
|
Joy_ff_afterburning = 0;
|
|
nprintf(("Joystick", "FF: Afterburn stopped\n"));
|
|
}
|
|
|
|
void joy_ff_deathroll()
|
|
{
|
|
if (pDeathroll1) {
|
|
pDeathroll1->Stop();
|
|
joy_ff_start_effect(pDeathroll1, "Deathroll1");
|
|
}
|
|
|
|
if (pDeathroll2) {
|
|
pDeathroll2->Stop();
|
|
joy_ff_start_effect(pDeathroll2, "Deathroll2");
|
|
}
|
|
}
|
|
|
|
void joy_ff_explode()
|
|
{
|
|
if (pDeathroll1)
|
|
pDeathroll1->Stop();
|
|
|
|
if (pDeathroll2)
|
|
pDeathroll2->Stop();
|
|
|
|
if (pExplode) {
|
|
pExplode->Stop();
|
|
joy_ff_start_effect(pExplode, "Explode");
|
|
}
|
|
}
|
|
|
|
void joy_ff_fly_by(int mag)
|
|
{
|
|
int gain;
|
|
|
|
if (Joy_ff_afterburning)
|
|
return;
|
|
|
|
gain = mag * 120 + 4000;
|
|
if (gain > 10000)
|
|
gain = 10000;
|
|
|
|
if (pAfterburn1) {
|
|
pAfterburn1->Stop();
|
|
joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
|
|
joy_ff_start_effect(pAfterburn1, "Afterburn1 (Fly by)");
|
|
}
|
|
|
|
if (pAfterburn2) {
|
|
pAfterburn2->Stop();
|
|
joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
|
|
joy_ff_start_effect(pAfterburn2, "Afterburn2 (Fly by)");
|
|
}
|
|
}
|
|
|
|
void joy_reacquire_ff()
|
|
{
|
|
if (!Joy_ff_enabled)
|
|
return;
|
|
|
|
nprintf(("Joystick", "FF: Reacquiring\n"));
|
|
pDiDevice->Acquire();
|
|
joy_ff_start_effect(pSpring, "Spring");
|
|
}
|
|
|
|
void joy_unacquire_ff()
|
|
{
|
|
}
|
|
|
|
void joy_ff_play_dir_effect(float x, float y)
|
|
{
|
|
int idegs, imag;
|
|
float degs;
|
|
|
|
if (!Joy_ff_enabled)
|
|
return;
|
|
|
|
if (!pHitEffect1 || !pHitEffect2)
|
|
return;
|
|
|
|
if (joy_ff_effect_playing(pHitEffect1) || joy_ff_effect_playing(pHitEffect2)) {
|
|
nprintf(("Joystick", "FF: HitEffect already playing. Skipping\n"));
|
|
return;
|
|
}
|
|
|
|
if (Joy_ff_directional_hit_effect_enabled) {
|
|
if (x > 8000.0f)
|
|
x = 8000.0f;
|
|
else if (x < -8000.0f)
|
|
x = -8000.0f;
|
|
|
|
if (y > 8000.0f)
|
|
y = 8000.0f;
|
|
else if (y < -8000.0f)
|
|
y = -8000.0f;
|
|
|
|
imag = (int) fl_sqrt(x * x + y * y);
|
|
if (imag > 10000)
|
|
imag = 10000;
|
|
|
|
degs = (float)atan2(x, y);
|
|
idegs = (int) (degs * 18000.0f / PI) + 90;
|
|
while (idegs < 0)
|
|
idegs += 36000;
|
|
|
|
while (idegs >= 36000)
|
|
idegs -= 36000;
|
|
|
|
if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1") == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1");
|
|
}
|
|
|
|
idegs += 9000;
|
|
if (idegs >= 36000)
|
|
idegs -= 36000;
|
|
|
|
if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2") == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2");
|
|
}
|
|
}
|
|
|
|
joy_ff_start_effect(pHitEffect1, "HitEffect1");
|
|
joy_ff_start_effect(pHitEffect2, "HitEffect2");
|
|
//nprintf(("Joystick", "FF: Dir: %d, Mag = %d\n", idegs, imag));
|
|
}
|
|
|
|
void joy_ff_play_vector_effect(vec3d *v, float scaler)
|
|
{
|
|
vec3d vf;
|
|
float x, y;
|
|
|
|
nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->xyz.x, v->xyz.y, v->xyz.z, scaler));
|
|
vm_vec_copy_scale(&vf, v, scaler);
|
|
x = vf.xyz.x;
|
|
vf.xyz.x = 0.0f;
|
|
|
|
if (vf.xyz.y + vf.xyz.z < 0)
|
|
y = -vm_vec_mag(&vf);
|
|
else
|
|
y = vm_vec_mag(&vf);
|
|
|
|
joy_ff_play_dir_effect(-x, -y);
|
|
}
|
|
|
|
static int secondary_ff_level = 0;
|
|
|
|
void joy_ff_play_secondary_shoot(int gain)
|
|
{
|
|
if (!Joy_ff_enabled)
|
|
return;
|
|
|
|
if (!pSecShootEffect)
|
|
return;
|
|
|
|
gain = gain * 100 + 2500;
|
|
if (gain > 10000)
|
|
gain = 10000;
|
|
|
|
if (gain != secondary_ff_level) {
|
|
if (joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect") == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect");
|
|
}
|
|
|
|
if (joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect") == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect");
|
|
}
|
|
|
|
secondary_ff_level = gain;
|
|
nprintf(("Joystick", "FF: Secondary force = %d\n", gain));
|
|
}
|
|
|
|
pSecShootEffect->Stop();
|
|
joy_ff_start_effect(pSecShootEffect, "SecShootEffect");
|
|
}
|
|
|
|
static int primary_ff_level = 0;
|
|
|
|
void joy_ff_play_primary_shoot(int gain)
|
|
{
|
|
if (!Joy_ff_enabled)
|
|
return;
|
|
|
|
if (!pShootEffect)
|
|
return;
|
|
|
|
if (gain > 10000)
|
|
gain = 10000;
|
|
|
|
if (gain != primary_ff_level) {
|
|
if (joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect") == DIERR_INPUTLOST) {
|
|
joy_reacquire_ff();
|
|
joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect");
|
|
}
|
|
|
|
primary_ff_level = gain;
|
|
}
|
|
|
|
pShootEffect->Stop();
|
|
joy_ff_start_effect(pShootEffect, "ShootEffect");
|
|
}
|
|
|
|
void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang, int mag, int att, int fade)
|
|
{
|
|
// type-specific stuff
|
|
DWORD dwPhase = 0;
|
|
GUID guid = GUID_Square;
|
|
|
|
switch (type) {
|
|
case SINE:
|
|
guid = GUID_Sine;
|
|
break;
|
|
case COSINE:
|
|
guid = GUID_Sine;
|
|
dwPhase = 9000;
|
|
break;
|
|
case SQUARE_HIGH:
|
|
guid = GUID_Square;
|
|
break;
|
|
case SQUARE_LOW:
|
|
guid = GUID_Square;
|
|
dwPhase = 18000;
|
|
break;
|
|
case TRIANGLE_UP:
|
|
guid = GUID_Triangle;
|
|
break;
|
|
case TRIANGLE_DOWN:
|
|
guid = GUID_Triangle;
|
|
dwPhase = 18000;
|
|
break;
|
|
case SAWTOOTH_UP:
|
|
guid = GUID_SawtoothUp;
|
|
break;
|
|
case SAWTOOTH_DOWN:
|
|
guid = GUID_SawtoothDown;
|
|
break;
|
|
default:
|
|
Int3(); // illegal
|
|
break;
|
|
}
|
|
|
|
effect->guid = guid;
|
|
effect->periodic_struct.dwMagnitude = mag;
|
|
effect->periodic_struct.lOffset = 0;
|
|
effect->periodic_struct.dwPhase = dwPhase;
|
|
effect->periodic_struct.dwPeriod = per;
|
|
|
|
effect->envelope_struct.dwSize = sizeof(DIENVELOPE);
|
|
effect->envelope_struct.dwAttackTime = att;
|
|
effect->envelope_struct.dwAttackLevel = 0;
|
|
effect->envelope_struct.dwFadeTime = fade;
|
|
effect->envelope_struct.dwFadeLevel = 0;
|
|
|
|
effect->axes[0] = DIJOFS_X;
|
|
effect->axes[1] = DIJOFS_Y;
|
|
|
|
effect->direction[0] = ang;
|
|
effect->direction[1] = 0;
|
|
|
|
effect->effect.dwSize = sizeof(DIEFFECT);
|
|
effect->effect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
|
|
effect->effect.dwDuration = dur;
|
|
effect->effect.dwSamplePeriod = HZ_TO_uS(100);
|
|
effect->effect.dwGain = 10000;
|
|
effect->effect.dwTriggerButton = DIEB_NOTRIGGER;
|
|
effect->effect.dwTriggerRepeatInterval = 0;
|
|
effect->effect.cAxes = 2;
|
|
effect->effect.rgdwAxes = effect->axes;
|
|
effect->effect.rglDirection = effect->direction;
|
|
effect->effect.lpEnvelope = &effect->envelope_struct;
|
|
effect->effect.cbTypeSpecificParams = sizeof(effect->periodic_struct);
|
|
effect->effect.lpvTypeSpecificParams = &effect->periodic_struct;
|
|
}
|