2764 lines
No EOL
57 KiB
C++
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 ** )¤tWeapon );
|
|
arc.ReadObjectPointer( ( Class ** )¤tItem );
|
|
arc.ReadObjectPointer( ( Class ** )&newWeapon );
|
|
arc.ReadString( ¤tAnim );
|
|
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 );
|
|
}
|
|
}
|
|
//###
|