519 lines
No EOL
10 KiB
C++
519 lines
No EOL
10 KiB
C++
// *****************************************************************************
|
|
// dm_arsenal.cpp
|
|
// *****************************************************************************
|
|
|
|
#include "g_local.h"
|
|
#include "dm_private.h"
|
|
#include "w_weapons.h"
|
|
#include "../strings/dm_arsenal.h"
|
|
|
|
const int DF_ARSENAL_NO_HEALTH = (1<<0);
|
|
const int DF_ARSENAL_NO_ITEMS = (1<<1);
|
|
const int DF_ARSENAL_NO_FALLING = (1<<3);
|
|
const int DF_ARSENAL_REALISTIC_DAMAGE = (1<<4);
|
|
const int DF_ARSENAL_SAME_LEVEL = (1<<5);
|
|
const int DF_ARSENAL_FORCE_RESPAWN = (1<<9);
|
|
const int DF_ARSENAL_NO_ARMOR = (1<<10);
|
|
|
|
void sendRestartPrediction(edict_t *ent);
|
|
|
|
/*
|
|
=================
|
|
getPlayerDMInfo
|
|
=================
|
|
*/
|
|
|
|
static player_dmarsenalInfo_c *getPlayerDMInfo(const edict_t *ent)
|
|
{
|
|
return((player_dmarsenalInfo_c *)ent->client->dmInfo);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SendStatusInfo
|
|
==================
|
|
*/
|
|
|
|
static void SendStatusInfo(edict_t *ent)
|
|
{
|
|
if(level.intermissiontime)
|
|
return;
|
|
|
|
if(ent->client->pers.spectator)
|
|
return;
|
|
|
|
gi.SP_Print(ent,DM_ARSENAL_LAYOUT_STATUS,ent->client->inv->countWeapons());
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SendStatusInfo2
|
|
==================
|
|
*/
|
|
|
|
static void SendStatusInfo2(edict_t *ent)
|
|
{
|
|
if(level.intermissiontime)
|
|
return;
|
|
|
|
if(ent->client->pers.spectator)
|
|
return;
|
|
|
|
ent->client->showinventory=false;
|
|
ent->client->showscores=false;
|
|
ent->client->showhelp_time=level.time+5.0;
|
|
SendStatusInfo(ent);
|
|
gi.unicast(ent,true);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::checkEvents
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::checkEvents(void)
|
|
{
|
|
if(resetGame==-1)
|
|
{
|
|
resetGame=0;
|
|
|
|
int i;
|
|
edict_t *e2;
|
|
|
|
// Reset the weapons for everyone as a new round is about to start.
|
|
|
|
setWeapons();
|
|
|
|
for (i = 1; i <= game.maxclients; i++)
|
|
{
|
|
e2 = &g_edicts[i];
|
|
|
|
if (!e2->inuse)
|
|
continue;
|
|
|
|
if (!e2->client)
|
|
continue;
|
|
|
|
assert(e2->client->inv);
|
|
|
|
sharedEdict_t sh;
|
|
sh.inv = (inven_c *)e2->client->inv;
|
|
sh.edict = e2;
|
|
sh.inv->setOwner(&sh);
|
|
e2->client->inv->deactivateCurrentWeapon();
|
|
e2->client->inv->deactivateInventory();
|
|
|
|
giveClientWeapons(*e2,true);
|
|
|
|
SendStatusInfo2(e2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::setWeapons
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::setWeapons(void)
|
|
{
|
|
int count,
|
|
bit,
|
|
tot,
|
|
max;
|
|
|
|
count=0;
|
|
tot=0;
|
|
max=SFW_NUM_WEAPONS-3;
|
|
|
|
while(1)
|
|
{
|
|
if((bit=gi.flrand(1,max+1))>max)
|
|
bit=max;
|
|
|
|
tot|=1<<(bit-1);
|
|
|
|
count=0;
|
|
|
|
for(int i=1;i<=max;i++)
|
|
{
|
|
if(tot&(1<<(i-1)))
|
|
{
|
|
weaponForSlot[count]=i;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if(count>=ARSENAL_MAXWEAPONS)
|
|
break;
|
|
}
|
|
|
|
firstToOneHit=0;
|
|
resetGame=0;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::giveClientWeapons
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::giveClientWeapons(edict_t &ent,qboolean newInv)
|
|
{
|
|
if(newInv)
|
|
{
|
|
W_InitInv(ent);
|
|
sendRestartPrediction(&ent);
|
|
}
|
|
|
|
sharedEdict_t sh;
|
|
|
|
sh.inv = (inven_c *)ent.client->inv;
|
|
sh.edict = &ent;
|
|
sh.inv->setOwner(&sh);
|
|
|
|
sh.inv->clearInv(true);
|
|
|
|
sh.inv->rulesSetWeaponsUseAmmo(0);
|
|
sh.inv->rulesSetDropWeapons(0);
|
|
sh.inv->rulesSetWeaponsReload(0);
|
|
sh.inv->rulesSetBestWeaponMode("unsafe");
|
|
sh.inv->rulesSetFreelySelectWeapon(0);
|
|
|
|
if(newInv)
|
|
{
|
|
for(int i = 0; i < ARSENAL_MAXWEAPONS; i++)
|
|
sh.inv->addWeaponType(weaponForSlot[i],0);
|
|
|
|
// Save off weapons and inventory counts.
|
|
|
|
sh.inv->buildInvString(getPlayerDMInfo((const edict_t *)&ent)->invString(),true,false,false,false);
|
|
}
|
|
else
|
|
{
|
|
// Reconstitute saved inventory.
|
|
|
|
int weaponsAvailable;
|
|
sh.inv->extractInvFromString(&weaponsAvailable,getPlayerDMInfo((const edict_t *)&ent)->invString());
|
|
}
|
|
|
|
sh.inv->addWeaponType(SFW_THROWHAND);
|
|
|
|
sh.inv->selectBestWeapon();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::levelInit
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::levelInit(void)
|
|
{
|
|
gi.SP_Register("dm_arsenal");
|
|
|
|
// Precache sounds.
|
|
|
|
gi.soundindex("dm/arsenal/bigwinner.wav");
|
|
gi.soundindex("dm/arsenal/lastweapon.wav");
|
|
|
|
// Make certain all the weapons needed during the game are precached,
|
|
// because it's possible for all the weapon types to be used during
|
|
// a game.
|
|
|
|
for(int i=SFW_KNIFE;i<=SFW_THROWHAND;i++)
|
|
{
|
|
// View weapons.
|
|
|
|
AddWeaponTypeForPrecache(i);
|
|
|
|
// Bolt-ons / pickups. Eugh... but necessary to precache.
|
|
|
|
if(Pickup *p=thePickupList.GetPickupFromType(PU_WEAPON,i))
|
|
{
|
|
edict_t *ent=G_Spawn();
|
|
I_Spawn(ent,p);
|
|
G_FreeEdict(ent);
|
|
}
|
|
}
|
|
|
|
// Determine the available weapons at the start of the game.
|
|
|
|
setWeapons();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::checkItemSpawn
|
|
==================
|
|
*/
|
|
|
|
int dmarsenal_c::checkItemSpawn(edict_t *ent,Pickup **pickup)
|
|
{
|
|
if ((*pickup)->GetPickupListIndex() == OBJ_CTF_FLAG)
|
|
{
|
|
G_FreeEdict (ent);
|
|
return(0);
|
|
}
|
|
|
|
if(dmRule_NO_HEALTH())
|
|
{
|
|
if ((*pickup)->GetType() == PU_HEALTH)
|
|
{
|
|
G_FreeEdict (ent);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
if(dmRule_NO_ITEMS())
|
|
{
|
|
if ((*pickup)->GetType() == PU_INV)
|
|
{
|
|
G_FreeEdict (ent);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
if(dmRule_NO_ARMOR())
|
|
{
|
|
if ((*pickup)->GetType() == PU_ARMOR)
|
|
{
|
|
G_FreeEdict (ent);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
if ((*pickup)->GetType() == PU_AMMO)
|
|
{
|
|
G_FreeEdict (ent);
|
|
return(0);
|
|
}
|
|
|
|
if ((*pickup)->GetType() == PU_WEAPON)
|
|
{
|
|
G_FreeEdict (ent);
|
|
return(0);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::dmRule_xxx
|
|
==================
|
|
*/
|
|
|
|
int dmarsenal_c::dmRule_NO_HEALTH(void)
|
|
{
|
|
return((int)dmflags->value&DF_ARSENAL_NO_HEALTH);
|
|
}
|
|
|
|
int dmarsenal_c::dmRule_NO_ITEMS(void)
|
|
{
|
|
return((int)dmflags->value&DF_ARSENAL_NO_ITEMS);
|
|
}
|
|
|
|
int dmarsenal_c::dmRule_NO_FALLING(void)
|
|
{
|
|
return((int)dmflags->value&DF_ARSENAL_NO_FALLING);
|
|
}
|
|
|
|
int dmarsenal_c::dmRule_REALISTIC_DAMAGE(void)
|
|
{
|
|
return((int)dmflags->value&DF_ARSENAL_REALISTIC_DAMAGE);
|
|
}
|
|
|
|
int dmarsenal_c::dmRule_SAME_LEVEL(void)
|
|
{
|
|
return((int)dmflags->value&DF_ARSENAL_SAME_LEVEL);
|
|
}
|
|
|
|
int dmarsenal_c::dmRule_FORCE_RESPAWN(void)
|
|
{
|
|
return((int)dmflags->value&DF_ARSENAL_FORCE_RESPAWN);
|
|
}
|
|
|
|
int dmarsenal_c::dmRule_NO_ARMOR(void)
|
|
{
|
|
return((int)dmflags->value&DF_ARSENAL_NO_ARMOR);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::initPlayerDMInfo
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::initPlayerDMInfo(edict_t &ent)
|
|
{
|
|
if(!ent.client->dmInfo)
|
|
ent.client->dmInfo = new player_dmarsenalInfo_c;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::clientConnect
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::clientConnect(edict_t &ent)
|
|
{
|
|
gamerules_c::clientConnect(ent);
|
|
giveClientWeapons(ent,true);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::clientDie
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::clientDie(edict_t &ent, edict_t &inflictor, edict_t &killer)
|
|
{
|
|
if(ent.client->pers.spectator)
|
|
return;
|
|
|
|
// Save off weapons and inventory counts.
|
|
|
|
sharedEdict_t sh;
|
|
|
|
sh.inv = (inven_c *)ent.client->inv;
|
|
sh.edict = &ent;
|
|
sh.inv->setOwner(&sh);
|
|
|
|
sh.inv->buildInvString(getPlayerDMInfo((const edict_t *)&ent)->invString(),true,false,false,false);
|
|
|
|
// Lose a frag?
|
|
|
|
if((!killer.client)||(&killer==&ent))
|
|
{
|
|
// I was killed by non player or killed myself.
|
|
|
|
if (&killer==&ent)
|
|
{
|
|
// the killer is me
|
|
ent.client->resp.score -= sv_suicidepenalty->value;
|
|
}
|
|
else
|
|
{
|
|
ent.client->resp.score--;
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// I was killed by another player.
|
|
|
|
int weaponsCount;
|
|
|
|
// Award my killer a frag.
|
|
|
|
killer.client->resp.score+=1;
|
|
|
|
// Telefragging, hand grenades and c4 don't count towards the big winner bonus.
|
|
|
|
if((meansOfDeath!=MOD_TELEFRAG)&&(meansOfDeath!=MOD_GRENADE)&&(meansOfDeath!=MOD_C4))
|
|
{
|
|
// My killer used their current weapon to kill me, so they should drop
|
|
// it and take out the next one.
|
|
|
|
killer.client->inv->removeCurrentWeapon();
|
|
|
|
// When counting the number of weapons, we need to subtract -1 because
|
|
// the inventory system takes a while to actually drop the weapon. In
|
|
// other words, we can't just set the current weapon slot to empty in
|
|
// removeCurrentWeapon()... and then count the weapons - that screws
|
|
// the whole weapon changing thing up. So I guess this is not the worst
|
|
// or the best hack, but re-writing the weapons code to avoid the above
|
|
// scenario just isn't worth it at this late stage.
|
|
|
|
weaponsCount=killer.client->inv->countWeapons()-1;
|
|
|
|
// Check to see how many weapons my killer has left...
|
|
|
|
if(weaponsCount==0)
|
|
{
|
|
// My killer used all his weapons up so award them a frag plus the big winner bonus.
|
|
|
|
killer.client->resp.score+=10;
|
|
gi.positioned_sound(vec3_origin,g_edicts,CHAN_BODY,gi.soundindex("dm/arsenal/bigwinner.wav"),0.6,ATTN_NONE,0);
|
|
gi.SP_Print(0,DM_ARSENAL_TEXT_BIG_WINNER,killer.s.number);
|
|
|
|
// Time to reset game!
|
|
|
|
resetGame=-1;
|
|
}
|
|
else if((!firstToOneHit)&&(weaponsCount==1))
|
|
{
|
|
// Somehas become the first player to have just one weapon left to
|
|
// use, so annouce this to everyone.
|
|
|
|
firstToOneHit=1;
|
|
gi.positioned_sound(vec3_origin,g_edicts,CHAN_BODY,gi.soundindex("dm/arsenal/lastweapon.wav"),0.6,ATTN_NONE,0);
|
|
gi.SP_Print(0,DM_ARSENAL_TEXT_LAST_WEAPON,killer.s.number);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::clientDieWeaponHandler
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::clientDieWeaponHandler(edict_t *ent)
|
|
{
|
|
sharedEdict_t sh;
|
|
|
|
sh.inv=(inven_c *)ent->client->inv;
|
|
sh.edict=ent;
|
|
sh.inv->setOwner(&sh);
|
|
sh.inv->rulesSetDropWeapons(0);
|
|
sh.inv->handlePlayerDeath();
|
|
|
|
if(IGhoulInst *gun=ent->client->ps.gun)
|
|
gun->SetAllPartsOnOff(false);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::clientHelpMessage
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::clientHelpMessage(edict_t *ent)
|
|
{
|
|
// Display my status info.
|
|
|
|
SendStatusInfo2(ent);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::clientRespawn
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::clientRespawn(edict_t &ent)
|
|
{
|
|
giveClientWeapons(ent,ent.client->pers.spectator);
|
|
|
|
// Send status update.
|
|
|
|
SendStatusInfo2(&ent);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
dmarsenal_c::clientScoreboardMessage
|
|
==================
|
|
*/
|
|
|
|
void dmarsenal_c::clientScoreboardMessage (edict_t *ent, edict_t *killer, qboolean log_file)
|
|
{
|
|
gamerules_c::clientScoreboardMessage(ent,killer,log_file);
|
|
} |