sin-2015/sentient.cpp
1999-04-22 00:00:00 +00:00

2764 lines
No EOL
57 KiB
C++

// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// Base class of entity that can carry other entities, and use weapons.
//
#include "g_local.h"
#include "entity.h"
#include "sentient.h"
#include "weapon.h"
#include "scriptmaster.h"
#include "ammo.h"
#include "armor.h"
#include "misc.h"
#include "inventoryitem.h"
#include "player.h"
#include "actor.h"
#include "hoverweap.h" //###
CLASS_DECLARATION( Entity, Sentient, NULL );
Event EV_Sentient_Attack( "fire" );
Event EV_Sentient_ReleaseAttack( "releasefire" );
Event EV_Sentient_GiveWeapon( "weapon" );
Event EV_Sentient_GiveAmmo( "ammo" );
Event EV_Sentient_GiveArmor( "armor" );
Event EV_Sentient_GiveItem( "item" );
Event EV_Sentient_GiveTargetname( "give" );
Event EV_Sentient_GiveInventoryItem( "invitem" );
Event EV_Sentient_GiveHealth( "health", EV_CHEAT );
Event EV_Sentient_TakeWeapon( "take_weapon" );
Event EV_Sentient_TakeAmmo( "take_ammo" );
Event EV_Sentient_TakeArmor( "take_armor" );
Event EV_Sentient_TakeItem( "take_item" );
Event EV_Sentient_WeaponPutAway( "weapon_putaway" );
Event EV_Sentient_WeaponReady( "weapon_ready" );
Event EV_Sentient_WeaponDoneFiring( "weapon_donefiring" );
Event EV_Sentient_AnimLoop( "animloop" );
Event EV_Sentient_UselessCheck( "useless_check" );
Event EV_Sentient_TurnOffShadow( "noshadow" );
Event EV_Sentient_Freeze( "freeze" );
Event EV_Sentient_UnFreeze( "unfreeze" );
Event EV_Sentient_ImpactDamage( "impact_damage" );
Event EV_Sentient_WeaponUse( "weaponuse", EV_CONSOLE );
Event EV_Sentient_SetDropWeapon( "dropweapon" );
Event EV_Sentient_DropWeaponNow( "dropweaponnow" );
//###
Event EV_Sentient_HurtFlame("hurtflame");
//###
ResponseDef Sentient::Responses[] =
{
{ &EV_Sentient_Attack, ( Response )Sentient::FireWeapon },
{ &EV_Sentient_ReleaseAttack, ( Response )Sentient::ReleaseFireWeapon },
{ &EV_Sentient_GiveWeapon, ( Response )Sentient::EventGiveWeapon },
{ &EV_Sentient_TakeWeapon, ( Response )Sentient::EventTakeWeapon },
{ &EV_Sentient_GiveAmmo, ( Response )Sentient::EventGiveAmmo },
{ &EV_Sentient_TakeAmmo, ( Response )Sentient::EventTakeAmmo },
{ &EV_Sentient_GiveArmor, ( Response )Sentient::EventGiveArmor },
{ &EV_Sentient_TakeArmor, ( Response )Sentient::EventTakeArmor },
{ &EV_Sentient_GiveItem, ( Response )Sentient::EventGiveItem },
{ &EV_Sentient_GiveInventoryItem,( Response )Sentient::EventGiveInventoryItem },
{ &EV_Sentient_GiveHealth, ( Response )Sentient::EventGiveHealth },
{ &EV_Sentient_GiveTargetname, ( Response )Sentient::EventGiveTargetname },
{ &EV_Sentient_WeaponPutAway, ( Response )Sentient::WeaponPutAway },
{ &EV_Sentient_WeaponReady, ( Response )Sentient::WeaponReady },
{ &EV_Sentient_WeaponDoneFiring, ( Response )Sentient::WeaponDoneFiring },
{ &EV_Sentient_AnimLoop, ( Response )Sentient::AnimLoop },
{ &EV_Damage, ( Response )Sentient::ArmorDamage },
{ &EV_Touch, ( Response )Sentient::SearchBody },
{ &EV_Sentient_UselessCheck, ( Response )Sentient::UselessCheck },
{ &EV_Sentient_TurnOffShadow, ( Response )Sentient::TurnOffShadow },
{ &EV_Sentient_Freeze, ( Response )Sentient::Freeze },
{ &EV_Sentient_UnFreeze, ( Response )Sentient::UnFreeze },
{ &EV_Sentient_ImpactDamage, ( Response )Sentient::ImpactDamage },
{ &EV_Sentient_WeaponUse, ( Response )Sentient::WeaponUse },
{ &EV_Sentient_SetDropWeapon, ( Response )Sentient::SetDropWeapon },
{ &EV_Sentient_DropWeaponNow, ( Response )Sentient::DropWeaponNowEvent },
{ &EV_Sentient_TakeItem, ( Response )Sentient::EventTakeItem },
//###
{ &EV_Sentient_HurtFlame, ( Response )Sentient::HurtFlame},
//###
{ NULL, NULL }
};
Container<Sentient *> SentientList;
Sentient::Sentient()
{
Vector defangles;
SentientList.AddObject( ( Sentient * )this );
inventory.ClearObjectList();
currentWeapon = NULL;
currentItem = NULL;
newWeapon = NULL;
animOverride = false;
dropweapon = true;
tempAnimEvent = NULL;
eyeposition = "0 0 64";
gunoffset = "0 0 56";
firedown = false;
firedowntime = 0;
// angles
defangles = Vector( 0, G_GetFloatArg( "angle", 0 ), 0 );
if (defangles.y == -1)
{
defangles = Vector( -90, 0, 0 );
}
else if (defangles.y == -2)
{
defangles = Vector( 90, 0, 0 );
}
angles = G_GetVectorArg( "angles", defangles );
setAngles( angles );
stopanimating_tillchange = false;
poweruptype = 0;
poweruptimer = 0;
sentientFrozen = false;
autoweaponswitch = 1; //### default weapon switching on
weaponoverride = 1; //### default weapon select override on
// HACK FIXME
//
// temporary shadow flag
//
edict->s.renderfx |= RF_XFLIP;
}
Sentient::~Sentient()
{
SentientList.RemoveObject( ( Sentient * )this );
FreeInventory();
}
Vector Sentient::EyePosition
(
void
)
{
Vector pos;
pos = worldorigin;
pos[ gravity_axis[ gravaxis ].x ] += eyeposition[ 0 ];
pos[ gravity_axis[ gravaxis ].y ] += eyeposition[ 1 ] * gravity_axis[ gravaxis ].sign;
pos[ gravity_axis[ gravaxis ].z ] += eyeposition[ 2 ] * gravity_axis[ gravaxis ].sign;
return pos;
}
Vector Sentient::GunPosition
(
void
)
{
Vector pos;
pos = worldorigin;
pos[ gravity_axis[ gravaxis ].x ] += gunoffset[ 0 ];
pos[ gravity_axis[ gravaxis ].y ] += gunoffset[ 1 ] * gravity_axis[ gravaxis ].sign;
pos[ gravity_axis[ gravaxis ].z ] += gunoffset[ 2 ] * gravity_axis[ gravaxis ].sign;
return pos;
}
inline void Sentient::GetGunOrientation
(
Vector pos,
Vector *forward,
Vector *right,
Vector *up
)
{
const gravityaxis_t &grav = gravity_axis[ gravaxis ];
if ( forward )
{
( *forward )[ grav.x ] = orientation[ 0 ][ 0 ];
( *forward )[ grav.y ] = orientation[ 0 ][ 1 ] * grav.sign;
( *forward )[ grav.z ] = orientation[ 0 ][ 2 ] * grav.sign;
}
if ( right )
{
( *right )[ grav.x ] = -orientation[ 1 ][ 0 ];
( *right )[ grav.y ] = - orientation[ 1 ][ 1 ] * grav.sign;
( *right )[ grav.z ] = - orientation[ 1 ][ 2 ] * grav.sign;
}
if ( up )
{
( *up )[ grav.x ] = orientation[ 2 ][ 0 ];
( *up )[ grav.y ] = orientation[ 2 ][ 1 ] * grav.sign;
( *up )[ grav.z ] = orientation[ 2 ][ 2 ] * grav.sign;
}
}
void Sentient::GetMuzzlePositionAndDirection
(
Vector *pos,
Vector *dir
)
{
*pos = Vector( "0 0 0" );
*dir = Vector( "0 0 0" );
if ( currentWeapon )
currentWeapon->GetMuzzlePosition( pos, dir );
}
void Sentient::EventGiveHealth
(
Event *ev
)
{
int group_num;
health = ev->GetFloat( 1 );
if ( isClient() )
{
for( group_num = 0; group_num < edict->s.numgroups ; group_num++ )
{
edict->s.groups[ group_num ] &= ~MDL_GROUP_SKINOFFSET_BIT0;
}
}
}
//### had to modify for hoverbike's multiple firing modes
void Sentient::FireWeapon
(
Event *ev
)
{
/* if ( ( currentWeapon ) && currentWeapon->ReadyToFire() && currentWeapon->HasAmmo() )
{
currentWeapon->Fire();
}
*/
if ( ( currentWeapon ) && currentWeapon->ReadyToFire() && currentWeapon->HasAmmo() )
{
if(this->isSubclassOf(Player))
{
if(((Player *)this)->GetHoverbike())
{
((HoverWeap *)currentWeapon)->Fire();
}
else
{
currentWeapon->Fire();
}
}
else
{
currentWeapon->Fire();
}
}
}
//###
void Sentient::ReleaseFireWeapon
(
Event *ev
)
{
// Only need to check if currentWeapon because FireWeapon checks for
// the weapon ready and if it has ammo.
if ( currentWeapon )
{
float fireholdtime = ev->GetFloat( 1 );
currentWeapon->ReleaseFire( fireholdtime );
}
}
void Sentient::AddItem
(
Item *object
)
{
inventory.AddObject( object->entnum );
if ( object->isSubclassOf( InventoryItem ) )
{
currentItem = (InventoryItem *)object;
}
//
// set our global game variables if client
//
if ( isClient() )
{
str fullname;
ScriptVariable * var;
fullname = str( "playeritem_" ) + object->getClassname();
var = gameVars.GetVariable( fullname.c_str() );
if ( !var )
{
gameVars.SetVariable( fullname.c_str(), 1 );
}
else
{
int amount;
amount = var->intValue() + 1;
var->setIntValue( amount );
}
var = levelVars.GetVariable( fullname.c_str() );
if ( !var )
{
levelVars.SetVariable( fullname.c_str(), 1 );
}
else
{
int amount;
amount = var->intValue() + 1;
var->setIntValue( amount );
}
}
}
void Sentient::RemoveItem
(
Item *object
)
{
Weapon *weapon;
inventory.RemoveObject( object->entnum );
if ( currentWeapon == object )
{
currentWeapon = NULL;
if ( newWeapon == object )
{
newWeapon = NULL;
weapon = BestWeapon();
}
else
{
weapon = newWeapon;
}
SetCurrentWeapon( weapon );
}
//
// set our global game variables if client
//
if ( isClient() )
{
str fullname;
ScriptVariable * var;
fullname = str( "playeritem_" ) + object->getClassname();
var = levelVars.GetVariable( fullname.c_str() );
if ( var )
{
int amount;
amount = var->intValue() - 1;
if ( amount < 0 )
amount = 0;
var->setIntValue( amount );
}
}
if ( currentItem == (InventoryItem *) object )
{
currentItem = NULL;
// Try to set the the current item
currentItem = (InventoryItem *)NextItem( NULL );
if (!currentItem)
{
currentItem = (InventoryItem *)PrevItem( NULL );
}
}
}
Item *Sentient::FindItem
(
const char *itemname
)
{
int num;
int i;
Item *item;
num = inventory.NumObjects();
for( i = 1; i <= num; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( !Q_stricmp( item->getClassname(), itemname ) )
{
return item;
}
}
return NULL;
}
void Sentient::FreeInventory
(
void
)
{
int num;
int i;
Item *item;
if ( currentWeapon )
currentWeapon->DetachFromOwner();
SetCurrentWeapon( NULL );
newWeapon = NULL;
num = inventory.NumObjects();
for( i = num; i > 0; i-- )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
delete item;
}
inventory.ClearObjectList();
currentItem = NULL;
}
void Sentient::FreeInventoryOfType
(
const char *type
)
{
int num;
int i;
Item *item;
num = inventory.NumObjects();
for( i = num; i > 0; i-- )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( checkInheritance( type, item->getClassname() ) )
{
inventory.RemoveObject( item->entnum );
// If the current item is about to be deleted, then look for
// another item to be current
if ( currentItem == item )
{
currentItem = NULL;
// Try to set the the current item
currentItem = (InventoryItem *)NextItem( NULL );
if (!currentItem)
{
currentItem = (InventoryItem *)PrevItem( NULL );
}
}
delete item;
}
}
}
qboolean Sentient::HasItem
(
const char *itemname
)
{
return ( FindItem( itemname ) != NULL );
}
int Sentient::NumWeapons
(
void
)
{
int num;
int i;
Item *item;
int numweaps;
numweaps = 0;
num = inventory.NumObjects();
for( i = 1; i <= num; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( checkInheritance( &Weapon::ClassInfo, item->getClassname() ) )
{
numweaps++;
}
}
return numweaps;
}
Weapon * Sentient::WeaponNumber
(
int weaponnum
)
{
int num;
int i;
Item *item;
// make it so that 0 is our first weapon rather than 1
weaponnum++;
num = inventory.NumObjects();
for( i = 1; i <= num; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( item->isSubclassOf( Weapon ) )
{
weaponnum--;
if ( !weaponnum )
{
return ( Weapon * )item;
}
}
}
return NULL;
}
void Sentient::ChangeWeapon
(
Weapon *weapon
)
{
if ( weapon == currentWeapon )
{
return;
}
if ( currentWeapon )
{
if ( !weapon->ReadyToUse() )
{
return;
}
newWeapon = weapon;
if ( !currentWeapon->ReadyToChange() )
{
return;
}
currentWeapon->PutAway();
}
else
{
SetCurrentWeapon( weapon );
}
}
void Sentient::ForceChangeWeapon
(
Weapon *weapon
)
{
if ( newWeapon == weapon )
{
return;
}
if ( weapon == currentWeapon )
{
return;
}
newWeapon = NULL;
if ( currentWeapon )
{
currentWeapon->DetachFromOwner();
}
SetCurrentWeapon( weapon );
}
qboolean Sentient::CanChangeWeapons
(
void
)
{
return !(
( newWeapon != NULL ) ||
( currentWeapon && !currentWeapon->ReadyToChange() ) ||
( flags & FL_MUTANT )
);
}
void Sentient::SetCurrentWeapon
(
Weapon *weapon
)
{
const char *name;
newWeapon = NULL;
currentWeapon = weapon;
if ( currentWeapon )
{
currentWeapon->ReadyWeapon();
// get the name of the group that the gun bone belongs to
name = gi.GetBoneGroupName( edict->s.modelindex, "gun" );
if ( name )
{
gun_bone_group_name = name;
}
else
{
gun_bone_group_name = "";
}
}
}
Weapon *Sentient::CurrentWeapon
(
void
)
{
return currentWeapon;
}
Weapon *Sentient::BestWeapon
(
Weapon *ignore
)
{
Item *next;
int n;
int j;
int bestrank;
Weapon *bestweapon;
n = inventory.NumObjects();
// Search forewards until we find a weapon
bestweapon = NULL;
bestrank = -999999;
for( j = 1; j <= n; j++ )
{
next = ( Item * )G_GetEntity( inventory.ObjectAt( j ) );
assert( next );
if ( ( next != ignore ) && next->isSubclassOf( Weapon ) && ( ( ( Weapon * )next )->Rank() > bestrank ) &&
( ( ( Weapon * )next )->HasAmmo() ) )
{
bestweapon = ( Weapon * )next;
bestrank = bestweapon->Rank();
}
}
return bestweapon;
}
Weapon *Sentient::NextWeapon
(
Weapon *weapon
)
{
Item *item;
int i;
int n;
int weaponorder;
Weapon *choice;
int choiceorder;
Weapon *bestchoice;
int bestorder;
Weapon *worstchoice;
int worstorder;
if ( !inventory.ObjectInList( weapon->entnum ) )
{
error( "NextWeapon", "Weapon not in list" );
}
weaponorder = weapon->Order();
bestchoice = weapon;
bestorder = 65535;
worstchoice = weapon;
worstorder = weaponorder;
n = inventory.NumObjects();
for( i = 1; i <= n; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( item );
if ( item->isSubclassOf( Weapon ) )
{
choice = ( Weapon * )item;
if ( !choice->HasAmmo() || !choice->AutoChange() )
{
continue;
}
choiceorder = choice->Order();
if ( ( choiceorder > weaponorder ) && ( choiceorder < bestorder ) )
{
bestorder = choiceorder;
bestchoice = choice;
}
if ( choiceorder < worstorder )
{
worstorder = choiceorder;
worstchoice = choice;
}
}
}
if ( bestchoice == weapon )
{
return worstchoice;
}
return bestchoice;
}
Weapon *Sentient::PreviousWeapon
(
Weapon *weapon
)
{
Item *item;
int i;
int n;
int weaponorder;
Weapon *choice;
int choiceorder;
Weapon *bestchoice;
int bestorder;
Weapon *worstchoice;
int worstorder;
if ( !inventory.ObjectInList( weapon->entnum ) )
{
error( "PreviousWeapon", "Weapon not in list" );
}
weaponorder = weapon->Order();
bestchoice = weapon;
bestorder = -65535;
worstchoice = weapon;
worstorder = weaponorder;
n = inventory.NumObjects();
for( i = 1; i <= n; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( item );
if ( item->isSubclassOf( Weapon ) )
{
choice = ( Weapon * )item;
if ( !choice->HasAmmo() || !choice->AutoChange() )
{
continue;
}
choiceorder = choice->Order();
if ( ( choiceorder < weaponorder ) && ( choiceorder > bestorder ) )
{
bestorder = choiceorder;
bestchoice = choice;
}
if ( choiceorder > worstorder )
{
worstorder = choiceorder;
worstchoice = choice;
}
}
}
if ( bestchoice == weapon )
{
return worstchoice;
}
return bestchoice;
}
qboolean Sentient::WeaponReady
(
void
)
{
Weapon *best;
if ( !currentWeapon )
{
return false;
}
if (!currentWeapon->ReadyToUse() )
{
best = BestWeapon();
if ( best )
{
ChangeWeapon( best );
}
return false;
}
if ( !currentWeapon->ReadyToFire() )
{
return false;
}
if ( !currentWeapon->HasAmmo() )
{
best = BestWeapon();
if ( best )
{
ChangeWeapon( best );
}
return false;
}
return true;
}
void Sentient::DropWeapon
(
Weapon *weapon
)
{
if ( weapon == currentWeapon )
{
if ( deadflag )
{
SetCurrentWeapon( NULL );
}
else
{
// Choose the best weapon that's not the current
SetCurrentWeapon( BestWeapon( currentWeapon ) );
}
}
// check if the sentient can drop the weapon
if ( dropweapon )
{
// This one checks if the weapon is droppable
if ( !weapon->Drop() && deadflag )
{
// can't drop it, probably because there's no worldmodel
// since we're dead, just delete the weapon.
weapon->PostEvent(EV_Remove, 0);
}
}
else
{
weapon->PostEvent(EV_Remove, 0);
}
}
void Sentient::DropCurrentWeapon
(
void
)
{
if ( currentWeapon )
DropWeapon( currentWeapon );
}
void Sentient::takeWeapon
(
const char *weaponname
)
{
Weapon *weapon;
assert( weaponname );
weapon = ( Weapon * )FindItem( weaponname );
if ( !weapon )
{
return;
}
if(weapon == newWeapon)
{
newWeapon = NULL;
}
if ( weapon == currentWeapon )
{
//### added check for the newWeapon value
if(newWeapon && newWeapon != weapon)
{
SetCurrentWeapon(newWeapon);
}
else
{
SetCurrentWeapon( BestWeapon( weapon ) );
}
//###
}
weapon->DetachFromOwner();
weapon->PostEvent( EV_Remove, 0 );
}
void Sentient::EventGiveTargetname
(
Event *ev
)
{
str name;
Item *item=NULL;
edict_t *from;
name = ev->GetString( 1 );
for ( from = &g_edicts[ 1 ]; from < &g_edicts[ globals.num_edicts ] ; from++ )
{
if ( !from->inuse )
continue;
if ( from->entity->isSubclassOf( Item ) )
{
item = ( Item * ) from->entity;
if ( item->itemname == name )
break;
}
}
// Item should be found
assert( item );
if ( item )
{
item->SetOwner( this );
item->ProcessPendingEvents();
AddItem( item );
}
else
{
ev->Error( "Could not give item %s to %s.\n", name, targetname );
}
}
Item *Sentient::giveItem
(
const char * itemname,
int amount,
int icon_index
)
{
ClassDef *cls;
Item *item;
int index;
item = ( Item * )FindItem( itemname );
if ( item )
{
item->Add( amount );
}
else
{
cls = getClass( itemname );
if ( !cls )
{
gi.dprintf( "No item named '%s'\n", itemname );
return NULL;
}
item = ( Item * )cls->newInstance();
item->SetOwner( this );
item->ProcessPendingEvents();
item->Set( amount );
AddItem( item );
}
// Send icon to the player
if ( this->isSubclassOf( Player ) )
{
if ( icon_index >= 0 )
index = icon_index;
else
index = item->Icon();
if ( index >= 0 )
{
gi.WriteByte( svc_console_command );
gi.WriteString( va( "pl %d %d", index, amount ) );
gi.unicast( edict, NULL );
}
}
return item;
}
void Sentient::takeItem
(
const char *itemname,
int amount
)
{
Item *item;
item = ( Item * )FindItem( itemname );
if ( item )
{
item->Remove( amount );
if ( !item->Amount() )
item->ProcessEvent( EV_Remove );
}
}
void Sentient::EventTakeItem
(
Event *ev
)
{
int amount;
if ( ev->NumArgs() > 1 )
amount = ev->GetInteger( 2 );
else
amount = 1;
takeItem( ev->GetString( 1 ), amount );
}
//### modified for hoverbike
Weapon *Sentient::giveWeapon
(
const char *weaponname
)
{
Weapon *weapon;
assert( weaponname );
if ( !checkInheritance( &Weapon::ClassInfo, weaponname ) )
{
gi.dprintf( "A %s is not usable as a weapon\n", weaponname );
return NULL;
}
weapon = ( Weapon * )giveItem( weaponname, 1 );
//### don't change weapons if on a hoverbike
// or if autoweapon switching is turned off
// if ( !currentWeapon || ( weapon->Rank() > currentWeapon->Rank() ) )
if (
autoweaponswitch &&
(!currentWeapon || (weapon->Rank() > currentWeapon->Rank()))
&& !IsOnBike()
)
{
ChangeWeapon( weapon );
}
//###
else
{
NonAutoChangeWeapon( weapon );
}
//###
return weapon;
}
Weapon *Sentient::useWeapon
(
const char *weaponname
)
{
Weapon *weapon;
assert( weaponname );
weapon = ( Weapon * )FindItem( weaponname );
if ( weapon )
{
ChangeWeapon( weapon );
}
return currentWeapon;
}
void Sentient::EventGiveWeapon
(
Event *ev
)
{
ChangeWeapon( giveWeapon( ev->GetString( 1 ) ) );
}
void Sentient::EventTakeWeapon
(
Event *ev
)
{
takeWeapon( ev->GetString( 1 ) );
}
void Sentient::EventTakeAmmo
(
Event *ev
)
{
int amount;
const char *type;
Ammo *ammo;
Weapon *best;
type = ev->GetString( 1 );
amount = ev->GetInteger( 2 );
ammo = ( Ammo * )FindItem( type );
if ( ammo )
{
ammo->Remove( amount );
}
if ( currentWeapon && !currentWeapon->HasAmmo() )
{
best = BestWeapon();
if ( best )
{
ChangeWeapon( best );
}
}
}
void Sentient::EventGiveAmmo
(
Event *ev
)
{
int amount;
const char *type;
type = ev->GetString( 1 );
amount = ev->GetInteger( 2 );
if ( !checkInheritance( &Ammo::ClassInfo, type ) )
{
gi.dprintf( "%s is not usable as ammo\n", type );
return;
}
giveItem( type, amount );
if ( currentWeapon && !currentWeapon->HasAmmo() )
{
Weapon *best;
best = BestWeapon();
if ( best )
{
ChangeWeapon( best );
}
}
}
void Sentient::EventTakeArmor
(
Event *ev
)
{
int amount;
const char *type;
Armor *armor;
type = ev->GetString( 1 );
amount = ev->GetInteger( 2 );
armor = ( Armor * )FindItem( type );
if ( armor )
{
armor->Remove( amount );
}
}
void Sentient::EventGiveArmor
(
Event *ev
)
{
int amount;
const char *type;
Armor *armor;
type = ev->GetString( 1 );
amount = ev->GetInteger( 2 );
if ( !checkInheritance( &Armor::ClassInfo, type ) )
{
gi.dprintf( "%s is not usable as armor\n", type );
return;
}
armor = ( Armor * )FindItem( type );
if ( armor )
{
armor->Set( amount );
}
else
{
giveItem( type, amount );
}
}
void Sentient::EventGiveItem
(
Event *ev
)
{
const char *type;
float amount;
type = ev->GetString( 1 );
amount = ev->GetInteger( 2 );
giveItem( type, amount );
}
void Sentient::EventGiveInventoryItem
(
Event *ev
)
{
const char *type;
int amount;
type = ev->GetString( 1 );
amount = ev->GetInteger( 2 );
if ( !checkInheritance( &InventoryItem::ClassInfo, type ) )
{
gi.dprintf( "item '%s' is not a InventoryItem\n", type );
return;
}
giveItem( type, amount );
}
void Sentient::WeaponPutAway
(
Event *ev
)
{
Weapon *best;
if(newWeapon)
{
SetCurrentWeapon(newWeapon);
}
else
{
best = BestWeapon();
if(best)
{
SetCurrentWeapon(best);
}
}
}
void Sentient::WeaponReady
(
Event *ev
)
{
if ( newWeapon )
{
ChangeWeapon( newWeapon );
}
}
void Sentient::WeaponDoneFiring
(
Event *ev
)
{
Weapon *best;
if ( newWeapon )
{
ChangeWeapon( newWeapon );
}
else if ( !currentWeapon || !currentWeapon->HasAmmo() || !currentWeapon->ReadyToUse() )
{
best = BestWeapon();
if ( best )
{
ChangeWeapon( best );
}
}
}
EXPORT_FROM_DLL void Sentient::AnimLoop
(
Event *ev
)
{
Event *t;
animOverride = false;
if ( deadflag )
{
StopAnimating();
}
else if ( animating )
{
RandomAnimate( currentAnim.c_str(), NULL );
}
else
{
stopanimating_tillchange = true;
}
if ( tempAnimEvent )
{
t = tempAnimEvent;
tempAnimEvent = NULL;
ProcessEvent( t );
}
}
EXPORT_FROM_DLL void Sentient::SetAnim
(
const char *anim
)
{
assert( anim );
assert ( !deadflag );
if ( str( anim ) == currentAnim )
{
if (!animating && !stopanimating_tillchange)
StartAnimating();
return;
}
stopanimating_tillchange = false;
currentAnim = str( anim );
if ( animOverride )
{
return;
}
RandomAnimate( currentAnim.c_str(), EV_Sentient_AnimLoop );
if (!animating)
StartAnimating();
}
EXPORT_FROM_DLL void Sentient::TempAnim
(
const char *anim,
Event *event
)
{
assert ( !deadflag );
assert( anim );
animOverride = true;
tempAnimEvent = event;
RandomAnimate( anim, EV_Sentient_AnimLoop );
}
void Sentient::TempAnim
(
const char *anim,
Event &event
)
{
Event *ev;
ev = new Event( event );
TempAnim( anim, ev );
}
void Sentient::PrintDamageLocationToAttacker
(
edict_s *attacker,
const char *victim_name,
const char *location
)
{
const char *expanded_location;
expanded_location = ExpandLocation( location );
if ( expanded_location )
gi.cprintf ( attacker, PRINT_MEDIUM, "You hit %s in the %s.\n", victim_name, expanded_location );
}
void Sentient::PrintDamageLocationToVictim
(
edict_s *victim,
const char *location
)
{
const char *expanded_location;
expanded_location = ExpandLocation( location );
if ( expanded_location )
gi.cprintf ( victim, PRINT_MEDIUM, "%s damage\n", expanded_location );
}
qboolean Sentient::DoGib
(
int meansofdeath,
Entity *inflictor
)
{
if ( !( flags & FL_DIE_GIBS ) || parentmode->value )
{
return false;
}
if (
( meansofdeath == MOD_TELEFRAG ) ||
( meansofdeath == MOD_MUTANTHANDS ) ||
( meansofdeath == MOD_HELIGUN ) ||
( meansofdeath == MOD_LAVA ) ||
( ( meansofdeath == MOD_FISTS ) && ( inflictor->flags & FL_ADRENALINE ) ) ||
( ( meansofdeath == MOD_SHOTGUN ) && ( G_Random() < 0.5 ) ) ||
//### added gib deaths from IP36 and Hoverbike run-overs
( meansofdeath == MOD_NUKE) ||
( meansofdeath == MOD_NUKEEXPLOSION) ||
( meansofdeath == MOD_HOVERBIKE)
//###
)
{
return true;
}
if ( health > -75 )
{
return false;
}
// Impact and Crush < -75 health
if ( ( meansofdeath == MOD_IMPACT ) ||
( meansofdeath == MOD_CRUSH ) ||
( meansofdeath == MOD_VEHICLE )
)
{
return true;
}
// Any projectiles except spear
if ( inflictor->isSubclassOf( Projectile ) )
{
if ( meansofdeath != MOD_SPEARGUN )
{
return true;
}
}
// Shotgun only 10% of the time
if ( meansofdeath == MOD_SHOTGUN )
{
if ( G_Random() > 0.5 )
{
return true;
}
}
return false;
}
void Sentient::ArmorDamage
(
Event *ev
)
{
Armor *armor=0;
Entity *inflictor;
Entity *attacker;
float damage;
float armor_damage=0;
Vector momentum;
Vector position;
Vector normal;
Vector direction;
Event *event;
int dflags;
int meansofdeath;
int knockback;
float damage_mult;
int groupnum;
int trinum;
const char *location="";
float unmodified_damage=0;
if ( ( takedamage == DAMAGE_NO ) || ( movetype == MOVETYPE_NOCLIP ) )
{
if ( isClient () )
{
Player *player;
player = ( Player * )this;
if ( !player->GetVehicle() )
return;
}
else
{
return;
}
}
damage = ev->GetFloat ( 1 );
inflictor = ev->GetEntity ( 2 );
attacker = ev->GetEntity ( 3 );
position = ev->GetVector ( 4 );
direction = ev->GetVector ( 5 );
normal = ev->GetVector ( 6 );
knockback = ev->GetInteger( 7 );
dflags = ev->GetInteger( 8 );
meansofdeath = ev->GetInteger( 9 );
groupnum = ev->GetInteger( 10 );
trinum = ev->GetInteger( 11 );
damage_mult = ev->GetFloat ( 12 );
// make sure attacker os there
if(!attacker)
{
return;
}
// Forcefields make objects invulnerable
if ( flags & FL_FORCEFIELD )
{
float alpha;
float radius;
Vector delta;
Vector org;
Entity *forcefield;
//
// spawn forcefield
//
forcefield = new Entity;
delta = centroid - worldorigin;
radius = delta.length();
//
// transform the centroid around by the current orientation
//
MatrixTransformVector( delta.vec3(), orientation, org.vec3() );
org += worldorigin;
forcefield->setModel( "sphere2.def" );
forcefield->setOrigin( org );
forcefield->worldorigin.copyTo(forcefield->edict->s.old_origin);
forcefield->setMoveType( MOVETYPE_NONE );
forcefield->setSolidType( SOLID_NOT );
forcefield->edict->s.scale = radius / 16;
alpha = ( damage * damage_mult ) / 100;
if ( alpha > 1 )
alpha = 1;
if ( alpha < 0.15f )
alpha = 0.15f;
forcefield->edict->s.alpha = alpha;
forcefield->edict->s.renderfx |= RF_TRANSLUCENT;
forcefield->PostEvent( EV_Remove, 0.1f );
return;
}
//### MFD
if(deathmatch->value == DEATHMATCH_MFD)
{
// the mobsters can't be damaged in MFD
if(isClient() && !client->resp.informer)
damage = 0;
}
else if(deathmatch->value == DEATHMATCH_MOB)
{
// people can't hit people that are on the same team
if(OnSameTeam(this, attacker) || !attacker->isClient())
damage = 0;
}
// if enabled you can't hurt teammates (but you can hurt yourself)
// knockback still occurs
else if ( ( attacker != this ) && ( DM_FLAG( DF_MODELTEAMS | DF_SKINTEAMS ) ) )
//###
{
if ( OnSameTeam ( this, attacker ) )
{
// check for friendly fire
if ( DM_FLAG( DF_FRIENDLY_FIRE ) || (dflags & DAMAGE_NO_PROTECTION))
meansofdeath = MOD_FRIENDLY_FIRE;
else
damage = 0;
}
}
if (groupnum == -1)
location = "all";
else
location = gi.Group_NumToName( edict->s.modelindex, groupnum );
// Adjust damage for skill level
if ( isClient() && !deathmatch->value && !(dflags & DAMAGE_NO_SKILL))
{
// Clamp the damage multiplier
if ( damage_mult > 2 )
damage_mult = 2;
switch( ( int )skill->value )
{
case 0:
damage *= 0.2f;
break;
case 1:
damage *= 0.4f;
break;
case 2:
damage *= 0.6f;
break;
default:
damage *= 0.8f;
break;
}
}
// Shields reduce damage by 75%
if ( flags & FL_SHIELDS )
{
damage *= 0.25f;
}
else if ( flags & FL_MUTANT )
{
damage *= 0.33f;
}
// If in deathmatch, client takes a minimum of 33% damage from source
if ( deathmatch->value )
{
damage *= 0.66f;
unmodified_damage = damage * 0.33f;
}
// check to see if we have armor in this location to reduce the damage
if (location[0] && !(dflags & DAMAGE_NO_ARMOR) )
{
if ( !strncmp( location,"all",3 ) )
{
char *armortypes[] = {"RiotHelmet", "FlakJacket", "FlakPants", NULL};
int i = 0;
float reduced_damage = 0;
// Go through all the armor types
while (armortypes[i])
{
float partial_damage = damage / 3;
float odam = partial_damage;
armor = ( Armor * )FindItem( armortypes[i] );
// Check for armor in this spot
if ( armor && ( armor->Amount() > 0 ) )
{
// Reduce the parital damage by the amount of armor
partial_damage -= armor->Amount();
if ( partial_damage < 0 )
partial_damage = 0;
// Remove the amount absorbed by the armor from the armor.
armor->Remove( odam - partial_damage );
// Keep track of the damage done to the armor for testing purposes
armor_damage += (odam - partial_damage );
}
// Keep track of damage done after armor has been applied
reduced_damage += partial_damage;
i++;
}
// This is the damage after all armor is done
damage = reduced_damage;
}
// else check for specific location based
else if ( !strncmp( location,"head",4 ) )
armor = ( Armor * )FindItem( "RiotHelmet" );
else if ( !strncmp(location,"torso",4 ) )
armor = ( Armor * )FindItem( "FlakJacket" );
// else if ( !strncmp( location,"arm",3 ) )
// armor = ( Armor * )FindItem( "FlakJacket" );
else if ( !strncmp(location,"leg",3 ) )
armor = ( Armor * )FindItem( "FlakPants" );
// If armor is there, remove the appropriate amount
if ( armor && ( armor->Amount() > 0 ) && !(dflags & DAMAGE_NO_ARMOR) )
{
float odam = damage;
damage -= armor->Amount();
if ( damage < 0 )
damage = 0;
armor->Remove( odam-damage );
armor_damage += (odam - damage );
}
}
damage += unmodified_damage;
// Damage multiplier
damage *= damage_mult;
// Damage skins if armor didn't absorb all damage
if ( ( damage > 0 ) && ( groupnum != -1 ) && ( flags & FL_BLOOD ) )
if ( !edict->s.groups[ groupnum ] )
edict->s.groups[ groupnum ]++;
// Show location based damage.
if ( sv_showdamagelocation->value && deathmatch->value )
{
// Send message to attacker
if ( attacker->isClient() )
{
if ( this->client )
{
PrintDamageLocationToAttacker( attacker->edict, this->client->pers.netname, location );
}
}
// Send message to victim
if ( this->isClient() && attacker != this )
{
PrintDamageLocationToVictim(this->edict, location);
}
}
// Shows detailed damage messages
if ( sv_showdamage->value )
{
// Send message to attacker
if ( attacker->isClient() )
{
if ( this->client )
{
gi.cprintf ( attacker->edict, PRINT_HIGH, "TARG:%s ARMOR_DAM:%0.2f TOT_DAM:%0.2f LOC:%s\n",
this->client->pers.netname, armor_damage, damage, location );
}
else
{
gi.cprintf ( attacker->edict, PRINT_HIGH, "TARG:%s ARMOR_DAM:%0.2f TOT_DAM:%0.2f LOC:%s\n",
getClassname(), armor_damage, damage, location );
}
}
// Send message to victim
if ( this->isClient() && attacker != this )
{
gi.cprintf ( this->edict, PRINT_HIGH, "TARG:%s ARMOR_DAM:%0.2f TOT_DAM:%0.2f LOC:%s\n",
this->client->pers.netname, armor_damage, damage, location );
}
}
// Blood and sparks effects
if (
( damage > 0 ) &&
( meansofdeath != MOD_DROWN ) &&
( meansofdeath != MOD_MUTANT_DRAIN )
)
{
// Blood particles
if ( flags & FL_BLOOD )
{
//### tripled blood amount
// Particles( position, normal, __min( 150,damage ), 127, PARTICLE_RANDOM );
Particles( position, normal, __min(150, (damage*3)) + 10, 127, PARTICLE_RANDOM );
// Blood splat
if ( dflags & DAMAGE_BULLET )
SprayBlood( position, direction, damage );
}
// Sparks from metal
else if ( flags & FL_SPARKS )
{
SpawnSparks (position, normal, __min(damage, 75) );
}
}
else if ( armor_damage > 0 )
{
// Sparks off armor
Particles( position, normal, __min(armor_damage, 75), 122, 0 );
}
// Gib if we are dead and get hit by a rocket or shotgun
if ( deadflag == DEAD_DEAD )
{
health -= damage;
if ( DoGib( meansofdeath, inflictor ) )
{
Event *gibEv;
gibEv = new Event( EV_Gib );
gibEv->AddInteger( 0 );
ProcessEvent( gibEv );
}
return;
}
else if ( deadflag )
{
health -= damage;
return;
}
// Do the kick
if (!(dflags & DAMAGE_NO_KNOCKBACK))
{
if ((knockback) &&
(movetype != MOVETYPE_NONE) &&
(movetype != MOVETYPE_BOUNCE) &&
(movetype != MOVETYPE_PUSH) &&
(movetype != MOVETYPE_STOP))
{
float m;
if (mass < 50)
m = 50;
else
m = mass;
direction.normalize();
if ( isClient() && ( attacker == this ) && deathmatch->value )
momentum = direction * ( 1700.0f * ( float )knockback / m ); // the rocket jump hack...
else
momentum = direction * ( 500.0f * ( float )knockback / m );
if ( dflags & DAMAGE_BULLET )
{
// Clip the z velocity for bullet weapons
if ( momentum.z > 75)
momentum.z = 75;
}
velocity += momentum;
}
}
// check for godmode or invincibility
if ( flags & FL_GODMODE )
{
return;
}
// do the damage
health -= damage;
// He's dead jim, send out a bunch of events
if ( health <= 0 )
{
if ( ( meansofdeath == MOD_ION ) && !( flags & FL_NOION ) )
{
flags |= FL_DIE_TESSELATE;
RandomGlobalSound( "snd_tesselate" );
}
event = new Event( EV_Killed );
event->AddEntity( attacker );
event->AddInteger( damage );
event->AddEntity( inflictor );
event->AddString( location );
event->AddInteger( meansofdeath );
ProcessEvent( event );
return;
}
if ( ( damage > 0 ) && ( meansofdeath == MOD_ION ) && !( flags & FL_NOION ) )
{
// Do the ion particlizer effect
TesselateModel
(
this,
1,
1000,
direction,
damage,
1.0f,
0,
vec3_origin,
TESSELATE_EXPANDANDSHRINK,
126
);
}
// Tesselate from damage
if (flags & FL_TESSELATE)
{
TesselateModel
(
this,
tess_min_size,
tess_max_size,
direction,
damage,
1.0f,
tess_thickness,
vec3_origin
);
}
// Darken if we are hurt
if (flags & FL_DARKEN)
{
edict->s.renderfx |= RF_LIGHTOFFSET;
if ( max_health )
{
edict->s.lightofs = - ( 40.0f * ( (float)(max_health - health) / (float)max_health ) );
}
else
{
edict->s.lightofs -= damage;
}
if ( edict->s.lightofs < -127 )
edict->s.lightofs = -127;
if ( edict->s.lightofs > 127 )
edict->s.lightofs = 127;
}
// Drop your weapon if you get hit in the hands
if ( location == gun_bone_group_name && ( ( damage > 50 ) || ( G_Random( 1 ) > 0.97f ) ) )
{
// don't drop weapons when in deathmatch and DF_NO_DROP_WEAPONS is set
if ( currentWeapon && currentWeapon->IsDroppable() && ( !deathmatch->value || !DM_FLAG( DF_NO_DROP_WEAPONS ) ) )
{
DropCurrentWeapon();
WeaponKnockedFromHands();
}
}
if ( meansofdeath == MOD_MUTANT_DRAIN )
return;
// Send pain event
event = new Event( EV_Pain );
event->AddFloat( damage );
event->AddEntity( attacker );
event->AddString( location );
event->AddInteger( meansofdeath );
ProcessEvent( event );
}
void Sentient::UpdateSilencedWeapons
(
void
)
{
// If the current weapon is silencable, then change it.
if ( currentWeapon && currentWeapon->IsSilenced() )
{
currentWeapon->ProcessEvent(EV_Weapon_PutAwayAndRaise);
}
}
int Sentient::NumInventoryItems
(
void
)
{
return inventory.NumObjects();
}
Item *Sentient::NextItem
(
Item *item
)
{
Item *next_item;
int i;
int n;
qboolean item_found = false;
if ( !item )
{
item_found = true;
}
else if ( !inventory.ObjectInList( item->entnum ) )
{
error( "NextItem", "Item not in list" );
}
n = inventory.NumObjects();
for( i = 1; i <= n; i++ )
{
next_item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( next_item );
if ( next_item->isSubclassOf( InventoryItem ) && item_found )
return next_item;
if ( next_item == item )
item_found = true;
}
return NULL;
}
Item *Sentient::PrevItem
(
Item *item
)
{
Item *prev_item;
int i;
int n;
qboolean item_found = false;
if ( !item )
{
item_found = true;
}
else if ( !inventory.ObjectInList( item->entnum ) )
{
error( "NextItem", "Item not in list" );
}
n = inventory.NumObjects();
for( i = n; i >= 1; i-- )
{
prev_item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( prev_item );
if ( prev_item->isSubclassOf( InventoryItem ) && item_found)
return prev_item;
if ( prev_item == item )
item_found = true;
}
return NULL;
}
// This will search a dead body and create a floating inventory for that player
// which will be displayed on the client as a list of icons. If the user presses
// use, then he will pick up the items in that floating inventory, and the body
// will be faded out.
void Sentient::SearchBody
(
Event *ev
)
{
int i,n;
Item *item;
Entity *other;
Player *player;
Event *event;
qboolean inventory_created = false;
other = ev->GetEntity( 1 );
assert( other );
// Only players can touch this
if ( !other->isSubclassOf( Player ) )
return;
// Can't touch yourself
if ( other == this )
return;
// Make sure the body is dead
if ( !deadflag )
return;
n = inventory.NumObjects();
// No items, get out of here
if ( n == 0 )
{
return;
}
player = ( Player * )other;
// Check to see if this inventory is already being displayed for
// this body.
if ( player->GetFloatingOwner() == this )
{
// Still touching the body, so extend the clear time
player->CancelEventsOfType( EV_Player_ClearFloatingInventory );
event = new Event( EV_Player_ClearFloatingInventory );
player->PostEvent( event, 0.4f );
return;
}
else if ( player->GetFloatingOwner() )
{
// Currently looking at an inventory, so don't bother with this one.
return;
}
else
{
// Make sure the inventory is cleared out before making a new one.
// Could have some left over items in there from other bodies
player->ProcessEvent( EV_Player_ClearFloatingInventory );
}
// Create the inventory
for( i = 1; i <= n; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( item );
if ( item->Amount() )
{
player->AddItemToFloatingInventory( item );
inventory_created = true;
}
}
if (!inventory_created)
return;
// Set the current owner of the inventory
player->SetFloatingOwner( this );
// Send the inventory to the client
player->SendFloatingInventory();
// Clear the inventory in the future
event = new Event( EV_Player_ClearFloatingInventory );
player->PostEvent( event, 0.3f );
}
void Sentient::UselessCheck( Event *ev )
{
Item *item;
int n;
int i;
Event *event;
// Check to see if we have any inventory and
// remove if we don't
n = inventory.NumObjects();
for ( i = 1; i <= n; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( item );
if ( item->Amount() )
{
PostEvent ( EV_Sentient_UselessCheck, 1.0f + G_Random() );
return;
}
}
event = new Event( "remove_useless" );
ProcessEvent( event );
}
qboolean Sentient::HasInventoryOfType
(
const char *type
)
{
int num;
int i;
Item *item;
num = inventory.NumObjects();
for( i = num; i > 0; i-- )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( checkInheritance( type, item->getClassname() ) )
{
return true;
}
}
return false;
}
void Sentient::DropInventoryItems
(
void
)
{
int num;
int i;
Item *item;
// Drop any inventory items
num = inventory.NumObjects();
for( i = num; i >= 1; i-- )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( item->isSubclassOf( InventoryItem ) )
{
item->Drop();
}
}
}
qboolean Sentient::PowerupActive
(
void
)
{
if ( poweruptype && this->client )
{
gi.cprintf( edict, PRINT_HIGH, "You are already using a powerup\n" );
}
return poweruptype;
}
void Sentient::SprayBlood
(
Vector src,
Vector dir,
float damage
)
{
trace_t trace;
Vector norm;
Vector end;
Vector ang;
float dist;
float scale;
BloodSplat *splat;
dir.normalize();
end = src + dir * 2048;
trace = G_Trace( src, vec_zero, vec_zero, end, this, MASK_SOLIDNONFENCE, "Sentient::SprayBlood" );
if ( HitSky( &trace ) || ( trace.ent->solid != SOLID_BSP ) || ( trace.ent->s.number != 0 ) )
{
return;
}
dist = ( Vector( trace.endpos ) - src ).length();
scale = damage / ( dist * 0.3 );
if ( scale > 0.6 )
{
scale = 0.6;
}
if ( scale < 0.02 )
{
return;
}
norm = trace.plane.normal;
norm.x = -norm.x;
norm.y = -norm.y;
ang = norm.toAngles();
ang.z = G_Random( 360 );
end = trace.endpos + Vector( trace.plane.normal ) * 0.2;
splat = new BloodSplat( end, ang, scale );
}
EXPORT_FROM_DLL void Sentient::setModel
(
const char *mdl
)
{
if ( currentWeapon )
{
// rebind the current gun
currentWeapon->DetachFromOwner();
Entity::setModel( mdl );
currentWeapon->AttachToOwner();
}
else
{
Entity::setModel( mdl );
}
}
void Sentient::TurnOffShadow
(
Event *ev
)
{
// HACK FIXME
//
// temporary shadow flag
//
edict->s.renderfx &= ~RF_XFLIP;
}
EXPORT_FROM_DLL void Sentient::Archive
(
Archiver &arc
)
{
int i;
int num;
Entity::Archive( arc );
num = inventory.NumObjects();
arc.WriteInteger( num );
for( i = 1; i <= num; i++ )
{
arc.WriteInteger( inventory.ObjectAt( i ) );
}
arc.WriteObjectPointer( currentWeapon );
arc.WriteObjectPointer( currentItem );
arc.WriteObjectPointer( newWeapon );
arc.WriteString( currentAnim );
arc.WriteBoolean( animOverride );
arc.WriteEvent( *tempAnimEvent );
arc.WriteString( gun_bone_group_name );
arc.WriteBoolean( stopanimating_tillchange );
arc.WriteInteger( poweruptype );
arc.WriteInteger( poweruptimer );
arc.WriteBoolean( sentientFrozen );
arc.WriteBoolean( dropweapon );
arc.WriteVector( gunoffset );
arc.WriteVector( eyeposition );
arc.WriteFloat( firedowntime );
arc.WriteBoolean( firedown );
arc.WriteString( saveskin );
arc.WriteString( savemodel );
//###
arc.WriteSafePointer(rope_grabbed);
arc.WriteFloat(rope_debounce_time);
arc.WriteBoolean(ropesound);
arc.WriteInteger(autoweaponswitch);
arc.WriteInteger(weaponoverride);
//###
}
EXPORT_FROM_DLL void Sentient::Unarchive
(
Archiver &arc
)
{
int i;
int num;
int temp;
Entity::Unarchive( arc );
inventory.FreeObjectList();
num = arc.ReadInteger();
for( i = 1; i <= num; i++ )
{
temp = arc.ReadInteger();
inventory.AddObject( temp );
}
arc.ReadObjectPointer( ( Class ** )&currentWeapon );
arc.ReadObjectPointer( ( Class ** )&currentItem );
arc.ReadObjectPointer( ( Class ** )&newWeapon );
arc.ReadString( &currentAnim );
arc.ReadBoolean( &animOverride );
//FIXME
// make a ReadEventPointer function
tempAnimEvent = new Event( arc.ReadEvent() );
arc.ReadString( &gun_bone_group_name );
arc.ReadBoolean( &stopanimating_tillchange );
arc.ReadInteger( &poweruptype );
arc.ReadInteger( &poweruptimer );
arc.ReadBoolean( &sentientFrozen );
arc.ReadBoolean( &dropweapon );
arc.ReadVector( &gunoffset );
arc.ReadVector( &eyeposition );
arc.ReadFloat( &firedowntime );
arc.ReadBoolean( &firedown );
arc.ReadString( &saveskin );
arc.ReadString( &savemodel );
//###
arc.ReadSafePointer(&rope_grabbed);
arc.ReadFloat(&rope_debounce_time);
arc.ReadBoolean(&ropesound);
arc.ReadInteger(&autoweaponswitch);
arc.ReadInteger(&weaponoverride);
//###
}
void Sentient::WritePersistantData
(
SpawnArgGroup &group
)
{
int i;
int num;
Entity *ent;
G_SetSpawnArg( "classname", getClassname() );
if ( currentWeapon )
{
G_SetSpawnArg( "current_weapon", currentWeapon->getClassname() );
}
if ( currentItem )
{
G_SetSpawnArg( "current_item", currentItem->getClassname() );
}
G_SetIntArg( "health", health );
G_SetIntArg( "max_health", max_health );
G_SetIntArg( "savegame", 1 );
group.AddArgs();
num = inventory.NumObjects();
for( i = 1; i <= num; i++ )
{
ent = G_GetEntity( inventory.ObjectAt( i ) );
if ( ent && ent->isSubclassOf( Item ) )
{
G_InitSpawnArguments();
( ( Item * )ent )->CreateSpawnArgs();
G_SetSpawnArg( "classname", ent->getClassname() );
G_SetIntArg( "savegame", 1 );
group.AddArgs();
}
}
G_InitSpawnArguments();
}
void Sentient::RestorePersistantData
(
SpawnArgGroup &group
)
{
str weap;
str item;
Entity *ent;
int i;
int num;
Item *it;
num = group.NumInGroup();
for( i = 2; i <= num; i++ )
{
group.RestoreArgs( i );
game.force_entnum = false;
ent = G_CallSpawn();
if ( !ent->isSubclassOf( Item ) )
{
error( "RestorePersistantData", "Loaded non-Item object of class %s\n", ent->getClassname() );
}
// G_CallSpawn clears all the spawnargs
group.RestoreArgs( i );
it = ( Item * )ent;
it->SetOwner( this );
it->ProcessPendingEvents();
AddItem( it );
}
group.RestoreArgs( 1 );
weap = G_GetStringArg( "current_weapon" );
item = G_GetStringArg( "current_item" );
health = G_GetIntArg( "health", 100 );
max_health = G_GetIntArg( "max_health", 100 );
if ( weap.length() )
{
it = FindItem( weap.c_str() );
assert( it );
if ( it )
{
if ( !it->isSubclassOf( Weapon ) )
{
error( "RestorePersistantData", "Tried to set non-weapon of type %s as current weapon\n", it->getClassname() );
}
SetCurrentWeapon( ( Weapon * )it );
}
}
if ( item.length() )
{
it = FindItem( item.c_str() );
assert( it );
if ( it )
{
if ( !it->isSubclassOf( InventoryItem ) )
{
error( "RestorePersistantData", "Tried to set non-inventory item of type %s as current item\n", it->getClassname() );
}
currentItem = ( InventoryItem * )it;
}
}
}
EXPORT_FROM_DLL void Sentient::Freeze
(
Event *ev
)
{
sentientFrozen = true;
}
EXPORT_FROM_DLL void Sentient::UnFreeze
(
Event *ev
)
{
sentientFrozen = false;
}
void Sentient::ImpactDamage
(
Event *ev
)
{
float damg;
if ( deathmatch->value )
{
damg = velocity.length();
damg /= 20.0f;
}
else
{
damg = 100;
}
setMoveType( MOVETYPE_STEP );
Damage(world, enemy, damg,
worldorigin, vec_zero, vec_zero, 0,
0, MOD_IMPACT,
-1, -1, 1.0f );
}
void Sentient::WeaponUse
(
Event *ev
)
{
Event *event;
if ( !currentWeapon )
return;
currentWeapon->ForceState( WEAPON_READY );
event = new Event( EV_Weapon_SecondaryUse );
event->AddEntity( this );
currentWeapon->ProcessEvent( event );
}
void Sentient::DoubleArmor
(
void
)
{
int i,n;
n = inventory.NumObjects();
for( i = 1; i <= n; i++ )
{
Item *item;
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( item->isSubclassOf( Armor ) )
item->Set( item->Amount() * 2 );
}
}
void Sentient::WeaponKnockedFromHands
(
void
)
{
str realname;
realname = GetRandomAlias( "snd_lostweapon" );
if ( realname.length() > 1 )
{
sound( realname, 1, CHAN_VOICE );
}
else
{
RandomSound( "snd_pain", 1, CHAN_VOICE );
}
}
void Sentient::SetDropWeapon
(
Event *ev
)
{
dropweapon = ev->GetInteger( 1 );
}
void Sentient::DropWeaponNowEvent
(
Event *ev
)
{
DropCurrentWeapon();
}
//### ====================================================
// added 2015 stuff
// flamethrower stop the hurting
void Sentient::HurtFlame(Event *ev)
{
edict->s.effects &= ~EF_FLAMES;
// turn off the actor's heat signature if dead
if(deadflag)
edict->s.effects &= ~EF_WARM;
}
// to tell if it's on a bike
qboolean Sentient::IsOnBike(void)
{
if(this->isSubclassOf(Player))
{
if(((Player *)this)->GetHoverbike())
return true;
else
return false;
}
else
{
return false;
}
}
// this allows for some special case weapon switching
// when auto weapon switching is off
void Sentient::NonAutoChangeWeapon (Weapon *weapon)
{
if ( weapon == currentWeapon )
{
return;
}
if ( currentWeapon )
{
if ( !weapon->ReadyToUse() )
{
return;
}
if ( !currentWeapon->ReadyToChange() )
{
return;
}
// if using a magnum, switch to dual magnum
if(!stricmp(currentWeapon->getClassname(), "Magnum") &&
!stricmp(weapon->getClassname(), "DualMagnum"))
{
newWeapon = weapon;
currentWeapon->PutAway();
}
}
else
{
SetCurrentWeapon( weapon );
}
}
//###