Files
DoomRTX/neo/prey/Prey/game_inventory.cpp
2026-05-05 15:01:01 -07:00

580 lines
15 KiB
C++

#include "precompiled.h"
#pragma hdrstop
#include "prey_local.h"
void hhInventory::Clear() {
memset( &requirements, 0, sizeof( requirements ) );
maxSpirit = 0;
bHasDeathwalked = false;
storedHealth = 0;
energyType = "energy_plasma";
memset( altMode, 0, sizeof( altMode ) );
memset( weaponRaised, 0, sizeof( weaponRaised ) );
memset( lastShot, 0, sizeof( lastShot ) ); //HUMANHEAD bjk PATCH 7-27-06
zoomFov = 0;
idInventory::Clear();
}
void hhInventory::Save(idSaveGame *savefile) const {
idInventory::Save( savefile );
savefile->WriteBool(bHasDeathwalked);
savefile->Write(&maxSpirit, sizeof(maxSpirit));
savefile->Write(&requirements, sizeof(requirements));
savefile->WriteInt( storedHealth );
savefile->WriteString( energyType.c_str() );
savefile->Write(&altMode, sizeof(altMode));
savefile->Write(&weaponRaised, sizeof(weaponRaised));
savefile->WriteInt( zoomFov );
}
void hhInventory::Restore( idRestoreGame *savefile ) {
idInventory::Restore( savefile );
savefile->ReadBool(bHasDeathwalked);
savefile->Read(&maxSpirit, sizeof(maxSpirit));
savefile->Read(&requirements, sizeof(requirements));
savefile->ReadInt( storedHealth );
savefile->ReadString( energyType );
savefile->Read(&altMode, sizeof(altMode));
savefile->Read(&weaponRaised, sizeof(weaponRaised));
memset( lastShot, 0, sizeof( lastShot ) ); //HUMANHEAD bjk PATCH 7-27-06
savefile->ReadInt( zoomFov );
}
/*
==============
hhInventory::GetPersistantData
PDMMERGE PERSISTENTMERGE: Overridden, Done for 6-03-05 merge
==============
*/
void hhInventory::GetPersistantData( idDict &dict ) {
int i;
int num;
idDict *item;
idStr key;
const idKeyValue *kv;
const char *name;
// don't bother with powerups or the clip
// maxhealth, maxspirit
dict.SetInt( "maxhealth", maxHealth);
dict.SetInt( "max_ammo_spiritpower", maxSpirit);
dict.SetBool( "bHasDeathwalked", bHasDeathwalked );
dict.SetInt( "storedHealth", storedHealth );
dict.Set( "energyType", energyType.c_str() ); //HUMANHEAD bjk
dict.SetInt( "zoomFov", zoomFov ); //HUMANHEAD bjk
// ammo
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
if ( name ) {
dict.SetInt( name, ammo[ i ] );
}
}
//HUMANHEAD bjk: weapons
for( i = 0; i < MAX_WEAPONS; i++ ) {
sprintf( key, "altMode_%i", i );
dict.SetBool( key, altMode[i] );
sprintf( key, "weaponRaised_%i", i );
dict.SetBool( key, weaponRaised[i] );
}
//HUMANHEAD END
// items
num = 0;
for( i = 0; i < items.Num(); i++ ) {
item = items[ i ];
// copy all keys with "inv_"
kv = item->MatchPrefix( "inv_" );
if ( kv ) {
while( kv ) {
sprintf( key, "item_%i %s", num, kv->GetKey().c_str() );
dict.Set( key, kv->GetValue() );
kv = item->MatchPrefix( "inv_", kv );
}
// HUMANHEAD CJR: copy all keys with "def_"
// Needed for Hunter Hand GUI
kv = item->MatchPrefix( "def_" );
if ( kv ) {
while( kv ) {
sprintf( key, "item_%i %s", num, kv->GetKey().c_str() );
dict.Set( key, kv->GetValue() );
kv = item->MatchPrefix( "def_", kv );
}
} // HUMANHEAD END
// HUMANHEAD CJR: copy all keys with "passtogui_"
// Needed for Hunter Hand GUI
kv = item->MatchPrefix( "passtogui_" );
if ( kv ) {
while( kv ) {
sprintf( key, "item_%i %s", num, kv->GetKey().c_str() );
dict.Set( key, kv->GetValue() );
kv = item->MatchPrefix( "passtogui_", kv );
}
} // HUMANHEAD END
num++;
}
}
dict.SetInt( "items", num );
// weapons
dict.SetInt( "weapon_bits", weapons );
dict.SetInt( "levelTriggers", levelTriggers.Num() );
for ( i = 0; i < levelTriggers.Num(); i++ ) {
sprintf( key, "levelTrigger_Level_%i", i );
dict.Set( key, levelTriggers[i].levelName );
sprintf( key, "levelTrigger_Trigger_%i", i );
dict.Set( key, levelTriggers[i].triggerName );
}
}
/*
==============
hhInventory::RestoreInventory
PDMMERGE PERSISTENTMERGE: Overridden, Done for 6-03-05 merge
==============
*/
void hhInventory::RestoreInventory( idPlayer *owner, const idDict &dict ) {
int i;
int num;
idDict *item;
idStr key;
idStr itemname;
const idKeyValue *kv;
const char *name;
Clear();
// health
maxHealth = dict.GetInt( "maxhealth", "100" );
bHasDeathwalked = dict.GetBool( "bHasDeathwalked" );
storedHealth = dict.GetInt( "storedHealth", "0" );
energyType = dict.GetString( "energyType", "energy_plasma" );
zoomFov = dict.GetInt( "zoomFov" );
// the clip and powerups aren't restored
// max spirit
maxSpirit = dict.GetInt( "max_ammo_spiritpower" );
// ammo
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
if ( name ) {
ammo[ i ] = dict.GetInt( name );
}
}
//HUMANHEAD bjk: weapons
for( i = 0; i < MAX_WEAPONS; i++ ) {
sprintf( key, "altMode_%i", i );
altMode[i] = dict.GetBool( key );
sprintf( key, "weaponRaised_%i", i );
weaponRaised[i] = dict.GetBool( key );
}
//HUMANHEAD END
// items
num = dict.GetInt( "items" );
items.SetNum( num );
for( i = 0; i < num; i++ ) {
item = new idDict();
items[ i ] = item;
sprintf( itemname, "item_%i ", i );
kv = dict.MatchPrefix( itemname );
while( kv ) {
key = kv->GetKey();
key.Strip( itemname );
item->Set( key, kv->GetValue() );
kv = dict.MatchPrefix( itemname, kv );
}
}
//HUMANHEAD aob: in addition to the persistent items, give hardcoded items from players.def
Give( owner, dict, "item", dict.GetString( "item" ), NULL, true );
//HUMANHEAD END
// weapons are stored as a number for persistant data, but as strings in the entityDef
weapons = dict.GetInt( "weapon_bits", "0" );
Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
num = dict.GetInt( "levelTriggers" );
for ( i = 0; i < num; i++ ) {
sprintf( itemname, "levelTrigger_Level_%i", i );
idLevelTriggerInfo lti;
lti.levelName = dict.GetString( itemname );
sprintf( itemname, "levelTrigger_Trigger_%i", i );
lti.triggerName = dict.GetString( itemname );
levelTriggers.Append( lti );
}
// Keep weapon switch HUD element from showing up at level load
weaponPulse = false;
}
int hhInventory::MaxAmmoForAmmoClass( idPlayer *owner, const char *ammo_classname ) const {
int max = 0;
if (ammo_classname != NULL) {
if (!idStr::Icmp(ammo_classname, "ammo_spiritpower")) {
max = maxSpirit;
}
else {
max = idInventory::MaxAmmoForAmmoClass(owner, ammo_classname);
}
}
return max;
}
// PDMMERGE PERSISTENTMERGE: Overridden, Done for 6-03-05 merge
void hhInventory::AddPickupName( const char *name, const char *icon, bool bIsWeapon) {
if ( idStr::Length(icon) > 0 ) {
idItemInfo &info = pickupItemNames.Alloc();
if ( !idStr::Icmpn( name, STRTABLE_ID, strlen( STRTABLE_ID ) ) ) {
info.name = common->GetLanguageDict()->GetString( name );
} else {
info.name = name;
}
info.icon = icon;
info.time = 0;
info.slotZeroTime = 0;
info.matcolorAlpha = 0.0f;
info.bDoubleWide = bIsWeapon;
}
}
/*
==============
hhInventory::Give
PDMMERGE PERSISTENTMERGE: Overridden, Done for 6-03-05 merge
==============
*/
bool hhInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value, int *idealWeapon, bool updateHud ) {
int i;
const char *pos;
const char *end;
int len;
idStr weaponString;
int max;
const idDeclEntityDef *weaponDecl;
bool tookWeapon;
int amount;
idItemInfo info;
hhPlayer* playerOwner;
if( owner && owner->IsType( hhPlayer::Type ) ) {
playerOwner = static_cast<hhPlayer*>(owner);
}
if ( !idStr::Icmp( statname, "health" ) || !idStr::Icmp( statname, "healthspecial" ) ) { //healthspecial is for mp and indicates that this item can push a player's health up to the "real" maxhealth
int localMaxHealth = maxHealth;
if (gameLocal.isMultiplayer) { //rww - only pipes can put us above 100 in mp
if (idStr::Icmp( statname, "healthspecial" )) {
localMaxHealth = MAX_HEALTH_NORMAL_MP;
}
}
if ( playerOwner->health >= localMaxHealth ) {
return false;
}
int oldHealth = playerOwner->health;
playerOwner->health += atoi( value );
if ( playerOwner->health > localMaxHealth ) {
playerOwner->health = localMaxHealth;
}
if (playerOwner) {
playerOwner->healthPulse = true;
}
} else if ( !idStr::Icmpn( statname, "ammo_", 5 ) ) {
i = AmmoIndexForAmmoClass( statname );
max = MaxAmmoForAmmoClass( owner, statname );
if ( ammo[ i ] >= max ) {
return false;
}
amount = atoi( value );
if ( amount ) {
ammo[ i ] += amount;
if ( ( max > 0 ) && ( ammo[ i ] > max ) ) {
ammo[ i ] = max;
}
ammoPulse = true;
}
if (playerOwner && !idStr::Icmp(statname, "ammo_spiritpower")) {
playerOwner->spiritPulse = true;
}
} else if ( !idStr::Icmp( statname, "item" ) ) {
pos = value;
while( pos != NULL ) {
end = strchr( pos, ',' );
if ( end ) {
len = end - pos;
end++;
} else {
len = strlen( pos );
}
idStr itemName( pos, 0, len );
GiveItem( spawnArgs, gameLocal.FindEntityDefDict(itemName.c_str(), false) );
pos = end;
}
} else if ( !idStr::Icmp( statname, "weapon" ) ) {
tookWeapon = false;
for( pos = value; pos != NULL; pos = end ) {
end = strchr( pos, ',' );
if ( end ) {
len = end - pos;
end++;
} else {
len = strlen( pos );
}
idStr weaponName( pos, 0, len );
// find the number of the matching weapon name
for( i = 1; i < MAX_WEAPONS; i++ ) {
if ( weaponName == playerOwner->GetWeaponName(i) ) {
break;
}
}
if ( i >= MAX_WEAPONS ) {
gameLocal.Error( "Unknown weapon '%s'", weaponName.c_str() );
}
// cache the media for this weapon
weaponDecl = gameLocal.FindEntityDef( weaponName, false );
// don't pickup "no ammo" weapon types twice
// not for D3 SP .. there is only one case in the game where you can get a no ammo
// weapon when you might already have it, in that case it is more conistent to pick it up
if ( gameLocal.isMultiplayer && weaponDecl && ( weapons & ( 1 << i ) ) && !weaponDecl->dict.GetInt( "ammoRequired" ) ) {
continue;
}
if ( !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || ( weaponName == "weaponobj_fists" ) ) {
playerOwner->UnlockWeapon( i ); //TODO add key for disabling this
if ( ( weapons & ( 1 << i ) ) == 0 || gameLocal.isMultiplayer ) {
if ( (owner->GetUserInfo()->GetBool( "ui_autoSwitch" ) || !gameLocal.isMultiplayer) && idealWeapon ) {
// HUMANHEAD pdm: added spirit check, so we don't autoswitch to weapons when picking them up in spriitwalk
// HUMANHEAD pdm: also added check for spiritweapon, don't autoswitch to it.
if (!static_cast<hhPlayer*>(owner)->IsSpiritOrDeathwalking() && weaponName.Icmp("weaponobj_bow")) {
assert( !gameLocal.isClient );
*idealWeapon = i;
}
}
// Pulse if not picking up the spirit bow
if (weaponName.Icmp("weaponobj_bow") != 0) {
weaponPulse = true;
}
weapons |= ( 1 << i );
tookWeapon = true;
}
}
}
return tookWeapon;
}
else if ( !idStr::Icmp( statname, "maxhealth" ) ) {
if (owner) {
if (gameLocal.GetLocalPlayer() == owner) { //sp, listen server
if (owner->hud) {
owner->hud->HandleNamedEvent( "maxHealthPulse" );
}
}
else if (gameLocal.isMultiplayer && !gameLocal.isClient) { //otherwise, broadcast event to owner
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
msg.Init(msgBuf, sizeof(msgBuf));
msg.WriteBits((1<<idPlayer::MENU_NET_EVENT_MAXHEALTHPULSE), idPlayer::MENU_NET_EVENT_NUM);
owner->ServerSendEvent(idPlayer::EVENT_MENUEVENT, &msg, false, -1, owner->entityNumber);
}
}
if (gameLocal.isMultiplayer) { //rww - different behaviour for mp
maxHealth = atoi(value);
}
else {
maxHealth += atoi(value);
}
}
else if ( !idStr::Icmp( statname, "maxspirit" ) ) {
owner->hud->HandleNamedEvent( "maxSpiritPulse" );
maxSpirit += atoi(value);
}
else {
// unknown item
return false;
}
return true;
}
/*
==============
hhInventory::GiveItem
==============
*/
bool hhInventory::GiveItem( const idDict& spawnArgs, const idDict* item ) {
idStr itemName;
if( !item ) {
return false;
}
idDict* dict = new idDict( *item );
if( !FindItem(item) ) {
items.Append( dict );
return true;
}
SAFE_DELETE_PTR( dict );
return false;
}
/*
===============
hhInventory::HasAmmo
===============
*/
int hhInventory::HasAmmo( ammo_t type, int amount ) {
assert(type >= 0 && type < AMMO_NUMTYPES);
if ( ( type == idWeapon::GetAmmoNumForName("ammo_none") ) || !amount ) {
// always allow weapons that don't use ammo to fire
return -1;
}
// check if we have infinite ammo
if ( ammo[ type ] < 0 ) {
return -1;
}
// return how many shots we can fire
return ammo[ type ] / amount;
}
/*
===============
hhInventory::HasAmmo
===============
*/
int hhInventory::HasAmmo( const char *weapon_classname ) {
int ammoRequired;
ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, &ammoRequired );
return HasAmmo( ammo_i, ammoRequired );
}
/*
===============
hhInventory::HasAltAmmo
HUMANHEAD bjk
===============
*/
int hhInventory::HasAltAmmo( const char *weapon_classname ) {
int ammoRequired;
ammo_t ammo_i = AltAmmoIndexForWeaponClass( weapon_classname, &ammoRequired );
return HasAmmo( ammo_i, ammoRequired );
}
/*
===============
hhInventory::UseAmmo
===============
*/
bool hhInventory::UseAmmo( ammo_t type, int amount ) {
if ( !HasAmmo( type, amount ) ) {
return false;
}
// take an ammo away if not infinite
if ( ammo[ type ] >= 0 ) {
ammo[ type ] -= amount;
//rww - don't forget this, it's important!
ammoPredictTime = gameLocal.time; // mp client: we predict this. mark time so we're not confused by snapshots
}
return true;
}
float hhInventory::AmmoPercentage(idPlayer *player, ammo_t type) {
float amount = ammo[type];
const char *ammoName = idWeapon::GetAmmoNameForNum( type );
float max = MaxAmmoForAmmoClass( player, ammoName );
max = max(1, max);
return amount / max;
}
/*
===============
hhInventory::FindInventoryItem
===============
*/
idDict* hhInventory::FindItem( const idDict* dict ) {
if( !dict ) {
return NULL;
}
return FindItem( dict->GetString("inv_name") );
}
/*
===============
hhInventory::FindInventoryItem
===============
*/
idDict* hhInventory::FindItem( const char *name ) {
const char* lname = NULL;
for( int ix = 0; ix < items.Num(); ++ix ) {
if( !items[ix] ) {
continue;
}
lname = items[ix]->GetString( "inv_name" );
if ( lname && *lname ) {
if ( idStr::Icmp( name, lname ) == 0 ) {
return items[ix];
}
}
}
return NULL;
}
/*
===============
hhInventory::EvaluateRequirements
===============
*/
void hhInventory::EvaluateRequirements(idPlayer *p) {
if (p) {
requirements.bCanDeathWalk = gameLocal.RequirementMet(p, p->spawnArgs.GetString("requirement_deathwalk"), 0);
requirements.bCanSpiritWalk = gameLocal.RequirementMet(p, p->spawnArgs.GetString("requirement_spiritwalk"), 0);
requirements.bCanSummonTalon = gameLocal.RequirementMet(p, p->spawnArgs.GetString("requirement_talon"), 0);
requirements.bCanUseBowVision = gameLocal.RequirementMet(p, p->spawnArgs.GetString("requirement_bowvision"), 0);
requirements.bCanUseLighter = gameLocal.RequirementMet(p, p->spawnArgs.GetString("requirement_lighter"), 0);
requirements.bCanWallwalk = gameLocal.RequirementMet(p, p->spawnArgs.GetString("requirement_wallwalk"), 0);
requirements.bHunterHand = gameLocal.RequirementMet(p, p->spawnArgs.GetString("requirement_hunterhand"), 0);
// See if talon should be spawned
if (p->IsType(hhPlayer::Type)) {
static_cast<hhPlayer*>(p)->TrySpawnTalon();
}
}
}