sof-sdk/Source/Game/player/w_weapons.cpp
2000-06-15 00:00:00 +00:00

758 lines
18 KiB
C++

#include "q_shared.h"
#include "..\ghoul\ighoul.h"
#include "w_public.h"
#include "w_types.h"
#include "w_weapons.h"
#include "w_utils.h"
#include "player.h"
#include "../gamecpp/game.h"
extern player_com_import_t picom;
extern player_cl_import_t picl;
extern player_sv_import_t pisv;
extern int isClient;
extern int fireEvent;
extern int altfireEvent;
// noiserad does NOT belong here...
attackInfoExtra_t attacks[] =
{
//ammo noiseRad waver waverMax kickamount AttackID
{AMMO_NONE, 0.0, 0.0, 0.0, 0.0, ATK_NOTHING}, //SFW_EMPTYSLOT
{AMMO_KNIFE, 0.0, 0.0, 0.0, 0.0, ATK_KNIFE}, //SFW_KNIFE,
{AMMO_KNIFE, 0.0, 0.0, 0.0, 0.0, ATK_KNIFE_ALT}, //SFW_KNIFE,
{AMMO_44, 900.0, 0.2, 0.8, 5.0, ATK_PISTOL2}, //SFW_PISTOL2,
{AMMO_9MM, 600.0, 0.3, 1.5, 1.0, ATK_PISTOL1}, //SFW_PISTOL1,
{AMMO_9MM, 10.0, 0.6, 3.5, 0.0, ATK_MACHINEPISTOL}, //SFW_MACHINEPISTOL,
{AMMO_556, 800.0, 0.4, 1.8, 0.0, ATK_ASSAULTRIFLE}, //SFW_ASSAULTRIFLE,
{AMMO_556, 0.0, 0.0, 0.0, 2.0, ATK_SNIPER}, //SFW_SNIPER, ->sniper rifle
{AMMO_NONE, 0.0, 0.0, 0.0, 0.0, ATK_SNIPER_ALT}, //SFW_SNIPER, ->sniper rifle
{AMMO_SLUG, 1200.0, 1.5, 1.5, 4.0, ATK_AUTOSHOTGUN}, //SFW_AUTOSHOTGUN, ->shotgun
{AMMO_SLUG, 0.0, 0.0, 0.0, 4.0, ATK_AUTOSHOTGUN_ALT}, //SFW_AUTOSHOTGUN, ->shotgun
{AMMO_SHELLS, 1200.0, 0.0, 0.0, 5.0, ATK_SHOTGUN}, //SFW_SHOTGUN, ->shotgun
{AMMO_556, 1500.0, 0.1, 0.4, 0.0, ATK_MACHINEGUN}, //SFW_MACHINEGUN, -> machinegun
{AMMO_556, 500.0, 0.0, 0.0, 0.0, ATK_MACHINEGUN_ALT}, //SFW_MACHINEGUN, -> machinegun
{AMMO_ROCKET, 0.0, 0.0, 0.0, 0.0, ATK_ROCKET}, //SFW_ROCKET, -> rocketlauncher
{AMMO_ROCKET, 0.0, 0.0, 0.0, 0.0, ATK_ROCKET_ALT}, //SFW_ROCKET, -> rocketlauncher
{AMMO_MWAVE, 0.0, 0.0, 0.0, 0.0, ATK_MICROWAVE}, //SFW_MICROWAVEPULSE,
{AMMO_MWAVE, 0.0, 0.0, 0.0, 0.0, ATK_MICROWAVE_ALT}, //SFW_MICROWAVEPULSE,
{AMMO_FTHROWER, 0.0, 0.0, 0.0, 0.0, ATK_FLAMEGUN}, //SFW_FLAMETHROWER,
{AMMO_FTHROWER, 0.0, 0.0, 0.0, 0.0, ATK_FLAMEGUN_ALT}, //SFW_FLAMETHROWER,
};
WeapSoundCallBack theWeapSoundCallback;
GunFireCallBack theGunFireCallback;
AltfireCallBack theAltfireCallback;
EOSCallBack theEOSCallback;
KnifeCleanCallBack theKnifeCleanCallback;
MinimiReloadCallBack theMinimiReloadCallback;
EffectCallBack theEffectCallback;
weaponInfoExtra_t extraInfo[] =
{ // name slotsNeeded clipsize attack1 attack2
{"", 0, -1, &attacks[0], &attacks[0]}, //SFW_EMPTYSLOT
{"Knife", 0, 0, &attacks[1], &attacks[2]}, //SFW_KNIFE,
{"Pistol2", 1, 9, &attacks[3], &attacks[0]}, //SFW_PISTOL2,
{"Pistol1", 1, 18, &attacks[4], &attacks[0]}, //SFW_PISTOL1,
{"MPistol", 2, 32, &attacks[5], &attacks[0]}, //SFW_MACHINEPISTOL,
{"Assaultrifle", 2, 40, &attacks[6], &attacks[0]}, //SFW_ASSAULTRIFLE,
{"SniperRifle", 2, 6, &attacks[7], &attacks[8]}, //SFW_SNIPER, ->sniper rifle
{"Autoshotgun", 2, 10, &attacks[9], &attacks[10]}, //SFW_AUTOSHOTGUN, ->shotgun
{"Shotgun", 2, 8, &attacks[11], &attacks[0]}, //SFW_SHOTGUN, ->shotgun
{"Machinegun", 2, 40, &attacks[12], &attacks[13]}, //SFW_MACHINEGUN, -> machinegun
{"Rocket", 3, 4, &attacks[14], &attacks[15]}, //SFW_ROCKET, -> rocketlauncher
{"MPG", 3, 30, &attacks[16], &attacks[17]}, //SFW_MICROWAVEPULSE,
{"Flamegun", 3, 60, &attacks[18], &attacks[19]}, //SFW_FLAMETHROWER,
{"Hurthand", 0, 0, &attacks[0], &attacks[0]}, //SFW_HURTHAND,
{"Throwhand", 0, 0, &attacks[0], &attacks[0]}, //SFW_THROWHAND,
};
weaponInfo_c noweap(&extraInfo[0]);
knifeInfo knife(&extraInfo[1]);
pistol2Info eagle(&extraInfo[2]);
pistol1Info glock(&extraInfo[3]);
machinePistolInfo ingram(&extraInfo[4]);
assaultRifleInfo hk53(&extraInfo[5]);
sniperInfo sig(&extraInfo[6]);
autoshotgunInfo jhammer(&extraInfo[7]);
shotgunInfo spas(&extraInfo[8]);
machinegunInfo minimi(&extraInfo[9]);
rocketInfo m202a2(&extraInfo[10]);
microInfo micro(&extraInfo[11]);
flamegunInfo fthrow(&extraInfo[12]);
hurthandInfo hhand(&extraInfo[13]);
throwhandInfo thand(&extraInfo[14]);
weaponInfo_c *weapInfo[SFW_NUM_WEAPONS] =
{
&noweap,
&knife,
&eagle,
&glock,
&ingram,
&hk53,
&sig,
&jhammer,
&spas,
&minimi,
&m202a2,
&micro,
&fthrow,
&hhand,
&thand,
};
weaponInfo_c::weaponInfo_c(weaponInfoExtra_t *w)
{
clip = w->clipsize;
slotsNeeded = w->slotsNeeded;
ammoType[0] = w->a1->ammoType;
ammoType[1] = w->a2->ammoType;
noiseRad[0] = w->a1->noiseRad;
noiseRad[1] = w->a2->noiseRad;
waver[0] = w->a1->waver;
waver[1] = w->a2->waver;
waverMax[0] = w->a1->waverMax;
waverMax[1] = w->a2->waverMax;
kick[0] = w->a1->kickAmount;
kick[1] = w->a2->kickAmount;
strcpy(modelName, w->name);
atkID[0] = w->a1->id;
atkID[1] = w->a2->id;
soundToken = NULL_GhoulID;
}
int weaponInfo_c::useAmmo1(sharedEdict_t &ent, int *skipIdle)
{
weapon_c *weap = inven(ent)->getCurWeapon();
if(getAmmoType(0) == AMMO_NONE)
return 1;
if(!inven(ent)->rulesDoWeaponsUseAmmo())
return 1;
if(!weap->getClip())
{
if(inven(ent)->rulesIsWeaponReloadAutomatic())
inven(ent)->reload();
else
inven(ent)->dryfire();
return 0;
}
weap->setClip(weap->getClip() - 1);
return 1;
}
int weaponInfo_c::useAmmo2(sharedEdict_t &ent, int *skipIdle)
{
weapon_c *weap = inven(ent)->getCurWeapon();
if(getAmmoType(1) == AMMO_NONE)
return 1;
if(!inven(ent)->rulesDoWeaponsUseAmmo())
return 1;
if(!weap->getClip())
{
if(inven(ent)->rulesIsWeaponReloadAutomatic())
inven(ent)->reload();
else
inven(ent)->dryfire();
return 0;
}
weap->setClip(weap->getClip() - 1);
return 1;
}
int weaponInfo_c::checkReloadAtEOS(sharedEdict_t &ent)
{
if((wAnim(ent)->getAnimType() == WANIM_FIRE)||(wAnim(ent)->getAnimType() == WANIM_ALTFIRE))
{
if(!inven(ent)->rulesDoWeaponsUseAmmo())
return 0;
if(!inven(ent)->getCurClip())
{
if(inven(ent)->rulesIsWeaponReloadAutomatic())
{
inven(ent)->reload();
return 1;
}
else
{
return 0;
}
}
}
return 0;
}
void weaponInfo_c::precacheResources()
{
if(getAtkID(0) != ATK_NOTHING)
{
pisv.CacheAttack((int)getAtkID(0));
}
if(getAtkID(1) != ATK_NOTHING)
{
pisv.CacheAttack((int)getAtkID(1));
}
}
//------------------------------------------------------------------------
weapon_c::weapon_c(void)
{
curClip = 0;
cannotFire = 0;
type = SFW_EMPTYSLOT;
}
weapon_c::weapon_c(weaponInfo_c *wInfo)
{
curClip = getWeaponInfo()->getClipSize();
cannotFire = 0;
type = SFW_EMPTYSLOT;
}
void weapon_c::setType(int newType, int startWithFullClip)
{
type = newType;
if(startWithFullClip == -1)
{
curClip = getWeaponInfo()->getClipSize();
}
else if(startWithFullClip)
{
curClip = startWithFullClip;
}
else
{
curClip = 0;
}
}
int weapon_c::handleAmmo(sharedEdict_t &ent, int mode, int *skipIdle)
{
return((mode) ? getWeaponInfo()->useAmmo2(ent, skipIdle) : getWeaponInfo()->useAmmo1(ent, skipIdle));
}
weaponInfo_c *weapon_c::getWeaponInfo(void)
{
return weapInfo[type];
}
int weapon_c::canSelect(sharedEdict_t &ent,int withNoAmmo)
{
if(type == SFW_EMPTYSLOT)
return 0;
if(isClient)
{
// Client is always able to select any weapon.
return(1);
}
if(!withNoAmmo)
{
if((curClip == 0)&&(inven(ent)->rulesDoWeaponsUseAmmo()))
{
if((inven(ent)->hasAmmo(weapInfo[type]->getAmmoType(0)) == 0)&&(!weapInfo[type]->canSelectWithNoAmmo()))
return 0;
}
}
return(1);
}
//---------------------------------------------------------------------------------------------
//
// wrapper funcs
//
//---------------------------------------------------------------------------------------------
void Fire(sharedEdict_t &sh, edict_t &ent, inven_c &inven)
{
if(!inven.getCurWeapon()->getWeaponInfo()->atMomentOfFire(sh))
return;
if(wAnim(sh)->getAnimType() != WANIM_FIRE)
return;
if(!isClient)
pisv.FireHelper(sh, ent, inven);
else
picl.FireHelper(sh, ent, inven);
sh.weaponkick_angles[PITCH]=-(inven.getCurWeapon()->getWeaponInfo()->getKick(0));
}
void Altfire(sharedEdict_t &sh, edict_t &ent, inven_c &inven)
{
if(!inven.getCurWeapon()->getWeaponInfo()->atMomentOfFire(sh))
return;
if(wAnim(sh)->getAnimType() != WANIM_ALTFIRE)
return;
if(!isClient)
pisv.AltfireHelper(sh, ent, inven);
else
picl.AltfireHelper(sh, ent, inven);
sh.weaponkick_angles[PITCH]=-(inven.getCurWeapon()->getWeaponInfo()->getKick(1));
}
void EOS(sharedEdict_t &ent, inven_c &inven)
{
if(inven.getCurWeapon()->getWeaponInfo()->handleEOS(ent))
{
// Special end of sequence stuff handled by weapon specific EOS.
return;
}
if(inven.getCurWeapon()->getWeaponInfo()->checkReloadAtEOS(ent))
{
// Do we need to reload now?
return;
}
switch(wAnim(ent)->getAnimType())
{
case WANIM_NORMAL:
case WANIM_RELOAD:
inven.idle();
break;
case WANIM_FIRE:
if(!(inven.pendingCommand()))
{
qboolean doAttack=false;
if(!isClient)
{
if(!inven.isClientPredicting())
{
if(ent.attack)
doAttack=true;
}
else
{
if((inven.getCurWeaponID()==SFW_FLAMEGUN)||
(inven.getCurWeaponID()==SFW_KNIFE)||
(inven.getCurWeaponID()==SFW_MICROWAVEPULSE))
{
// Yuck!! Sigh!!
if(ent.attack)
doAttack=true;
}
else
{
if(pisv.getFireEvent(inven.getOwner()->edict))
doAttack=true;
}
}
}
else
{
if(ent.attack)
doAttack=true;
}
if(doAttack)
{
inven.getAnimInfo()->incFireLoop();
int skipIdle = 0;
if(!inven.getCurWeapon()->handleAmmo(ent, 0, &skipIdle))
{
if(!skipIdle)
{
inven.idle();
}
return;
}
if(!isClient)
{
if(inven.isClientPredicting())
{
if((inven.getCurWeaponID()==SFW_FLAMEGUN)||
(inven.getCurWeaponID()==SFW_KNIFE)||
(inven.getCurWeaponID()==SFW_ASSAULTRIFLE)||
(inven.getCurWeaponID()==SFW_MACHINEPISTOL)||
(inven.getCurWeaponID()==SFW_MICROWAVEPULSE))
{
// Yuck!! Sigh!!
RunVWeapAnim(&ent,
inven.getCurWeapon()->getWeaponInfo()->getFireSeq(ent),
inven.getCurWeapon()->getWeaponInfo()->getLoopType());
}
else
{
RunVWeapAnim(&ent,
inven.getCurWeapon()->getWeaponInfo()->getFireSeq(ent),
inven.getCurWeapon()->getWeaponInfo()->getLoopType(),
pisv.getFireEvent(inven.getOwner()->edict));
}
pisv.clearFireEvent(inven.getOwner()->edict);
}
else
{
RunVWeapAnim(&ent,
inven.getCurWeapon()->getWeaponInfo()->getFireSeq(ent),
inven.getCurWeapon()->getWeaponInfo()->getLoopType());
}
}
else
{
RunVWeapAnim(&ent,
inven.getCurWeapon()->getWeaponInfo()->getFireSeq(ent),
inven.getCurWeapon()->getWeaponInfo()->getLoopType());
fireEvent=1;
}
// Some weapons don't just loop their anim.
inven.getCurWeapon()->getWeaponInfo()->handleFireLoop(ent);
return;
}
}
inven.getAnimInfo()->clearFireLoop();
inven.idle();
break;
case WANIM_ALTFIRE:
if(!inven.pendingCommand())
{
qboolean doAltAttack=false;
if(!isClient)
{
if(!inven.isClientPredicting())
{
if(ent.altattack)
doAltAttack=true;
}
else
{
if(pisv.getAltfireEvent(inven.getOwner()->edict))
doAltAttack=true;
}
}
else
{
if(ent.altattack)
doAltAttack=true;
}
if(doAltAttack)
{
inven.getAnimInfo()->incFireLoop();
int skipIdle = 0;
if(!inven.getCurWeapon()->handleAmmo(ent, 1, &skipIdle))
{
if(!skipIdle)
{
inven.idle();
}
return;
}
if(!isClient)
{
if(inven.isClientPredicting())
{
if(inven.getCurWeaponID()==SFW_ROCKET)
{
// Yuck!! Sigh!!
RunVWeapAnim(&ent,
inven.getCurWeapon()->getWeaponInfo()->getAltFireSeq(ent),
IGhoulInst::Hold);
}
else
{
RunVWeapAnim(&ent,
inven.getCurWeapon()->getWeaponInfo()->getAltFireSeq(ent),
IGhoulInst::Hold,
pisv.getAltfireEvent(inven.getOwner()->edict));
}
pisv.clearAltfireEvent(inven.getOwner()->edict);
}
else
{
RunVWeapAnim(&ent,
inven.getCurWeapon()->getWeaponInfo()->getAltFireSeq(ent),
IGhoulInst::Hold);
}
}
else
{
RunVWeapAnim(&ent,
inven.getCurWeapon()->getWeaponInfo()->getAltFireSeq(ent),
IGhoulInst::Hold);
altfireEvent=1;
}
// Some weapons don't just loop their anim!
inven.getCurWeapon()->getWeaponInfo()->handleAltfireLoop(ent);
return;
}
}
inven.getAnimInfo()->clearFireLoop();
inven.idle();
break;
case WANIM_PUTAWAY:
// Set new weapon to take out.
inven.setPendingChange();
break;
case WANIM_LOSE:
inven.idle();
break;
}
}
//------------------------------------------------------------------------
bool WeapSoundCallBack::Execute(IGhoulInst *me,void *user,float now,const void *data)
{
inven_c *inv = (inven_c *)user;
sharedEdict_t *ent = inv->getOwner();
if(isClient)
picl.WeapSoundHelper(data);
return true;
}
bool GunFireCallBack::Execute(IGhoulInst *me,void *user,float now,const void *data)
{
inven_c *inv = (inven_c *)user;
sharedEdict_t *ent = inv->getOwner();
Fire(*ent, *ent->edict, *inv);
if (data && *((char *)data)=='1')
{ // Use ammo during this fire.
if (!inv->getCurWeapon()->getWeaponInfo()->useAmmo1(*ent))
{
EOS(*ent, *inv);
}
}
return true;
}
bool AltfireCallBack::Execute(IGhoulInst *me,void *user,float now,const void *data)
{
inven_c *inv = (inven_c *)user;
sharedEdict_t *ent = inv->getOwner();
Altfire(*ent, *ent->edict, *inv);
return true;
}
bool EOSCallBack::Execute(IGhoulInst *me,void *user,float now,const void *data)
{
inven_c *inv = (inven_c *)user;
sharedEdict_t *ent = inv->getOwner();
EOS(*ent, *inv);
return true;
}
bool KnifeCleanCallBack::Execute(IGhoulInst *me,void *user,float now,const void *data)
{
inven_c *inv = (inven_c *)user;
sharedEdict_t *ent = inv->getOwner();
inv->getCurWeapon()->getWeaponInfo()->handleSpecialNotes((char *)data, *ent);
return true;
}
bool MinimiReloadCallBack::Execute(IGhoulInst *me,void *user,float now,const void *data)
{
inven_c *inv = (inven_c *)user;
sharedEdict_t *ent = inv->getOwner();
inv->getCurWeapon()->getWeaponInfo()->handleSpecialNotes((char *)data, *ent);
return true;
}
bool EffectCallBack::Execute(IGhoulInst *me,void *user,float now,const void *data)
{
inven_c *inv = (inven_c *)user;
sharedEdict_t *ent = inv->getOwner();
if(isClient)
picl.WeaponEffectHelper(data);
return true;
}
class EffectPrecacheCallBack: public IGhoulCallBack
{
public:virtual bool Execute(IGhoulInst *me,void *user,float now,const void *data)
{
char effectstr[256], *effectname;
strcpy(effectstr, (char *)data);
effectname = strtok(effectstr, " ");
pisv.EffectIndex(effectname);
return true;
}
};
EffectPrecacheCallBack effectPrecacher;
//------------------------------------------------------------------------
class SoundPrecacheCallBack: public IGhoulCallBack
{
public:virtual bool Execute(IGhoulInst *me,void *user,float now,const void *data)
{
char buffer[256], soundname[256], *soundtok, *numtok;
int i,c;
strcpy(soundname, (char *)data);
soundtok = strtok(soundname, " ");
numtok = strtok(NULL, " ");
if (!numtok)
{
c=0;
}
else
{
c=atoi(numtok);
}
if (c>0)
{
for (i=1; i<=c; i++)
{
picom.Com_Sprintf(buffer, sizeof(buffer), "%s%d.wav", soundtok, i);
pisv.SoundIndex(buffer);
}
}
else
{
picom.Com_Sprintf(buffer, sizeof(buffer), "%s.wav", soundtok);
pisv.SoundIndex(buffer);
}
return true;
}
};
SoundPrecacheCallBack soundPrecacher;
//------------------------------------------------------------------------
void W_UncacheViewWeaponModels(void)
{
int i;
for(i = 0; i < SFW_NUM_WEAPONS; i++)
{
if(weapInfo[i]->getGhoulObj())
{
weapInfo[i]->setGhoulObj(NULL);
}
}
}
void W_PrecacheViewWeaponModels(int types)
{
// types is a bitflag of all the weapon types that should be precached
char buffer[256];
for(int i = SFW_EMPTYSLOT + 1; i < SFW_NUM_WEAPONS; i++)
{
if(!(types & (1<<i)))continue;
#ifdef _OEM_
if((i == SFW_MACHINEPISTOL)||(i == SFW_AUTOSHOTGUN)||(i == SFW_MACHINEGUN)
|| (i == SFW_ROCKET) || (i == SFW_MICROWAVEPULSE) || (i == SFW_FLAMEGUN))
{ // none of these in the oem
continue;
}
#endif
#ifdef _DEMO_
if((i == SFW_MACHINEPISTOL)||(i == SFW_AUTOSHOTGUN)||(i == SFW_MACHINEGUN)
|| (i == SFW_MICROWAVEPULSE) || (i == SFW_FLAMEGUN))
{ // none of these in the demo
continue;
}
#endif
picom.Com_Sprintf(buffer, sizeof(buffer), "Weapon/Inview/%s", extraInfo[i].name);
IGhoulObj *obj;
if(weapInfo[i]->getGhoulObj())continue;//only need to precache if this weapon isn't already loaded, of course
obj = ((IGhoul *)pisv.GetGhoul())->NewObj();
if(!obj)
{
// This is real bad - should not happen right?
assert(obj);
continue;
}
picom.RegisterGSQSequences(buffer, extraInfo[i].name, obj);
obj->RegisterEverything();
obj->PreCache(picom.ghl_specular->value>0.0f);
// Precache all the weapon sounds.
GhoulID id = obj->FindNoteToken("sound");
if(id)
{
obj->FireAllNoteCallBacks(&soundPrecacher,id);
}
// Precache all effect calls.
id = obj->FindNoteToken("effect");
if(id)
{
obj->FireAllNoteCallBacks(&effectPrecacher,id);
}
// Precache all other resources.
weapInfo[i]->setGhoulObj(obj);
weapInfo[i]->precacheResources();
}
}