ef2gamesource/dlls/game/sentient.cpp
2012-12-31 17:38:54 +01:00

5124 lines
108 KiB
C++

//-----------------------------------------------------------------------------
//
// $Logfile:: /EF2/Code/DLLs/game/sentient.cpp $
// $Revision:: 197 $
// $Author:: Singlis $
// $Date:: 9/26/03 2:36p $
//
// 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 "_pch_cpp.h"
#include "entity.h"
#include "sentient.h"
#include "weapon.h"
#include "WeaponDualWield.h"
#include "weaputils.h"
#include "scriptmaster.h"
#include "ammo.h"
#include "armor.h"
#include "misc.h"
#include "inventoryitem.h"
#include "player.h"
#include "actor.h"
#include "mp_manager.hpp"
#include <qcommon/gameplaymanager.h>
#include "decals.h"
Event EV_Sentient_BeginAttack
(
"beginattack",
EV_DEFAULT,
NULL,
NULL,
"Called before attack animation begins"
);
Event EV_Sentient_EndAttack
(
"endattack",
EV_DEFAULT,
NULL,
NULL,
"Called after attack animation ends"
);
Event EV_Sentient_Attack
(
"fire",
EV_DEFAULT,
"SS",
"hand mode",
"Fires the weapon in the specified hand. With the specified mode (primary, alternate)"
);
Event EV_Sentient_StopFire
(
"stopfire",
EV_TIKIONLY,
"s",
"hand",
"Stops the firing of the weapon in the specified hand."
);
Event EV_Sentient_StartChargeFire
(
"startcharge",
EV_DEFAULT,
"s",
"firemode",
"Draws back the bow string"
);
Event EV_Sentient_ReleaseAttack
(
"releasefire",
EV_TIKIONLY,
"f",
"fireholdtime",
"Releases the attack in the time specified."
);
Event EV_Sentient_GiveWeapon
(
"weapon",
EV_DEFAULT,
"s",
"weapon_modelname",
"Gives the sentient the weapon specified."
);
Event EV_Sentient_Take
(
"take",
EV_DEFAULT,
"s",
"item_name",
"Takes away the specified item from the sentient."
);
Event EV_Sentient_GiveAmmo
(
"ammo",
EV_DEFAULT,
"siI",
"type amount max_amount",
"Gives the sentient some ammo."
);
Event EV_Sentient_GiveAmmoOverTime
(
"giveAmmoOverTime",
EV_DEFAULT,
"sif",
"type amount time",
"Gives the sentient some ammo over the specified period of time."
);
Event EV_Sentient_GiveArmor
(
"armor",
EV_DEFAULT,
"sFB",
"type amount pickedup",
"Gives the sentient some armor."
);
Event EV_Sentient_GiveItem
(
"item",
EV_DEFAULT,
"si",
"type amount",
"Gives the sentient the specified amount of the specified item."
);
Event EV_Sentient_GiveTargetname
(
"give",
EV_CHEAT | EV_TIKIONLY | EV_SCRIPTONLY,
"s",
"name",
"Gives the sentient the targeted item."
);
Event EV_Sentient_GiveHealth
(
"health",
EV_CHEAT | EV_TIKIONLY,
"f",
"health",
"Gives the sentient the specified amount health."
);
Event EV_Sentient_SetBloodModel
(
"bloodmodel",
EV_TIKIONLY,
"s",
"bloodModel",
"set the model to be used when showing blood"
);
Event EV_Sentient_TurnOffShadow
(
"noshadow",
EV_TIKIONLY,
NULL,
NULL,
"Turns off the shadow for this sentient."
);
Event EV_Sentient_TurnOnShadow
(
"shadow",
EV_TIKIONLY,
NULL,
NULL,
"Turns on the shadow for this sentient."
);
Event EV_Sentient_AddImmunity
(
"immune",
EV_DEFAULT,
"sSSSSS",
"immune_string1 immune_string2 immune_string3 immune_string4 immune_string5 immune_string6",
"Adds to the immunity list for this sentient."
);
Event EV_Sentient_AddResistance
(
"resistance",
EV_DEFAULT,
"si",
"resistance_string resistance_amount",
"Adds to the resistance list for this sentient."
);
Event EV_Sentient_RemoveImmunity
(
"removeimmune",
EV_DEFAULT,
"sSSSSS",
"immune_string1 immune_string2 immune_string3 immune_string4 immune_string5 immune_string6",
"Removes from the immunity list for this sentient."
);
Event EV_Sentient_RemoveResistance
(
"removeresistance",
EV_DEFAULT,
"s",
"resistance_string",
"Removes from the resistance list for this sentient."
);
Event EV_Sentient_UpdateOffsetColor
(
"updateoffsetcolor",
EV_CODEONLY,
NULL,
NULL,
"Updates the offset color."
);
Event EV_Sentient_JumpXY
(
"jumpxy",
EV_DEFAULT,
"fff",
"forwardmove sidemove speed",
"Makes the sentient jump."
);
Event EV_Sentient_MeleeAttackStart
(
"meleeattackstart",
EV_TIKIONLY,
"S",
"hand",
"Is the start of the sentient's melee attack."
);
Event EV_Sentient_MeleeAttackEnd
(
"meleeattackend",
EV_TIKIONLY,
"S",
"hand",
"Is the end of the sentient's melee attack."
);
Event EV_Sentient_RangedAttackStart
(
"rangedattackstart",
EV_TIKIONLY,
NULL,
NULL,
"Is the start of the sentient's hitscan attack."
);
Event EV_Sentient_RangedAttackEnd
(
"rangedattackend",
EV_TIKIONLY,
NULL,
NULL,
"Is the end of the sentient's hitscan attack."
);
Event EV_Sentient_BlockStart
(
"blockstart",
EV_TIKIONLY,
NULL,
NULL,
"Is the start of the sentient's block."
);
Event EV_Sentient_BlockEnd
(
"blockend",
EV_TIKIONLY,
NULL,
NULL,
"Is the end of the sentient's block."
);
Event EV_Sentient_StunStart
(
"stunstart",
EV_CODEONLY,
NULL,
NULL,
"Is the start of the sentient's stun."
);
Event EV_Sentient_StunEnd
(
"stunend",
EV_CODEONLY,
NULL,
NULL,
"Is the end of the sentient's stun."
);
Event EV_Sentient_SetMouthAngle
(
"mouthangle",
EV_DEFAULT,
"f",
"mouth_angle",
"Sets the mouth angle of the sentient."
);
Event EV_Sentient_SetMaxMouthAngle
(
"maxmouthangle",
EV_TIKIONLY,
"f",
"max_mouth_angle",
"Sets the max mouth angle."
);
Event EV_Sentient_OnFire
(
"onfire",
EV_CODEONLY,
NULL,
NULL,
"Called every frame when the sentient is on fire."
);
Event EV_Sentient_StopOnFire
(
"stoponfire",
EV_CODEONLY,
NULL,
NULL,
"Stops the sentient from being on fire."
);
Event EV_Sentient_SpawnBloodyGibs
(
"spawnbloodygibs",
EV_DEFAULT,
"IF",
"number_of_gibs scale",
"Spawns some bloody generic gibs."
);
Event EV_Sentient_SetMaxGibs
(
"maxgibs",
EV_TIKIONLY,
"i",
"max_number_of_gibs",
"Sets the maximum amount of generic gibs this sentient will spawn when hit."
);
Event EV_Sentient_CheckAnimations
(
"checkanims",
EV_CONSOLE,
NULL,
NULL,
"Check the animations in the .tik file versus the statefile"
);
Event EV_Sentient_SetStateFile
(
"setstatefile",
EV_DEFAULT,
"s",
"state_file",
"Change the state file associated with this character"
);
Event EV_Sentient_AddHealth
(
"addhealth",
EV_SCRIPTONLY,
"fF",
"health_to_add maxhealth",
"Adds health to the sentient."
);
Event EV_Sentient_SetViewMode
(
"setviewmode",
EV_SCRIPTONLY,
"sB",
"viewModeName override",
"Puts this sentient into the specified view mode.\n"
"Override defaults to true."
);
Event EV_Sentient_GetActiveWeaponName
(
"getActiveWeaponName",
EV_SCRIPTONLY,
"@sS",
"weaponName hand",
"Gets the name of the weapon in the specified hand (left, right, or dual).\n"
"If no hand is specified it will return the first it finds"
);
Event EV_Sentient_CatchOnFire
(
"catchonfire",
EV_SCRIPTONLY,
NULL,
NULL,
"Catches the actor on fire."
);
Event EV_Sentient_AddMeleeAttacker
(
"addmeleeattacker",
EV_CODEONLY,
"e",
"attack_ent",
"Adds the entity to the melee attacker list."
);
Event EV_Sentient_SwipeOn
(
"swipeon",
EV_TIKIONLY,
"s",
"hand",
"Turn on the sword swiping for the weapon in the specified hand"
);
Event EV_Sentient_SwipeOff
(
"swipeoff",
EV_TIKIONLY,
"s",
"hand",
"Turn off the sword swiping for the weapon in the specified hand"
);
Event EV_Sentient_WeaponAnim
(
"weaponanimon",
EV_TIKIONLY,
"ss",
"animname hand",
"Put the weapon in hand in the animation specified"
);
Event EV_Sentient_HealOverTime
(
"healovertime",
EV_DEFAULT,
"ffff",
"add_immediately add_at_interval interval max_percentage",
"Will add specified amount of health immediatly, then add more at each specified time interval\n"
"until health reaches the maxPercentage"
);
Event EV_Sentient_HealAtInterval
(
"healatinterval",
EV_CODEONLY,
"fff",
"percentageToAdd interval maxPercentage",
"Will add the specified amount to health, then, if necessary , will generate a new event of its\n"
"own type to continue the regen process"
);
Event EV_Sentient_GroupMemberInjured
(
"groupmemberinjured",
EV_DEFAULT,
"b",
"injured",
"Informs us that a group member is injured or not"
);
Event EV_Sentient_SetCriticalHealthPercentage
(
"setcriticalhealthpercentage",
EV_DEFAULT,
"f",
"percentage",
"Sets the percentage of health that qualifies as critical -- Note, values must be entered as\n"
"floating point numbers... .10 for example is 10 percent"
);
Event EV_Sentient_HeadWatchAllowed
(
"headwatchallowed",
EV_DEFAULT,
"B",
"flag",
"Sets whether to headwatch or not, default is true."
);
Event EV_Sentient_SetDamageThreshold
(
"setdamagethreshold",
EV_DEFAULT,
"ff",
"maxDamage duration",
"Sets up the damage threshold"
);
Event EV_Sentient_DisplayFireEffect
(
"displayfireeffect",
EV_DEFAULT,
"B",
"flag",
"Sets whether or not this sentient displays the fire effect when on fire."
);
Event EV_Sentient_ClearDamageThreshold
(
"cleardamagethreshold",
EV_DEFAULT,
NULL,
NULL,
"Clears out and resets the damage threshold"
);
Event EV_Sentient_DropItem
(
"dropitem",
EV_DEFAULT,
"s",
"itemName",
"Drops the item by the specified name."
);
Event EV_Sentient_SetArmorActiveStatus
(
"setmyarmorstatus",
EV_DEFAULT,
"b",
"flag",
"Sets The Active Status on Armor"
);
Event EV_Sentient_SetArmorMultiplier
(
"setmyarmormultiplier",
EV_DEFAULT,
"f",
"multiplier",
"Sets the mulitplier of the armor"
);
Event EV_Sentient_AddToMyArmor
(
"addtomyarmor",
EV_DEFAULT,
"f",
"amountToAdd",
"Adds the amount to the current Armor"
);
Event EV_Sentient_SetMyArmorAmount
(
"setmyarmoramount",
EV_DEFAULT,
"f",
"amount",
"Sets the amount of armor to the specifed number"
);
Event EV_Sentient_FreeInventory
(
"freeInventory",
EV_DEFAULT,
NULL,
NULL,
"Frees the sentient's inventory"
);
Event EV_Sentient_ArmorCommand
(
"armorcommand",
EV_CODEONLY,
"sSSSSSSS",
"arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8",
"Pass the args to the active armor"
);
Event EV_Sentient_SetHateModifier
(
"sethatemodifier",
EV_DEFAULT,
"f",
"hate_modifier",
"Sets the hate modifier"
);
Event EV_Sentient_CacheStateMachineAnims
(
"cacheStateMachineAnims",
EV_CACHE,
"s",
"stateMachineName",
"Caches all of the anims needed by the statemachine"
);
CLASS_DECLARATION( Entity, Sentient, NULL )
{
{ &EV_Sentient_BeginAttack, &Sentient::BeginAttack },
{ &EV_Sentient_EndAttack, &Sentient::EndAttack },
{ &EV_Sentient_Attack, &Sentient::FireWeapon },
{ &EV_Sentient_StopFire, &Sentient::StopFireWeapon },
{ &EV_Sentient_StartChargeFire, &Sentient::StartChargeFire },
{ &EV_Sentient_ReleaseAttack, &Sentient::ReleaseFireWeapon },
{ &EV_Sentient_GiveAmmo, &Sentient::EventGiveAmmo },
{ &EV_Sentient_GiveAmmoOverTime, &Sentient::giveAmmoOverTime },
{ &EV_Sentient_GiveWeapon, &Sentient::EventGiveItem },
{ &EV_Sentient_GiveArmor, &Sentient::EventGiveArmor },
{ &EV_Sentient_GiveItem, &Sentient::EventGiveItem },
{ &EV_Sentient_GiveHealth, &Sentient::EventGiveHealth },
{ &EV_Sentient_Take, &Sentient::EventTake },
{ &EV_Sentient_SetBloodModel, &Sentient::SetBloodModel },
{ &EV_Sentient_GiveTargetname, &Sentient::EventGiveTargetname },
{ &EV_Damage, &Sentient::ArmorDamage },
{ &EV_Sentient_TurnOffShadow, &Sentient::TurnOffShadow },
{ &EV_Sentient_TurnOnShadow, &Sentient::TurnOnShadow },
{ &EV_Sentient_AddImmunity, &Sentient::AddImmunity },
{ &EV_Sentient_RemoveImmunity, &Sentient::RemoveImmunity },
{ &EV_Sentient_AddResistance, &Sentient::AddResistance },
{ &EV_Sentient_RemoveResistance, &Sentient::RemoveResistance },
{ &EV_Sentient_UpdateOffsetColor, &Sentient::UpdateOffsetColor },
{ &EV_Sentient_JumpXY, &Sentient::JumpXY },
{ &EV_Sentient_MeleeAttackStart, &Sentient::MeleeAttackStart },
{ &EV_Sentient_MeleeAttackEnd, &Sentient::MeleeAttackEnd },
{ &EV_Sentient_RangedAttackStart, &Sentient::RangedAttackStart },
{ &EV_Sentient_RangedAttackEnd, &Sentient::RangedAttackEnd },
{ &EV_Sentient_BlockStart, &Sentient::BlockStart },
{ &EV_Sentient_BlockEnd, &Sentient::BlockEnd },
{ &EV_Sentient_StunStart, &Sentient::StunStart },
{ &EV_Sentient_StunEnd, &Sentient::StunEnd },
{ &EV_Sentient_SetMaxMouthAngle, &Sentient::SetMaxMouthAngle },
{ &EV_Sentient_OnFire, &Sentient::OnFire },
{ &EV_Sentient_StopOnFire, &Sentient::StopOnFire },
{ &EV_Sentient_SpawnBloodyGibs, &Sentient::SpawnBloodyGibs },
{ &EV_Sentient_SetMaxGibs, &Sentient::SetMaxGibs },
{ &EV_Sentient_CheckAnimations, &Sentient::CheckAnimations },
{ &EV_Sentient_SetStateFile, &Sentient::SetStateFile },
{ &EV_Sentient_AddHealth, &Sentient::AddHealth },
{ &EV_Sentient_SetViewMode, &Sentient::setViewMode },
{ &EV_Sentient_GetActiveWeaponName, &Sentient::getActiveWeaponName },
{ &EV_Sentient_CatchOnFire, &Sentient::CatchOnFire },
{ &EV_Sentient_SwipeOn, &Sentient::SwipeOn },
{ &EV_Sentient_SwipeOff, &Sentient::SwipeOff },
{ &EV_Sentient_HealOverTime, &Sentient::AddHealthOverTime },
{ &EV_Sentient_HealAtInterval, &Sentient::AddHealthAtInterval },
{ &EV_Sentient_SetCriticalHealthPercentage, &Sentient::SetCriticalHealthPercentage },
{ &EV_Sentient_HeadWatchAllowed, &Sentient::HeadWatchAllowed },
{ &EV_Sentient_WeaponAnim, &Sentient::SetWeaponAnim },
{ &EV_Sentient_SetDamageThreshold, &Sentient::SetDamageThreshold },
{ &EV_Sentient_DisplayFireEffect, &Sentient::DisplayFireEffect },
{ &EV_Sentient_ClearDamageThreshold, &Sentient::ClearDamageThreshold },
{ &EV_Sentient_DropItem, &Sentient::DropItemEvent },
{ &EV_Sentient_SetArmorActiveStatus, &Sentient::SetArmorActiveStatus },
{ &EV_Sentient_SetArmorMultiplier, &Sentient::SetArmorMultiplier },
{ &EV_Sentient_AddToMyArmor, &Sentient::AddToMyArmor },
{ &EV_Sentient_SetMyArmorAmount, &Sentient::SetMyArmorAmount },
{ &EV_Sentient_ArmorCommand, &Sentient::ArmorEvent },
{ &EV_Sentient_SetHateModifier, &Sentient::SetHateModifier },
{ &EV_Sentient_FreeInventory, &Sentient::FreeInventory },
{ &EV_Sentient_CacheStateMachineAnims, &Sentient::cacheStateMachineAnims },
{ NULL, NULL }
};
Container<Sentient *> SentientList;
Sentient::Sentient()
{
animate = new Animate( this );
SentientList.AddObject( ( Sentient * )this );
setContents( CONTENTS_BODY );
inventory.ClearObjectList();
newWeapon = NULL;
currentBaseArmor = NULL;
eyeposition = Vector(0, 0, 64);
shotsFiredThisVolley = 0;
//firing_frame = -1;
//firing_anim = -1;
knock_start_time = 0;
// do better lighting on all sentients
edict->s.renderfx |= RF_EXTRALIGHT;
edict->s.renderfx |= RF_SHADOW;
// sentients have precise shadows
edict->s.renderfx |= RF_SHADOW_PRECISE;
in_melee_attack = false;
in_ranged_attack = false;
in_block = false;
in_stun = false;
attack_blocked = false;
max_mouth_angle = 10;
// touch triggers by default
flags |= FL_TOUCH_TRIGGERS;
on_fire = false;
max_gibs = 0;
next_bleed_time = 0;
last_surface_hit = -1;
last_bone_hit = -1;
_canSendInjuredEvent = true;
_headWatchAllowed = true;
_hateModifier = 1.0;
addAffectingViewModes( gi.GetViewModeClassMask( "sentient" ) );
SetCriticalHealthPercentage( .5 );
//Initial Max Value for _damageThreshold is set to -1 so that
//we won't do a lot of processing unless we specifically set
//our max value
_damageThreshold.maxDamage = -1.0f;
_displayFireEffect = true;
on_fire_tagnums[ 0 ] = -1;
on_fire_tagnums[ 1 ] = -1;
on_fire_tagnums[ 2 ] = -1;
}
Sentient::~Sentient()
{
Sentient *sentient;
for ( int i = 1; i <= resistances.NumObjects(); i++ )
{
if (resistances.ObjectAt( i ) )
{
delete resistances.ObjectAt( i );
resistances.ObjectAt( i ) = 0;
}
}
sentient = this;
SentientList.RemoveObject( sentient );
FreeInventory();
}
// HACK HACK HACK
void Sentient::UpdateOffsetColor( Event * )
{
G_SetConstantLight( &edict->s.constantLight, &offset_color[ 0 ], &offset_color[ 1 ], &offset_color[ 2 ], NULL );
offset_color -= offset_delta;
offset_time -= FRAMETIME;
if ( offset_time > 0.0f )
{
PostEvent( EV_Sentient_UpdateOffsetColor, FRAMETIME );
}
else
{
CancelEventsOfType( EV_Sentient_UpdateOffsetColor );
edict->s.renderfx &= ~RF_LIGHTOFFSET;
offset_color[ 0 ] = offset_color[ 1 ] = offset_color[ 2 ] = 0;
G_SetConstantLight( &edict->s.constantLight, &offset_color[ 0 ], &offset_color[ 1 ], &offset_color[ 2 ], NULL );
}
}
void Sentient::SetOffsetColor( float r, float g, float b, float time )
{
// kill all pending events
CancelEventsOfType( EV_Sentient_UpdateOffsetColor );
offset_color[ 0 ] = r;
offset_color[ 1 ] = g;
offset_color[ 2 ] = b;
G_SetConstantLight( &edict->s.constantLight, &offset_color[ 0 ], &offset_color[ 1 ], &offset_color[ 2 ], NULL );
// delta is a little less so we don't go below zero
offset_delta = offset_color * ( FRAMETIME / ( time + ( 0.5f * FRAMETIME ) ) );
offset_time = time;
edict->s.renderfx |= RF_LIGHTOFFSET;
PostEvent( EV_Sentient_UpdateOffsetColor, FRAMETIME );
}
Vector Sentient::EyePosition( void )
{
int tagNum;
tagNum = gi.Tag_NumForName( edict->s.modelindex, "tag_eyes" );
if ( tagNum >= 0 )
{
Vector tag_pos;
GetTag( tagNum, &tag_pos );
return tag_pos;
}
else
{
return origin + eyeposition;
}
}
Vector Sentient::GunPosition( void )
{
return origin;
}
void Sentient::EventGiveHealth( Event *ev )
{
health = ev->GetFloat( 1 );
}
void Sentient::AddHealth( Event *ev )
{
float health_to_add;
float maxhealth;
health_to_add = ev->GetFloat( 1 );
if ( ev->NumArgs() > 1 )
maxhealth = ev->GetFloat( 2 );
else
maxhealth = max_health;
AddHealth( health_to_add, maxhealth );
}
void Sentient::AddHealth( float healthToAdd, float maxHealth )
{
float tempMaxHealth;
float newHealth;
if ( maxHealth )
tempMaxHealth = maxHealth;
else
tempMaxHealth = max_health;
newHealth = health + healthToAdd;
// See if the health is already above the max (don't do anything if it is)
if ( health >= tempMaxHealth )
return;
if ( newHealth > tempMaxHealth )
SetHealth(tempMaxHealth);
else
SetHealth(newHealth);
}
void Sentient::SetBloodModel( Event *ev )
{
str name;
str cache_name;
str models_dir = "models/";
if ( ev->NumArgs() < 1 )
return;
blood_model = ev->GetString( 1 );
cache_name = models_dir + blood_model;
CacheResource( cache_name.c_str(), this );
name = GetBloodSpurtName();
if ( name.length() )
{
cache_name = models_dir + name;
CacheResource( cache_name.c_str(), this );
}
name = GetBloodSplatName();
if ( name.length() )
CacheResource( name.c_str(), this );
name = GetGibName();
if ( name.length() )
{
cache_name = models_dir + name;
CacheResource( cache_name.c_str(), this );
}
}
void Sentient::StartChargeFire( Event *ev )
{
Weapon * activeWeapon;
firemode_t mode=FIRE_MODE1;
weaponhand_t hand=WEAPON_RIGHT;
hand = WeaponHandNameToNum( "dualhand" );
if ( ev->NumArgs() > 0 )
{
mode = WeaponModeNameToNum( ev->GetString( 1 ) );
if ( mode < 0 )
return;
}
if ( hand > MAX_ACTIVE_WEAPONS )
{
warning( "Sentient::StartChargeFire", "Weapon hand number \"%d\" is out of bounds of 0 to MAX_ACTIVE_WEAPONS:%d\n", hand, MAX_ACTIVE_WEAPONS );
return;
}
// start charging the active weapon
activeWeapon = activeWeaponList[ (int)hand ];
// Save off firing animation and frame
/*firing_anim = ev->GetAnimationNumber();
firing_frame = ev->GetAnimationFrame();*/
knock_start_time = level.time;
/* if ( ( activeWeapon ) && activeWeapon->ReadyToFire( mode ) )
{
knock_start_time = level.time;
if ( mode == FIRE_MODE1 )
activeWeapon->SetAnim( "charge", EV_Weapon_DrawBowStrain);
else if ( mode == FIRE_MODE2 )
activeWeapon->SetAnim( "alternatecharge", EV_Weapon_AltDrawBowStrain );
} */
}
void Sentient::BeginAttack( Event * )
{
if ( !inheritsFrom( "Player" ) )
{
return;
}
Weapon *weapon = GetActiveWeapon( WEAPON_RIGHT );
if ( !weapon )
{
weapon = GetActiveWeapon( WEAPON_LEFT );
}
if ( !weapon )
{
return;
}
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
float animationRate = 1.0f;
if (gpm->hasProperty( weapon->getArchetype(), "animationRate" ) )
{
animationRate = gpm->getFloatValue( weapon->getArchetype(), "animationRate" );
}
animate->SetAnimationRate( animationRate );
}
void Sentient::EndAttack( Event * )
{
animate->RestoreAnimationRate();
}
void Sentient::FireWeapon( Event *ev )
{
Weapon *activeWeapon;
firemode_t mode=FIRE_MODE1;
int number=0;
str modestring, side;
if ( ev->NumArgs() > 0 )
{
side = ev->GetString( 1 );
if ( !stricmp( side, "righthand" ) )
number = WEAPON_RIGHT;
else if ( !stricmp( side, "lefthand" ) )
number = WEAPON_LEFT;
else if ( !stricmp( side, "dualhand" ) )
{
number = WEAPON_DUAL;
if ( ev->NumArgs() == 2 )
{
modestring = ev->GetString( 2 );
mode = WeaponModeNameToNum(modestring);
//if ( !modestring.icmp( "primary" ) )
// mode = FIRE_MODE1;
//else if ( !modestring.icmp( "alternate" ) )
// mode = FIRE_MODE2;
//else
// warning( "Sentient::FireWeapon", "Invalid fire mode %s\n", modestring );
}
}
else
number = atoi( side.c_str() );
}
if ( ( number > MAX_ACTIVE_WEAPONS ) || ( number < 0 ) )
{
warning( "Sentient::FireWeapon", "Weapon number \"%d\" is out of bounds of 0 to MAX_ACTIVE_WEAPONS:%d\n", number, MAX_ACTIVE_WEAPONS );
return;
}
// Save off firing animation and frame
//firing_anim = ev->GetAnimationNumber();
//firing_frame = ev->GetAnimationFrame();
activeWeapon = activeWeaponList[ number ];
if ( ( activeWeapon ) && activeWeapon->ReadyToFire( mode ) )
{
activeWeapon->Fire( mode );
}
else
{
if ( !activeWeapon )
gi.WDPrintf( "No active weapon in slot #: \"%i\"\n", number );
}
}
void Sentient::StopFireWeapon( Event *ev )
{
Weapon *activeWeapon;
int number=0;
str side;
if ( ev->NumArgs() > 0 )
{
side = ev->GetString( 1 );
if ( !stricmp( side, "righthand" ) )
number = WEAPON_RIGHT;
else if ( !stricmp( side, "lefthand" ) )
number = WEAPON_LEFT;
else if ( !stricmp( side, "dualhand" ) )
number = WEAPON_DUAL;
else
number = atoi( side.c_str() );
}
if ( ( number > MAX_ACTIVE_WEAPONS ) || ( number < 0 ) )
{
warning( "Sentient::StopFireWeapon", "Weapon number \"%d\" is out of bounds of 0 to MAX_ACTIVE_WEAPONS:%d\n", number, MAX_ACTIVE_WEAPONS );
return;
}
activeWeapon = activeWeaponList[ number ];
if ( activeWeapon )
{
if ( activeWeapon->animate->HasAnim("fire_stop") )
activeWeapon->SetAnim("fire_stop");
else
activeWeapon->ForceIdle();
}
else
{
gi.WDPrintf( "No active weapon in slot #: \"%i\"\n", number );
}
}
void Sentient::ReleaseFireWeapon( Event *ev )
{
Weapon *activeWeapon;
float charge_time=0;
firemode_t mode=FIRE_MODE1;
int number=0;
str modestring, side;
charge_time = level.time - knock_start_time;
// Reset the down timer
knock_start_time = 0;
if ( ev->NumArgs() > 0 )
{
side = ev->GetString( 1 );
if ( !stricmp( side, "righthand" ) )
number = WEAPON_RIGHT;
else if ( !stricmp( side, "lefthand" ) )
number = WEAPON_LEFT;
else if ( !stricmp( side, "dualhand" ) )
{
number = WEAPON_DUAL;
if ( ev->NumArgs() == 2 )
{
modestring = ev->GetString( 2 );
mode = WeaponModeNameToNum(modestring);
//if ( !modestring.icmp( "primary" ) )
// mode = FIRE_MODE1;
//else if ( !modestring.icmp( "alternate" ) )
// mode = FIRE_MODE2;
//else
// warning( "Sentient::FireWeapon", "Invalid fire mode %s\n", modestring );
}
}
else
number = atoi( side.c_str() );
}
if ( ( number > MAX_ACTIVE_WEAPONS ) || ( number < 0 ) )
{
warning( "Sentient::FireWeapon", "Weapon number \"%d\" is out of bounds of 0 to MAX_ACTIVE_WEAPONS:%d\n", number, MAX_ACTIVE_WEAPONS );
return;
}
// Save off firing animation and frame
//firing_anim = ev->GetAnimationNumber();
//firing_frame = ev->GetAnimationFrame();
activeWeapon = activeWeaponList[ number ];
if ( activeWeapon )
{
activeWeapon->ReleaseFire( mode, charge_time );
}
}
void Sentient::AddItem( const Item *object )
{
inventory.AddObject( object->entnum );
}
void Sentient::RemoveItem( Item *object )
{
int i;
inventory.RemoveObject( object->entnum );
if ( object->isSubclassOf( Weapon ) )
DeactivateWeapon( (Weapon *)object );
for ( i=0; i<MAX_ACTIVE_WEAPONS; i++ )
{
Weapon *weap = activeWeaponList[i];
if ( object == weap )
{
activeWeaponList[i] = NULL;
}
}
//
// let the sent know about it
//
RemovedItem( object );
}
Item *Sentient::FindBaseArmor( void )
{
int num;
int i;
Item *item;
num = inventory.NumObjects();
for( i = 1; i <= num; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( item );
if ( item->isSubclassOf( Armor ) )
{
return item;
}
}
return NULL;
}
//--------------------------------------------------------------
//
// Name: FindItemByExternalName
// Class: Sentient
//
// Description: Finds the item by its name
//
// Parameters: const char *itemname -- Name to find
// Item *current (default, 0) -- Item to start searching from
//
// Returns: Item * or NULL
//
//--------------------------------------------------------------
Item *Sentient::FindItemByExternalName( const char *itemname, Item *current )
{
int num, i;
Item *item;
bool seeking;
if ( current )
seeking = true;
else
seeking = false;
num = inventory.NumObjects();
for( i = 1; i <= num; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( item );
if ( item == current )
{
seeking = false;
continue; // Start compares with the next item
}
if ( seeking )
continue;
if ( !Q_stricmp( item->getName(), itemname ) )
return item;
}
return NULL;
}
//--------------------------------------------------------------
//
// Name: FindItemByModelname
// Class: Sentient
//
// Description: Finds the item by its name
//
// Parameters: const char *mdl -- Model name to find
// Item *current (default, 0) -- Item to start searching from
//
// Returns: Item * or NULL
//
//--------------------------------------------------------------
Item *Sentient::FindItemByModelname( const char *mdl, Item *current )
{
int i, num;
bool seeking;
Item *item;
str tmpmdl;
if ( current )
seeking = true;
else
seeking = false;
if ( strnicmp( "models/", mdl, 7 ) )
tmpmdl = "models/";
tmpmdl += mdl;
num = inventory.NumObjects();
for( i = 1; i <= num; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( item );
if ( item == current )
{
seeking = false;
continue; // Start compares with the next item
}
if ( seeking )
continue;
if ( !Q_stricmp( item->model, tmpmdl ) )
return item;
}
return NULL;
}
//--------------------------------------------------------------
//
// Name: FindItemByClassName
// Class: Sentient
//
// Description: Finds the item by its name
//
// Parameters: const char *classname -- Classname to find
// Item *current (default, 0) -- Item to start searching from
//
// Returns: Item * or NULL
//
//--------------------------------------------------------------
Item *Sentient::FindItemByClassName( const char *classname, Item *current )
{
int num, i;
Item *item;
bool seeking;
if ( current )
seeking = true;
else
seeking = false;
num = inventory.NumObjects();
for( i = 1; i <= num; i++ )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
assert( item );
if ( item == current )
{
seeking = false;
continue; // Start compares with the next item
}
if ( seeking )
continue;
if ( !Q_stricmp( item->edict->entname, classname ) )
return item;
}
return NULL;
}
//--------------------------------------------------------------
//
// Name: FindItem
// Class: Sentient
//
// Description: Finds the item by its name
//
// Parameters: const char *classname -- Classname to find
// Item *current (default, 0) -- Item to start searching from
//
// Returns: Item * or NULL
//
//--------------------------------------------------------------
Item *Sentient::FindItem( const char *itemname, Item *current )
{
Item *item = NULL;
if ( !itemname )
return NULL;
item = FindItemByExternalName( itemname, current );
if ( !item )
{
item = FindItemByModelname( itemname, current );
if ( !item )
{
item = FindItemByClassName( itemname, current );
}
}
return item;
}
void Sentient::AttachAllActiveWeapons( void )
{
int i;
for ( i=0; i<MAX_ACTIVE_WEAPONS; i++ )
{
Weapon *weap = activeWeaponList[i];
if ( weap )
weap->AttachToOwner( (weaponhand_t )i );
}
}
void Sentient::DetachAllActiveWeapons( void )
{
int i;
for ( i=0; i<MAX_ACTIVE_WEAPONS; i++ )
{
Weapon *weap = activeWeaponList[i];
if ( weap )
weap->DetachFromOwner();
}
}
void Sentient::FreeInventory( Event * )
{
FreeInventory();
}
void Sentient::FreeInventory( void )
{
int num;
int i;
Item *item;
Ammo *ammo;
// Detach all Weapons
DetachAllActiveWeapons();
// Delete all inventory items ( this includes weapons )
num = inventory.NumObjects();
for( i = num; i > 0; i-- )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
delete item;
}
inventory.ClearObjectList();
// Remove all ammo
num = ammo_inventory.NumObjects();
for( i = num; i > 0; i-- )
{
ammo = ( Ammo * )ammo_inventory.ObjectAt( i );
delete ammo;
}
ammo_inventory.ClearObjectList();
for( i = 0 ; i < MAX_ACTIVE_WEAPONS ; i++ )
{
activeWeaponList[i] = NULL;
}
}
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;
}
bool Sentient::ChangeWeapon( Weapon *weapon, weaponhand_t hand )
{
if ( ( hand > MAX_ACTIVE_WEAPONS ) )
{
warning( "Sentient::ChangeWeapon", "Weapon hand number \"%d\" is out of bounds of 0 to MAX_ACTIVE_WEAPONS:%d\n", hand, MAX_ACTIVE_WEAPONS );
return false;
}
// Check if weapon is already active in the slot
if ( weapon == activeWeaponList[hand] )
return false;
ActivateWeapon( weapon, hand );
return true;
}
void Sentient::DeactivateWeapon( weaponhand_t hand )
{
int i;
if ( !activeWeaponList[hand] )
{
warning( "Sentient::DeactivateWeapon", "Tried to deactivate a non-active weapon in hand %d\n", hand );
return;
}
activeWeaponList[hand]->AttachToHolster( hand );
activeWeaponList[hand]->SetPutAway( false );
activeWeaponList[hand]->animate->RandomAnimate( "putaway" );
// Check the player's inventory and detach any weapons that are already attached to that spot
for ( i=1; i<=inventory.NumObjects(); i++ )
{
Item *item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( item->isSubclassOf( Weapon ) )
{
Weapon *weap = ( Weapon * )item;
if (
( weap != activeWeaponList[hand] ) &&
( !str::cmp( weap->GetCurrentAttachToTag(), activeWeaponList[hand]->GetCurrentAttachToTag() ) )
)
{
weap->DetachFromOwner();
}
}
}
activeWeaponList[hand] = NULL;
}
void Sentient::DeactivateWeapon( Weapon *weapon )
{
int i;
for ( i=0; i<MAX_ACTIVE_WEAPONS; i++ )
{
if ( activeWeaponList[i] == weapon )
{
activeWeaponList[i]->DetachFromOwner();
activeWeaponList[i]->SetPutAway( false );
activeWeaponList[i] = NULL;
}
}
}
void Sentient::ActivateWeapon( Weapon *weapon, weaponhand_t hand )
{
int i;
if ( weapon->isSubclassOf(WeaponDualWield) )
{
WeaponDualWield *dw = (WeaponDualWield*)weapon;
activeWeaponList[WEAPON_LEFT] = dw->getLeftWeapon();
activeWeaponList[WEAPON_RIGHT] = dw->getRightWeapon();
}
else
activeWeaponList[hand] = weapon;
str holsterTag;
switch( hand )
{
case WEAPON_LEFT:
holsterTag = weapon->GetLeftHolsterTag();
break;
case WEAPON_RIGHT:
holsterTag = weapon->GetRightHolsterTag();
break;
case WEAPON_DUAL:
holsterTag = weapon->GetDualHolsterTag();
break;
default:
holsterTag = "";
break;
}
// Check the player's inventory and detach any weapons that are currently attached to that tag.
for ( i=1; i<=inventory.NumObjects(); i++ )
{
Item *item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( item->isSubclassOf( Weapon ) )
{
Weapon *weap = ( Weapon * )item;
if (
( !str::cmp( holsterTag, weap->GetCurrentAttachToTag() ) )
)
{
weap->DetachFromOwner();
}
}
}
weapon->AttachToOwner( hand );
weapon->playAnim( "raise", true );
}
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 )->GetRank() > bestrank ) &&
( ( ( Weapon * )next )->HasAmmo( FIRE_MODE1 ) ) )
{
bestweapon = ( Weapon * )next;
bestrank = bestweapon->GetRank();
}
}
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->GetOrder();
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( FIRE_MODE1 ) || !choice->AutoChange() )
{
continue;
}
choiceorder = choice->GetOrder();
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->GetOrder();
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( FIRE_MODE1 ) || !choice->AutoChange() )
{
continue;
}
choiceorder = choice->GetOrder();
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::GetActiveWeapon( weaponhand_t hand )
{
if ( ( hand > MAX_ACTIVE_WEAPONS ) || ( hand < 0 ) )
{
warning( "Sentient::GetActiveWeapon", "Weapon hand number \"%d\" is out of bounds of 0 to MAX_ACTIVE_WEAPONS:%d\n", hand, MAX_ACTIVE_WEAPONS );
return NULL;
}
else if ( hand == WEAPON_ANY )
{
if ( activeWeaponList[ WEAPON_LEFT ] )
return activeWeaponList[ WEAPON_LEFT ];
else if ( activeWeaponList[ WEAPON_RIGHT ] )
return activeWeaponList[ WEAPON_RIGHT ];
else if ( activeWeaponList[ WEAPON_DUAL ] )
return activeWeaponList[ WEAPON_DUAL ];
else
return NULL;
}
else
{
return activeWeaponList[hand];
}
}
qboolean Sentient::IsActiveWeapon( const Weapon *weapon )
{
int i;
for( i=0; i<MAX_ACTIVE_WEAPONS; i++ )
{
Weapon *activeWeap = activeWeaponList[i];
if ( activeWeap == weapon )
return true;
}
return false;
}
void Sentient::EventGiveTargetname( Event *ev )
{
int i;
TargetList *tlist;
str name;
const char *ptr;
qboolean found;
name = ev->GetString( 1 );
ptr = name.c_str();
// skip over the $
ptr++;
found = false;
tlist = world->GetTargetList( str( ptr ) );
for ( i = 1; i <= tlist->list.NumObjects(); i++ )
{
Entity * ent;
ent = tlist->list.ObjectAt( i );
assert( ent );
if ( ent->isSubclassOf( Item ) )
{
Item *item;
item = ( Item * )ent;
item->SetOwner( this );
item->ProcessPendingEvents();
AddItem( item );
found = true;
}
}
if ( !found )
{
ev->Error( "Could not give item with targetname %s to this sentient.\n", name.c_str() );
}
}
Item *Sentient::giveItem( const str &itemname, int amount, bool pickedUp, float skillLevel )
{
ClassDef *cls;
Item *item;
item = FindItem( itemname );
if ( item )
{
item->Add( amount );
item->SetSkillLevel( skillLevel );
return item;
}
else
{
qboolean set_the_model = false;
// we don't have it, so lets try to resolve the item name
// first lets see if it is a registered class name
cls = getClass( itemname );
if ( !cls )
{
SpawnArgs args;
// if that didn't work lets try to resolve it as a model
args.setArg( "model", itemname );
cls = args.getClassDef();
if ( !cls )
{
gi.WDPrintf( "No item called '%s'\n", itemname.c_str() );
return NULL;
}
set_the_model = true;
}
assert( cls );
item = ( Item * )cls->newInstance();
if ( !item )
{
gi.WDPrintf( "Could not spawn an item called '%s'\n", itemname.c_str() );
return NULL;
}
if ( !item->isSubclassOf( Item ) )
{
gi.WDPrintf( "Could not spawn an item called '%s'\n", itemname.c_str() );
delete item;
return NULL;
}
if ( set_the_model )
{
// Set the model
item->setModel( itemname );
}
item->SetOwner( this );
item->ProcessPendingEvents();
if ( amount )
item->setAmount( amount );
item->hideModel();
item->SetSkillLevel( skillLevel );
AddItem( item );
if ( item->isSubclassOf( Weapon ) )
{
// Post an event to give the ammo to the sentient
Event *ev1;
// Remove the fullbright from the weapon
item->edict->s.renderfx &= ~RF_FULLBRIGHT;
ev1 = new Event( EV_Weapon_GiveStartingAmmo );
ev1->AddEntity( this );
item->ProcessEvent( ev1 );
if ( this->isSubclassOf( Player ) )
{
Player *player = (Player *)this;
Weapon *weapon = (Weapon *)item;
Weapon *currentWeapon;
if ( pickedUp && player->getAutoSwitchWeapons() )
{
currentWeapon = GetActiveWeapon( WEAPON_DUAL );
if ( ( !currentWeapon ) || ( weapon->getWeaponPriority() < currentWeapon->getWeaponPriority() ) )
{
ev1 = new Event( EV_Player_UseItem );
ev1->AddString( item->getName() );
PostEvent( ev1, FRAMETIME );
}
}
}
}
return item;
}
}
void Sentient::takeItem( const char *name )
{
Item * item;
item = FindItem( name );
if ( item )
{
gi.DPrintf( "Taking item %s away from player\n", item->getName().c_str() );
item->PostEvent( EV_Remove, 0.0f );
return;
}
Ammo *ammo;
ammo = FindAmmoByName( name );
if ( ammo )
{
gi.DPrintf( "Taking ammo %s away from player\n", name );
ammo->setAmount( 0 );
}
}
bool Sentient::useWeapon( const char *weaponname, weaponhand_t hand )
{
Weapon *weapon;
assert( weaponname );
if ( !weaponname )
{
warning( "Sentient::useWeapon", "weaponname is NULL\n" );
return false;
}
if ( deadflag || ( multiplayerManager.inMultiplayer() && this->isSubclassOf( Player ) && multiplayerManager.isPlayerSpectator( (Player *)this ) ) )
return false;
// Find the item in the sentient's inventory
weapon = ( Weapon * )FindItem( weaponname );
// If it exists, then make the change to the slot number specified
if ( weapon )
{
return ChangeWeapon( weapon, hand );
}
return false;
}
void Sentient::EventTake( Event *ev )
{
takeItem( ev->GetString( 1 ) );
}
void Sentient::EventGiveAmmo( Event *ev )
{
int amount,maxamount=-1;
const char *type;
type = ev->GetString( 1 );
amount = ev->GetInteger( 2 );
if ( ev->NumArgs() == 3 )
maxamount = ev->GetInteger( 3 );
GiveAmmo( type, amount, false, maxamount );
}
void Sentient::giveAmmoOverTime( Event *ev )
{
Event *event;
float ammoToAdd;
float ammoToAddThisFrame;
float timeLeft;
int numFrames;
const char *ammoType;
ammoType = ev->GetString( 1 );
ammoToAdd = ev->GetInteger( 2 );
timeLeft = ev->GetFloat( 3 );
// Figure out how much ammo to add this frame
if ( timeLeft < level.frametime )
numFrames = 1;
else
numFrames = timeLeft / level.frametime;
ammoToAddThisFrame = ammoToAdd / numFrames;
// Actually add the ammo to the entity
GiveAmmo( ammoType, ammoToAddThisFrame, false, -1 );
// Post the event for the next frame
ammoToAdd -= ammoToAddThisFrame;
timeLeft -= level.frametime;
//CancelEventsOfType( EV_Sentient_GiveAmmoOverTime );
if ( timeLeft > 0.0f )
{
event = new Event( EV_Sentient_GiveAmmoOverTime );
event->AddString( ammoType );
event->AddInteger( ammoToAdd );
event->AddFloat( timeLeft );
PostEvent( event, level.frametime );
}
}
void Sentient::EventGiveItem( Event *ev )
{
const char *type;
float amount;
type = ev->GetString( 1 );
if ( ev->NumArgs() > 1 )
amount = ev->GetInteger( 2 );
else
amount = 1;
giveItem( type, amount );
}
void Sentient::EventGiveArmor ( Event *ev )
{
const char *type;
Item* currentArmor;
float amount;
bool pickedup;
currentArmor = NULL;
currentArmor = FindBaseArmor();
//We can only have 1 base armor
//if ( currentArmor )
// RemoveItem( currentArmor );
// Do the giveItem to put the armor in inventory
// and to get it instantiated
type = ev->GetString( 1 );
if ( stricmp(type , "none" ) == 0 )
{
currentArmor = NULL;
currentBaseArmor = NULL;
return;
}
if ( ev->NumArgs() > 1 )
amount = ev->GetFloat( 2 );
else
amount = 0;
if ( ev->NumArgs() > 2 )
pickedup = ev->GetBoolean( 3 );
else
pickedup = false;
giveItem( type , amount );
currentArmor = NULL;
currentArmor = FindBaseArmor();
if ( pickedup && this->isSubclassOf( Player ) && currentArmor )
{
int iconIndex;
iconIndex = gi.imageindex( "sysimg/icons/items/armor" );
((Player *)this)->setItemText( iconIndex, va( "$$PickedUp$$ %d $$Item-Armor$$", (int)amount ) );
//gi.centerprintf ( edict, CENTERPRINT_IMPORTANCE_NORMAL, "$$PickedUp$$ %d armor", (int)amount );
}
// Cast is safe because FindBaseArmor will only return
// items of type armor
if ( currentArmor )
currentBaseArmor = (Armor*)currentArmor;
}
qboolean Sentient::DoGib( int meansofdeath )
{
if ( !com_blood->integer )
{
return false;
}
if (
( meansofdeath == MOD_TELEFRAG ) ||
( meansofdeath == MOD_LAVA )
)
{
return true;
}
if ( health > -75.0f )
{
return false;
}
// Impact and Crush < -75 health
if ( ( meansofdeath == MOD_IMPACT ) || ( meansofdeath == MOD_CRUSH ) )
{
return true;
}
return false;
}
//#define WATER_CONVERSION_FACTOR 1.0f
void Sentient::ArmorDamage( ::Damage &damage )
{
CheckDamageThreshold( damage.damage );
if ( damageModSystem )
damageModSystem->resolveDamage(damage);
ArmorDamage(
damage.damage,
damage.inflictor,
damage.attacker,
damage.position,
damage.direction,
damage.normal,
damage.knockback,
damage.dflags,
damage.meansofdeath,
damage.surfaceNumber,
damage.boneNumber,
damage.weapon,
damage.showPain
);
}
void Sentient::ArmorDamage( Event *ev )
{
float damage;
Entity *inflictor;
Entity *attacker;
Entity *weapon = 0;
Vector position;
Vector direction;
Vector normal;
int knockback;
int dflags;
int meansofdeath;
int surfaceNumber = -1;
int boneNumber = -1;
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 );
if ( ev->NumArgs() > 9 )
surfaceNumber = ev->GetInteger( 10 );
if ( ev->NumArgs() > 10 )
boneNumber = ev->GetInteger( 11 );
if ( ev->NumArgs() > 11 )
weapon = ev->GetEntity( 12 );
ArmorDamage( damage, inflictor, attacker, position, direction, normal, knockback, dflags, meansofdeath, surfaceNumber,
boneNumber, weapon , false );
}
void Sentient::ArmorDamage( float damage, Entity *inflictor, Entity *attacker, const Vector &position, const Vector &direction,
const Vector &normal, int knockback, int dflags, int meansofdeath, int surfaceNumber, int boneNumber, Entity *weapon , bool showPain )
{
Vector momentum;
Event *event;
Event *injuredEvent;
float damage_red;
float damage_green;
float damage_time;
float resistance_modifier;
qboolean blocked = false;
qboolean set_means_of_death;
Vector normalizedDirection;
Q_UNUSED(normal);
if ( ( takedamage == DAMAGE_NO ) || ( movetype == MOVETYPE_NOCLIP ) )
{
return;
}
// See if we should set means of death
set_means_of_death = true;
if ( this->isSubclassOf( Actor ) )
{
Actor *act = (Actor *)this;
if ( ( act->state_flags & STATE_FLAG_SMALL_PAIN ) && ( meansofdeath == MOD_ON_FIRE || Immune( meansofdeath ) ) )
set_means_of_death = false;
}
// See if sentient is immune to this type of damage
if ( Immune ( meansofdeath ) )
{
if ( set_means_of_death )
means_of_death = meansofdeath;
// Send pain event
event = new Event( EV_Pain );
event->AddFloat( 0.0f );
event->AddEntity( attacker );
event->AddInteger( meansofdeath );
event->AddVector( position );
event->AddVector( direction );
ProcessEvent( event );
return;
}
// Apply any resistances if any
resistance_modifier = GetResistanceModifier( meansofdeath );
if( resistance_modifier )
{
resistance_modifier /= 100.0f;
float damagemod = resistance_modifier * damage;
damage -= damagemod;
}
// See if the damage is melee and high enough on actor
if ( this->isSubclassOf( Actor ) )
{
Actor *act = ( Actor * )this;
// Check the attack if it is melee
if ( ( meansofdeath == MOD_SWORD ) || ( meansofdeath == MOD_CHAINSWORD ) || ( meansofdeath == MOD_AXE ) ||
( meansofdeath == MOD_FIRESWORD ) || ( meansofdeath == MOD_ELECTRICSWORD ) || ( meansofdeath == MOD_LIGHTSWORD ) )
{
// Make sure attack is high enough
if ( position.z - origin.z < act->minimum_melee_height )
{
Entity::SpawnEffect("fx/fx-sparkblock.tik", position, vec_zero, 2.0f);
return;
}
// Make sure attack is within the damage angles
if ( act->damage_angles )
{
Vector attack_angle;
float yaw_diff;
attack_angle = direction.toAngles();
yaw_diff = angles[YAW] - attack_angle[YAW] + 180.0f;
yaw_diff = AngleNormalize180( yaw_diff );
if ( ( yaw_diff < -act->damage_angles ) || ( yaw_diff > act->damage_angles ) )
{
Entity::SpawnEffect("fx_sparkblock.tik", position, vec_zero, 2.0f);
return;
}
}
}
}
if ( deadflag )
{
// Spawn a blood spurt if this model has one
if ( ShouldBleed( meansofdeath, true ) )
{
AddBloodSpurt( position, direction, boneNumber );
if ( ShouldGib( meansofdeath, damage ) )
ProcessEvent( EV_Sentient_SpawnBloodyGibs );
}
if ( set_means_of_death )
means_of_death = meansofdeath;
if ( ( meansofdeath == MOD_FIRE ) || ( meansofdeath == MOD_FIRESWORD ) || ( meansofdeath == MOD_FIRE_BLOCKABLE ) )
{
if ( GetResistanceModifier(meansofdeath) < 100 )
TryLightOnFire( meansofdeath, attacker );
}
if ( damage > 0.0f )
{
// Send pain event
event = new Event( EV_Pain );
event->AddFloat( damage );
event->AddEntity( attacker );
event->AddInteger( meansofdeath );
event->AddVector( position );
event->AddVector( direction );
event->AddInteger( showPain );
ProcessEvent( event );
}
return;
}
if ( flags & FL_GODMODE )
{
return;
}
if ( currentBaseArmor )
{
if ( this->isSubclassOf(Actor) )
{
Actor *act;
qboolean resolveDamage;
act = (Actor*)this;
if ( act->GetActorFlag( ACTOR_FLAG_DISABLED ) || act->GetActorFlag( ACTOR_FLAG_CRIPPLED ) )
resolveDamage = false;
else
resolveDamage = true;
if ( resolveDamage )
damage = currentBaseArmor->ResolveDamage( damage , meansofdeath , direction , position, attacker );
}
else
damage = currentBaseArmor->ResolveDamage( damage , meansofdeath , direction , position , attacker );
}
if ( this->isSubclassOf( Player ) )
{
Vector attack_angle;
Vector player_angle;
float yaw_diff;
Player *player;
player = ( Player * )this;
player_angle = player->GetTorsoAngles();
attack_angle = direction.toAngles();
yaw_diff = player_angle[YAW] - attack_angle[YAW] + 180.0f;
yaw_diff = AngleNormalize180( yaw_diff );
// Get real knockback value
knockback = player->GetKnockback( knockback, blocked );
//If we're not in multiplayer AND we don't have a ground entity
//cut our knockback in half
if ( !multiplayerManager.inMultiplayer() && !player->groundentity)
knockback*=0.5;
}
// Do the kick
if (!(dflags & DAMAGE_NO_KNOCKBACK))
{
if ((knockback) &&
(movetype != MOVETYPE_NONE) &&
(movetype != MOVETYPE_STATIONARY) &&
(movetype != MOVETYPE_BOUNCE) &&
(movetype != MOVETYPE_PUSH) &&
(movetype != MOVETYPE_STOP))
{
float m;
Event *immunity_event;
Event *resistance_event;
if (mass < 50)
m = 50;
else
m = mass;
normalizedDirection = direction;
normalizedDirection.normalize();
if ( isClient() && ( attacker == this ) && multiplayerManager.inMultiplayer() )
momentum = normalizedDirection * ( 1000.0f * ( float )knockback / m ); // the rocket jump hack...
else
momentum = normalizedDirection * ( 500.0f * ( float )knockback / m );
if ( dflags & DAMAGE_BULLET )
{
// Clip the z velocity for bullet weapons
if ( momentum.z > 75.0f )
momentum.z = 75.0f;
}
velocity += momentum;
// Make this sentient vulnerable to falling damage now
if ( Immune( MOD_FALLING ) )
{
immunity_event = new Event( EV_Sentient_RemoveImmunity );
immunity_event->AddString( "falling" );
ProcessEvent( immunity_event );
}
if ( Resistant( MOD_FALLING ) )
{
resistance_event = new Event( EV_Sentient_RemoveResistance );
resistance_event->AddString( "falling" );
ProcessEvent( resistance_event );
}
}
}
Vector attack_angle;
float yaw_diff;
attack_angle = attacker->angles;
yaw_diff = angles[YAW] - attack_angle[YAW] + 180.0f;
yaw_diff = AngleNormalize180( yaw_diff );
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
float clashchance = 0.0f;
if ( gpm->isDefined("clash_chance") )
clashchance = atof(gpm->getDefine("clash_chance"));
// Consider attacker == inflictor, so we don't clash vs. projectiles.
if ( in_melee_attack && G_Random() < clashchance && attacker == inflictor)
{
if ( ( yaw_diff > -45.0f ) && ( yaw_diff < 45.0f ) )
{
if ( attacker->isSubclassOf( Sentient ) )
{
Sentient *sent = ( Sentient * )attacker;
sent->SetAttackBlocked( true );
}
SetAttackBlocked( true );
WeaponEffectsAndSound( weapon, "Clash", position );
means_of_death = meansofdeath;
return;
}
}
// Blocking of the attack by an enemy.
if ( in_block && ( meansofdeath != MOD_ON_FIRE ) && ( meansofdeath != MOD_EXPLOSION ) )
{
if ( ( yaw_diff > -45.0f ) && ( yaw_diff < 45.0f ) )
{
if ( ( meansofdeath != MOD_FIRE ) && ( meansofdeath != MOD_LIFEDRAIN ) )
{
if ( attacker->isSubclassOf( Sentient ) )
{
Sentient *sent = ( Sentient * )attacker;
sent->SetAttackBlocked( true );
}
if ( this->isSubclassOf( Actor ) )
{
Actor *act = ( Actor * )this;
act->AddStateFlag( STATE_FLAG_BLOCKED_HIT );
}
WeaponEffectsAndSound( weapon, "Blocked", position );
}
means_of_death = meansofdeath;
return;
}
in_block = false;
}
if ( g_debugdamage->integer )
G_DebugDamage( damage, this, attacker, inflictor );
// Handle statis damage
if ( meansofdeath == MOD_STASIS )
{
if ( damage > 0.0f )
{
//Stupid last minute hack to make sure we can't stasis people who will break
if ( this->isSubclassOf(Actor) )
{
Actor *me;
me = (Actor*)this;
if ( me->GetActorFlag(ACTOR_FLAG_CANNOT_FREEZE ) )
{
return;
}
}
startStasis();
PostEvent( EV_StopStasis, damage );
}
return;
}
if ( meansofdeath != MOD_LIFEDRAIN )
{
if ( damage < 0 )
{
//Because a resistance of over 100 will Add to health, need to make
//sure that the health doesn't go over the max
if( health < max_health )
{
health -= damage;
if ( health > max_health )
health = max_health;
}
}
else
{
health -= damage;
}
}
if ( ( damage > 0.0f ) && ( surfaceNumber != -1 ) )
{
if ( this->isSubclassOf( Actor ) )
{
Actor* theActor = (Actor*)this;
if( theActor->GetActorFlag(ACTOR_FLAG_USE_DAMAGESKINS) )
{
if ( !edict->s.surfaces[ surfaceNumber ] )
edict->s.surfaces[ surfaceNumber ]++;
}
}
}
// Do all of the regional damage stuff
SetRegionalDamage( surfaceNumber, boneNumber, direction );
if ( ( ( meansofdeath == MOD_FIRE ) || ( meansofdeath == MOD_FIRESWORD ) || ( meansofdeath == MOD_FIRE_BLOCKABLE ) ) && !blocked )
{
if ( GetResistanceModifier(meansofdeath) < 100 )
TryLightOnFire( meansofdeath, attacker );
}
// Set means of death
if ( set_means_of_death )
means_of_death = meansofdeath;
// Spawn a blood spurt if this model has one
if ( ( damage > 0.0f ) && ShouldBleed( meansofdeath, false ) && !blocked )
{
AddBloodSpurt( position, direction, boneNumber );
if ( ( this->isSubclassOf( Actor ) || ( damage > 10.0f ) ) && ShouldGib( meansofdeath, damage ) )
ProcessEvent( EV_Sentient_SpawnBloodyGibs );
}
if ( health < 0.1f )
{
// See if we can kill this actor or not
if ( this->isSubclassOf( Actor ) )
{
Actor *act = ( Actor * )this;
if ( act->IsImmortal() )
health = 1;
}
}
if ( health < 0.1f )
{
// Recalculate damage so that it is only damage needed to kill
damage += health;
// Make sure health is now 0
health = 0;
if ( ( meansofdeath == MOD_VAPORIZE ) || ( meansofdeath == MOD_VAPORIZE_COMP ) ||
( meansofdeath == MOD_VAPORIZE_DISRUPTOR ) || ( meansofdeath == MOD_VAPORIZE_PHOTON ) ||
( meansofdeath == MOD_SNIPER ) )
{
bool canDisintegrate = true;
//YAY!!! ANOTHER SPECIAL CASE!! WOOPEE!! YEE-HAW!!! MY EXCITEMENT KNOWS NO BOUNDS!!!
Actor *stupidSpecialCaseActor;
if ( isSubclassOf( Actor ) )
{
//YAY!!!! I get to cast myself!!! YAY!!!!
stupidSpecialCaseActor = ( Actor* )this;
if(stupidSpecialCaseActor->GetActorFlag(ACTOR_FLAG_CANNOT_DISINTEGRATE) )
canDisintegrate = false;
}
if ( canDisintegrate )
{
event = new Event( EV_DisplayEffect );
event->AddString( "FadeOut" );
if ( ( meansofdeath == MOD_VAPORIZE_DISRUPTOR ) )
event->AddString( "Disruptor" );
else if ( ( meansofdeath == MOD_VAPORIZE_PHOTON ) )
event->AddString( "Photon" );
else if ( ( meansofdeath == MOD_SNIPER ) )
event->AddString( "Sniper" );
else
event->AddString( "Phaser" );
ProcessEvent( event );
edict->s.renderfx |= RF_CHILDREN_DONT_INHERIT_ALPHA;
if ( !isSubclassOf( Player ) )
PostEvent( EV_Remove, 5.0f );
}
}
event = new Event( EV_Killed );
event->AddEntity( attacker );
event->AddFloat( damage );
event->AddEntity( inflictor );
event->AddInteger( meansofdeath );
event->AddInteger( false );
event->AddEntity( weapon );
event->AddVector( direction );
ProcessEvent( event );
}
if (
( meansofdeath == MOD_GAS ) ||
( meansofdeath == MOD_GAS_BLOCKABLE ) ||
( meansofdeath == MOD_SLIME ) ||
( meansofdeath == MOD_POISON )
)
{
damage_green = damage / 50.0f;
if ( damage_green > 1.0f )
damage_green = 1.0f;
if ( ( damage_green < 0.2f ) && ( damage_green > 0.0f ) )
damage_green = 0.2f;
damage_red = 0.0f;
}
else
{
damage_red = damage / 50.0f;
if ( damage_red > 0.5f )
damage_red = 0.5f;
if ( ( damage_red < 0.2f ) && ( damage_red > 0.0f ) )
damage_red = 0.2f;
damage_green = 0.0f;
}
damage_time = damage / 50.0f;
if ( damage_time > 0.5f )
damage_time = 0.5f;
if ( sv_showdamagecolors->integer && !this->isSubclassOf( Player ) )
{
SetOffsetColor( damage_red, damage_green, 0.0f, damage_time );
}
if ( health > 0.0f && damage > 0.0f )
{
// Send pain event
event = new Event( EV_Pain );
event->AddFloat( damage );
event->AddEntity( attacker );
event->AddInteger( meansofdeath );
event->AddVector( position );
event->AddVector( direction );
event->AddInteger( showPain );
ProcessEvent( event );
}
// Send the injured event to our group if we are at or below our critical health and
// we can send the event;
if ( _canSendInjuredEvent && ( health / max_health ) < GetCriticalHealthPercentage() )
{
injuredEvent = new Event (EV_Sentient_GroupMemberInjured);
injuredEvent->AddInteger( 1 );
groupcoordinator->SendEventToGroup(injuredEvent , GetGroupID());
_canSendInjuredEvent = false;
}
return;
}
void Sentient::SetRegionalDamage( int surface_number, int bone_number, Vector direction )
{
Vector attack_angle;
float yaw_diff;
// Save the last surface that was hit
last_surface_hit = surface_number;
// Save the last bone that was hit
last_bone_hit = bone_number;
// Save the last general region that was hit
last_region_hit = 0;
attack_angle = direction.toAngles();
yaw_diff = angles[YAW] - attack_angle[YAW] + 180.0f;
yaw_diff = AngleNormalize180( yaw_diff );
if ( ( yaw_diff < -90.0f ) || ( yaw_diff > 90.0f ) )
last_region_hit |= REGIONAL_DAMAGE_BACK;
else
last_region_hit |= REGIONAL_DAMAGE_FRONT;
}
qboolean Sentient::CanBlock( int meansofdeath, qboolean full_block )
{
// Check to see what a full block can't even block
switch ( meansofdeath )
{
case MOD_DROWN :
case MOD_TELEFRAG :
case MOD_SLIME :
case MOD_LAVA :
case MOD_FALLING :
case MOD_RADIATION :
case MOD_IMPALE :
case MOD_ON_FIRE :
case MOD_ELECTRICWATER :
case MOD_EAT :
return false;
}
// Full blocks block everything else
if ( full_block )
return true;
// Check to see what a small block can't block
switch ( meansofdeath )
{
case MOD_FIRE :
case MOD_GAS :
case MOD_CRUSH_EVERY_FRAME :
return false;
}
// Everything else is blocked
return true;
}
void Sentient::AddBloodSpurt( const Vector &position, const Vector &direction, int bone_number )
{
Entity *blood;
Vector dir;
Event *event;
str blood_splat_name;
float blood_splat_size;
float length;
trace_t trace;
float scale;
if ( !com_blood->integer )
return;
next_bleed_time = level.time + .5f;
// Calculate a good scale for the blood
if ( mass < 50 )
scale = .5f;
else if ( mass > 300 )
scale = 1.5f;
else if ( mass >= 200 )
scale = mass / 200.0;
else
scale = .5f + ( (float)mass - 50.0f ) / 300.0f;
// Add blood spurt
blood = new Entity( ENTITY_CREATE_FLAG_ANIMATE );
blood->setModel( blood_model );
if ( !isSubclassOf( Player ) )
{
// Network optimization
dir[0] = -direction[0];
dir[1] = -direction[1];
dir[2] = -direction[2];
blood->angles = dir.toAngles();
blood->setAngles( blood->angles );
}
// Make the blood come from the centroid of the bone if possible
if ( bone_number >= 0 )
{
int parent_bone_number;
Vector bone_pos;
Vector parent_bone_pos;
Vector bone_centroid;
parent_bone_number = gi.Bone_GetParentNum( edict->s.modelindex, bone_number );
if ( parent_bone_number >= 0 )
{
GetTag( bone_number, &bone_pos );
GetTag( parent_bone_number, &parent_bone_pos );
bone_centroid = (bone_pos + parent_bone_pos ) * 0.5f;
blood->setOrigin( bone_centroid );
}
}
else
{
// Default the blood spurt to the position where the trace stopped
blood->setOrigin( position );
}
//blood->origin.copyTo( blood->edict->s.origin2 );
blood->setSolidType( SOLID_NOT );
blood->setScale( scale );
event = new Event( EV_Remove );
blood->PostEvent( event, 1.0f );
// Add blood splats near feet
blood_splat_name = GetBloodSplatName();
blood_splat_size = GetBloodSplatSize();
if ( blood_splat_name.length() && G_Random() < 0.5f )
{
dir = origin - centroid;
dir.z -= 50.0f;
dir.x += G_CRandom( 20.0f );
dir.y += G_CRandom( 20.0f );
length = dir.length();
dir.normalize();
dir = dir * ( length + 10.0f );
trace = G_Trace( centroid, vec_zero, vec_zero, centroid + dir, NULL, MASK_DEADSOLID, false, "AddBloodSpurt" );
if ( trace.fraction < 1.0f )
{
Decal *decal = new Decal;
decal->setShader( blood_splat_name );
decal->setOrigin( Vector( trace.endpos ) + ( Vector( trace.plane.normal ) * 0.2f ) );
decal->setDirection( trace.plane.normal );
decal->setOrientation( "random" );
decal->setRadius( blood_splat_size + G_Random( blood_splat_size ) );
}
}
}
qboolean Sentient::ShouldBleed( int meansofdeath, qboolean dead )
{
// Make sure we have a blood model
if ( !blood_model.length() )
return false;
// Don't let the player bleed in single player
if ( !multiplayerManager.inMultiplayer() && isSubclassOf( Player ) )
return false;
// Make sure if we are dead we are allowed to bleed after death
if ( dead && this->isSubclassOf( Actor ) )
{
Actor *act = (Actor *)this;
if ( !act->GetActorFlag( ACTOR_FLAG_BLEED_AFTER_DEATH ) )
return false;
}
// See if we can bleed now based on means of death
switch ( meansofdeath )
{
// Sometimes bleed (based on time)
case MOD_BULLET :
case MOD_STING :
case MOD_STING2 :
case MOD_VORTEX :
case MOD_CHAINSWORD :
case MOD_PLASMABEAM :
case MOD_CRUSH_EVERY_FRAME :
case MOD_POISON :
case MOD_ELECTRICWATER :
case MOD_EAT :
case MOD_CIRCLEOFPROTECTION :
if ( next_bleed_time > level.time )
return false;
break;
// Sometimes bleed (based on chance)
case MOD_PLASMASHOTGUN :
if ( next_bleed_time < level.time )
return true;
if ( G_Random() > 0.1f )
return false;
break;
// Never bleed
case MOD_LIFEDRAIN :
case MOD_SLIME :
case MOD_LAVA :
case MOD_GAS :
case MOD_GAS_BLOCKABLE :
case MOD_FIRE :
case MOD_FIRE_BLOCKABLE :
case MOD_SLING :
case MOD_DROWN :
case MOD_FLASHBANG :
case MOD_ON_FIRE :
case MOD_FALLING :
case MOD_RADIATION :
case MOD_DEATH_QUAD :
return false;
}
// Always bleed by default
return true;
}
// ShouldGib assumes that ShouldBleed has already been called
qboolean Sentient::ShouldGib( int meansofdeath, float damage )
{
// See if we can gib based on means of death
switch ( meansofdeath )
{
// Always gib
case MOD_CHAINSWORD :
case MOD_CRUSH_EVERY_FRAME :
case MOD_PLASMASHOTGUN :
case MOD_EAT :
case MOD_EXPLOSION :
return true;
break;
// Sometimes gib
case MOD_BULLET :
case MOD_STING :
case MOD_STING2 :
if ( G_Random( 100.0f ) < damage * 10.0f )
return true;
break;
case MOD_PLASMABEAM :
if ( G_Random( 100.0f ) < damage * 5.0f )
return true;
break;
// Never gib
case MOD_LIFEDRAIN :
case MOD_SLIME :
case MOD_LAVA :
case MOD_GAS :
case MOD_GAS_BLOCKABLE :
case MOD_POISON :
case MOD_FIRE :
case MOD_FIRE_BLOCKABLE :
case MOD_SLING :
case MOD_DROWN :
case MOD_FLASHBANG :
case MOD_ON_FIRE :
case MOD_FALLING :
case MOD_RADIATION :
case MOD_VORTEX :
case MOD_ELECTRICWATER :
return false;
}
// Default is random based on how much damage done
if ( G_Random( 100.0f ) < damage * 2.0f )
return true;
return false;
}
str Sentient::GetBloodSpurtName( void )
{
str blood_spurt_name;
if ( blood_model == "fx/fx_bspurt.tik" )
blood_spurt_name = "fx/fx_bspurt2.tik";
else if ( blood_model == "fx/fx_gspurt.tik" )
blood_spurt_name = "fx/fx_gspurt2.tik";
else if ( blood_model == "fx/fx_bspurt_blue.tik" )
blood_spurt_name = "fx/fx_bspurt2_blue.tik";
return blood_spurt_name;
}
str Sentient::GetBloodSplatName( void )
{
str blood_splat_name;
if ( blood_model == "fx/fx_bspurt.tik" )
blood_splat_name = "bloodsplat.spr";
else if ( blood_model == "fx/fx_gspurt.tik" )
blood_splat_name = "greensplat.spr";
else if ( blood_model == "fx/fx_bspurt_blue.tik" )
blood_splat_name = "bluesplat.spr";
return blood_splat_name;
}
float Sentient::GetBloodSplatSize( void )
{
float m;
m = (int)mass;
if ( m < 50.0f )
m = 50.0f;
else if ( m > 250.0f )
m = 250.0f;
return ( 10.0f + ( m - 50.0f ) / 200.0f * 6.0f );
}
str Sentient::GetGibName( void )
{
str gib_name;
if ( blood_model == "fx/fx_bspurt.tik" )
gib_name = "fx/fx_rgib";
else if ( blood_model == "fx/fx_gspurt.tik" )
gib_name = "fx/fx_ggib";
else if ( blood_model == "fx/fx_bspurt_green.tik" )
gib_name = "fx/fx_ggib";
return gib_name;
}
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 ) || ( next_item->isSubclassOf( Weapon ) ) )&& 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;
}
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();
}
}
}
void Sentient::setModel( const char *mdl )
{
// Rebind all active weapons
DetachAllActiveWeapons();
Entity::setModel( mdl );
AttachAllActiveWeapons();
}
void Sentient::TurnOffShadow( Event * )
{
edict->s.renderfx &= ~RF_SHADOW;
}
void Sentient::TurnOnShadow( Event * )
{
edict->s.renderfx |= RF_SHADOW;
}
void Sentient::Archive( Archiver &arc )
{
int i;
int num;
Resistance *resistance;
Entity::Archive( arc );
arc.ArchiveFloat( &_criticalHealthPercentage );
// Archive Damage Threshold;
arc.ArchiveFloat( &_damageThreshold.duration );
arc.ArchiveFloat( &_damageThreshold.startTime );
arc.ArchiveFloat( &_damageThreshold.maxDamage );
arc.ArchiveFloat( &_damageThreshold.currentDamage );
arc.ArchiveFloat( &shotsFiredThisVolley );
arc.ArchiveFloat( &_hateModifier );
inventory.Archive( arc );
if ( arc.Saving() )
{
num = ammo_inventory.NumObjects();
}
else
{
ammo_inventory.ClearObjectList();
}
arc.ArchiveInteger( &num );
for( i = 1; i <= num; i++ )
{
Ammo * ptr;
if ( arc.Loading() )
{
ptr = new Ammo;
ammo_inventory.AddObject( ptr );
}
else
{
ptr = ammo_inventory.ObjectAt( i );
}
arc.ArchiveObject( ptr );
}
arc.ArchiveSafePointer( &newWeapon );
arc.ArchiveSafePointer( &currentBaseArmor );
//arc.ArchiveInteger( &firing_frame );
//arc.ArchiveInteger( &firing_anim );
arc.ArchiveVector( &offset_color );
arc.ArchiveVector( &offset_delta );
arc.ArchiveFloat( &offset_time );
arc.ArchiveFloat( &knock_start_time );
arc.ArchiveString( &blood_model );
arc.ArchiveInteger( &last_surface_hit );
arc.ArchiveInteger( &last_bone_hit );
arc.ArchiveUnsigned( &last_region_hit );
immunities.Archive( arc );
if ( arc.Saving() )
{
num = resistances.NumObjects();
arc.ArchiveInteger( &num );
for( i = 1 ; i <= num ; i++ )
{
resistance = resistances.ObjectAt( i );
arc.ArchiveInteger( &resistance->MODIndex );
arc.ArchiveInteger( &resistance->ResistanceAmount );
}
}
else
{
arc.ArchiveInteger( &num );
for( i = 1 ; i <= num ; i++ )
{
resistance = new Resistance;
resistances.AddObject( resistance );
arc.ArchiveInteger( &resistance->MODIndex );
arc.ArchiveInteger( &resistance->ResistanceAmount );
}
}
for( i = 0; i < MAX_ACTIVE_WEAPONS; i++ )
{
arc.ArchiveSafePointer( &activeWeaponList[ i ] );
}
arc.ArchiveUnsigned( &_viewMode );
arc.ArchiveBool( &_canSendInjuredEvent );
arc.ArchiveBool( &_headWatchAllowed );
arc.ArchiveBool( &_displayFireEffect );
arc.ArchiveVector( &gunoffset );
arc.ArchiveVector( &eyeposition );
arc.ArchiveInteger( &viewheight );
arc.ArchiveInteger( &means_of_death );
arc.ArchiveBoolean( &in_melee_attack );
arc.ArchiveBoolean( &in_ranged_attack );
arc.ArchiveBoolean( &in_block );
arc.ArchiveBoolean( &in_stun );
arc.ArchiveBoolean( &on_fire );
arc.ArchiveFloat( &on_fire_stop_time );
arc.ArchiveFloat( &next_catch_on_fire_time );
arc.ArchiveInteger( &on_fire_tagnums[ 0 ] );
arc.ArchiveInteger( &on_fire_tagnums[ 1 ] );
arc.ArchiveInteger( &on_fire_tagnums[ 2 ] );
arc.ArchiveSafePointer( &fire_owner );
arc.ArchiveBoolean( &attack_blocked );
arc.ArchiveFloat( &attack_blocked_time );
arc.ArchiveFloat( &max_mouth_angle );
arc.ArchiveInteger( &max_gibs );
arc.ArchiveFloat( &next_bleed_time );
}
void Sentient::ArchivePersistantData( Archiver &arc, qboolean sublevelTransition )
{
int i;
int num;
Q_UNUSED(sublevelTransition);
if ( arc.Loading() )
FreeInventory();
// archive the inventory
if ( arc.Saving() )
{
// count up the total number
num = inventory.NumObjects();
}
else
{
inventory.ClearObjectList();
}
// archive the number
arc.ArchiveInteger( &num );
// archive each item
for( i = 1; i <= num; i++ )
{
str name;
int amount;
Item * item = NULL;
if ( arc.Saving() )
{
Entity * ent;
ent = G_GetEntity( inventory.ObjectAt( i ) );
if ( ent && ent->isSubclassOf( Armor ) )
{
item = ( Item * )ent;
name = item->getName();
amount = item->getAmount();
}
else if ( ent && ent->isSubclassOf( Item ) )
{
item = ( Item * )ent;
name = item->model;
amount = item->getAmount();
}
else
{
error( "ArchivePersistantData", "Non Item in inventory\n" );
}
}
arc.ArchiveString( &name );
arc.ArchiveInteger( &amount );
if ( arc.Loading() )
{
item = giveItem( name, amount );
item->CancelEventsOfType( EV_Weapon_GiveStartingAmmo );
item->ProcessPendingEvents();
}
if ( item )
{
item->ArchivePersistantData( arc );
}
}
// archive the ammo inventory
if ( arc.Saving() )
{
// count up the total number
num = ammo_inventory.NumObjects();
}
else
{
ammo_inventory.ClearObjectList();
}
// archive the number
arc.ArchiveInteger( &num );
// archive each item
for( i = 1; i <= num; i++ )
{
str name;
int amount;
int maxamount;
Ammo * ptr;
if ( arc.Saving() )
{
ptr = ammo_inventory.ObjectAt( i );
name = ptr->getName();
amount = ptr->getAmount();
maxamount = ptr->getMaxAmount();
}
arc.ArchiveString( &name );
arc.ArchiveInteger( &amount );
arc.ArchiveInteger( &maxamount );
if ( arc.Loading() )
{
GiveAmmo( name, amount, false, maxamount );
}
}
for( i = 0; i < MAX_ACTIVE_WEAPONS; i++ )
{
str name;
if ( arc.Saving() )
{
if ( activeWeaponList[ i ] )
{
name = activeWeaponList[ i ]->getName();
}
else
{
name = "none";
}
}
arc.ArchiveString( &name );
if ( arc.Loading() )
{
if ( name != "none" )
{
Weapon * weapon;
weapon = ( Weapon * )FindItem( name );
if ( weapon )
{
ChangeWeapon( weapon, ( weaponhand_t )i );
}
}
}
}
arc.ArchiveFloat( &health );
arc.ArchiveFloat( &max_health );
if ( arc.Loading() )
{
currentBaseArmor = (Armor *)FindBaseArmor();
}
}
void Sentient::WeaponKnockedFromHands( void )
{
str realname;
realname = GetRandomAlias( "snd_lostweapon" );
if ( realname.length() > 1 )
{
Sound( realname.c_str() , CHAN_VOICE );
}
else
{
Sound( "snd_pain", CHAN_VOICE );
}
}
void Sentient::AddImmunity( Event *ev )
{
str immune_string;
int new_immunity;
int number_of_immunities;
int i;
// No Longer using Immunities. Now using Resistances
// A resistance of 100 is immune. However due to a large
// amount of legacy code, the immunity functions will still
// be inplace, however, they will instead be used as wrappers
// for the resistance functions
/*
number_of_immunities = ev->NumArgs();
for ( i = 1 ; i <= number_of_immunities ; i++ )
{
immune_string = ev->GetString( i );
new_immunity = MOD_NameToNum( immune_string );
if ( new_immunity != -1 )
immunities.AddUniqueObject( new_immunity );
}
*/
Resistance *new_resistance;
number_of_immunities = ev->NumArgs();
for ( i = 1 ; i <= number_of_immunities ; i ++ )
{
immune_string = ev->GetString( i );
new_immunity = MOD_NameToNum( immune_string );
if ( new_immunity != -1 )
{
new_resistance = new Resistance;
new_resistance->MODIndex = new_immunity;
new_resistance->ResistanceAmount = 100;
resistances.AddUniqueObject( new_resistance );
}
}
}
void Sentient::AddResistance( Event *ev )
{
Resistance *new_resistance = new Resistance;
if(new_resistance)
{
new_resistance->MODIndex = MOD_NameToNum(ev->GetString(1));
new_resistance->ResistanceAmount = ev->GetInteger(2);
resistances.AddUniqueObject( new_resistance );
}
}
void Sentient::RemoveImmunity( Event *ev )
{
str immune_string;
int old_immunity;
int number_of_immunities;
int i;
Resistance *old_resistance;
// No Longer using Immunities. Now using Resistances
// A resistance of 100 is immune. However due to a large
// amount of legacy code, the immunity functions will still
// be inplace, however, they will instead be used as wrappers
// for the resistance functions
/*
number_of_immunities = ev->NumArgs();
for ( i = 1 ; i <= number_of_immunities ; i++ )
{
immune_string = ev->GetString( i );
old_immunity = MOD_NameToNum( immune_string );
if ( old_immunity != -1 && immunities.ObjectInList( old_immunity ) )
immunities.RemoveObject( old_immunity );
}*/
number_of_immunities = ev->NumArgs();
for ( i = 1 ; i <= number_of_immunities ; i++ )
{
immune_string = ev->GetString( i );
old_immunity = MOD_NameToNum( immune_string );
for ( int x = resistances.NumObjects(); x > 0 ; x-- )
{
old_resistance = resistances.ObjectAt(i);
if( old_resistance->MODIndex == old_immunity )
{
resistances.RemoveObjectAt( x );
delete old_resistance;
old_resistance = 0;
}
}
}
}
void Sentient::RemoveResistance( Event *ev )
{
Resistance *old_resistance;
int resistance_idx;
resistance_idx = MOD_NameToNum( ev->GetString(1) );
for ( int i = resistances.NumObjects(); i > 0 ; i-- )
{
old_resistance = resistances.ObjectAt(i);
if( old_resistance->MODIndex == resistance_idx )
{
resistances.RemoveObjectAt( i );
delete old_resistance;
old_resistance = 0;
}
}
}
qboolean Sentient::Immune( int meansofdeath )
{
int number_of_immunities, i;
number_of_immunities = immunities.NumObjects();
for( i = 1 ; i <= number_of_immunities ; i++ )
{
if ( meansofdeath == immunities.ObjectAt( i ) )
return true;
}
return false;
}
qboolean Sentient::Resistant( int meansofdeath )
{
int number_of_resistances = resistances.NumObjects();
Resistance *checkResistance;
for( int i = 1; i <= number_of_resistances; i++ )
{
checkResistance = resistances.ObjectAt(i);
if( meansofdeath == checkResistance->MODIndex )
return true;
}
return false;
}
int Sentient::GetResistanceModifier( int meansofdeath )
{
int number_of_resistances = resistances.NumObjects();
Resistance *checkResistance;
for( int i = 1; i <= number_of_resistances; i++ )
{
checkResistance = resistances.ObjectAt(i);
if( meansofdeath == checkResistance->MODIndex )
return checkResistance->ResistanceAmount;
}
return 0;
}
Ammo *Sentient::FindAmmoByName( const str &name )
{
int count, i;
count = ammo_inventory.NumObjects();
for ( i=1; i<=count; i++ )
{
Ammo *ammo = ammo_inventory.ObjectAt( i );
if ( name == ammo->getName() )
{
return ammo;
}
}
return NULL;
}
int Sentient::AmmoIndex( const str &type )
{
Ammo *ammo;
ammo = FindAmmoByName( type );
if ( ammo )
return ammo->getIndex();
else
return 0;
}
int Sentient::AmmoCount( const str &type )
{
Ammo *ammo;
if ( !type.length() )
return 0;
ammo = FindAmmoByName( type );
if ( ammo )
return ammo->getAmount();
else
return 0;
}
int Sentient::MaxAmmoCount( const str &type )
{
Ammo *ammo;
ammo = FindAmmoByName( type );
if ( ammo )
return ammo->getMaxAmount();
else
return 0;
}
int Sentient::GiveAmmo( const str &type, int amount, bool pickedUp, int maxamount )
{
Ammo *ammo;
int startAmount = 0;
int totalAmount = 0;
int amountLeft;
ammo = FindAmmoByName( type );
if ( ammo )
{
// Add amount to current amount
startAmount = ammo->getAmount();
if ( maxamount >= 0 )
ammo->setMaxAmount( maxamount );
ammo->setAmount( ammo->getAmount() + amount );
}
else
{
// Create a new inventory entry with this name
ammo = new Ammo;
if ( maxamount >= 0 )
ammo->setMaxAmount( maxamount );
ammo->setAmount( amount );
ammo->setName( type );
ammo_inventory.AddObject( ammo );
}
totalAmount = ammo->getAmount() - startAmount;
amountLeft = amount - totalAmount;
if ( amountLeft > 0 )
{
int entnum;
Item *item;
Weapon *weapon;
int roomLeftInClip;
int amountToAdd;
int i;
// Try to give directly to weapons now
for ( i = 1 ; i <= inventory.NumObjects() ; i++ )
{
entnum = inventory.ObjectAt( i );
item = ( Item * )G_GetEntity( entnum );
if ( item->isSubclassOf( Weapon ) )
{
weapon = (Weapon *)item;
if ( weapon->HasFullClip() )
continue;
if ( stricmp( weapon->GetAmmoType( FIRE_MODE1 ).c_str(), type.c_str() ) != 0 )
continue;
roomLeftInClip = weapon->GetClipSize( FIRE_MODE1 ) - weapon->getAmmoInClip( FIRE_MODE1 );
if ( roomLeftInClip )
{
if ( amountLeft > roomLeftInClip )
amountToAdd = roomLeftInClip;
else
amountToAdd = amountLeft;
amountLeft -= amountToAdd;
weapon->SetAmmoAmount( weapon->getAmmoInClip( FIRE_MODE1 ) + amountToAdd, FIRE_MODE1 );
}
if ( amountLeft <= 0 )
{
break;
}
}
}
}
if ( pickedUp && this->isSubclassOf( Player ) && ( amount - amountLeft > 0 ) )
{
int iconIndex;
str imageName;
imageName = "sysimg/icons/items/ammo_";
imageName += type;
iconIndex = gi.imageindex( imageName );
((Player *)this)->setItemText( iconIndex, va( "$$PickedUp$$ %d $$Ammo-%s$$\n", amount, type.c_str() ) );
//((Player *)this)->setItemText( iconIndex, va( "$$PickedUp$$ %d $$Ammo-%s$$\n", amount - amountLeft, type.c_str() ) );
//gi.centerprintf ( edict, CENTERPRINT_IMPORTANCE_NORMAL, "$$PickedUp$$ %d %s\n", totalAmount, type.c_str() );
}
AmmoAmountChanged( ammo );
return amount - amountLeft;
}
int Sentient::UseAmmo( const str &type, int amount )
{
int count, i;
count = ammo_inventory.NumObjects();
for ( i=1; i<=count; i++ )
{
Ammo *ammo = ammo_inventory.ObjectAt( i );
if ( type == ammo->getName() )
{
int ammo_amount = ammo->getAmount();
// Less ammo than what we specified to use
if ( ammo_amount < amount )
{
ammo->setAmount( 0 );
AmmoAmountChanged( ammo );
return ammo_amount;
}
else
{
ammo->setAmount( ammo->getAmount() - amount );
AmmoAmountChanged( ammo );
return amount;
}
}
}
return 0;
}
void Sentient::AmmoAmountInClipChanged( const str &type, int amount_in_clip )
{
int count, i;
count = ammo_inventory.NumObjects();
for ( i=1; i<=count; i++ )
{
Ammo *ammo = ammo_inventory.ObjectAt( i );
if ( type == ammo->getName() )
{
AmmoAmountChanged( ammo, amount_in_clip );
}
}
}
void Sentient::JumpXY( Event *ev )
{
float forwardmove;
float sidemove;
float distance;
float time;
float speed;
Vector yaw_forward;
Vector yaw_left;
forwardmove = ev->GetFloat( 1 );
sidemove = ev->GetFloat( 2 );
speed = ev->GetFloat( 3 );
Vector( 0.0f, angles.y, 0.0f ).AngleVectors( &yaw_forward, &yaw_left );
velocity = ( yaw_forward * forwardmove ) - ( yaw_left * sidemove );
distance = velocity.length();
velocity *= speed / distance;
time = distance / speed;
velocity[ 2 ] = sv_currentGravity->integer * time * 0.5f;
}
void Sentient::MeleeAttackStart( Event * )
{
int i;
Entity *victim;
Vector mins, maxs, pos, end;
Container<Entity *> potential_victimlist;
mins = Vector( -8, -8, -8 );
maxs = Vector( 8, 8, 8 );
pos = centroid;
end = centroid + Vector( orientation[0] ) * 96.0f;
G_TraceEntities( pos, mins, maxs, end, &potential_victimlist, MASK_MELEE );
for( i = 1 ; i <= potential_victimlist.NumObjects() ; i++ )
{
victim = potential_victimlist.ObjectAt( i );
Event *event = new Event(EV_Sentient_AddMeleeAttacker);
event->AddEntity(this);
victim->ProcessEvent(event);
}
SetMeleeAttack(true);
}
void Sentient::MeleeAttackEnd( Event * )
{
SetMeleeAttack(false);
}
void Sentient::SetMeleeAttack(bool value)
{
in_melee_attack = value;
}
void Sentient::RangedAttackStart( Event * )
{
in_ranged_attack = true;
}
void Sentient::RangedAttackEnd( Event * )
{
in_ranged_attack = false;
}
void Sentient::BlockStart( Event * )
{
in_block = true;
}
void Sentient::BlockEnd( Event * )
{
in_block = false;
}
void Sentient::StunStart( Event * )
{
in_stun = true;
}
void Sentient::StunEnd( Event * )
{
in_stun = false;
}
void Sentient::ListInventory( void )
{
int i,count;
// Display normal inventory
count = inventory.NumObjects();
gi.Printf( "'Name' : 'Amount'\n" );
for ( i=1; i<=count; i++ )
{
int entnum = inventory.ObjectAt( i );
Item *item = ( Item * )G_GetEntity( entnum );
gi.Printf( "'%s' : '%d'\n", item->getName().c_str(), item->getAmount() );
}
// Display ammo inventory
count = ammo_inventory.NumObjects();
for ( i=1; i<=count; i++ )
{
Ammo *ammo = ammo_inventory.ObjectAt( i );
gi.Printf( "'%s' : '%d'\n", ammo->getName().c_str(), ammo->getAmount() );
}
}
void Sentient::SetAttackBlocked( qboolean blocked )
{
attack_blocked = blocked;
attack_blocked_time = level.time;
}
void Sentient::SetMaxMouthAngle( Event *ev )
{
max_mouth_angle = ev->GetFloat( 1 );
}
//----------------------------------------------------------------
// Name: CatchOnFire
// Class: Sentient
//
// Description: Sets the character on fire
//
// Parameters: Event *ev
//
// Returns: None
//----------------------------------------------------------------
void Sentient::CatchOnFire( Event * )
{
TryLightOnFire(MOD_FIRE, NULL);
}
void Sentient::TryLightOnFire( int meansofdeath, Entity *attacker )
{
float chance;
float min_time;
float add_time;
if ( !com_blood->integer )
return;
// Get the base chance of catching on fire
if ( meansofdeath == MOD_FIRESWORD )
chance = .5f;
else
chance = .05f;
// Get some values based on whether or not the sentient is a player or an actor
if ( this->isSubclassOf( Player ) )
{
chance = chance / 2.0f;
min_time = 4.0f;
add_time = 2.0f;
}
else
{
min_time = 8.0f;
add_time = 4.0f;
// Going to use MOD_FIRE to force fire on Actors - not players
if ( meansofdeath == MOD_FIRE )
chance = 1.0f;
}
// Make sure not immune to on_fire
if ( Immune( MOD_ON_FIRE ) )
return;
// See if we should catch the victim on fire
if ( G_Random() < chance )
{
Event *event;
int number_of_tags;
int i;
const char *tag_name;
int number_of_fires_to_add;
float scale;
float chanceToChange;
if ( !on_fire )
{
on_fire_tagnums[0] = -1;
on_fire_tagnums[1] = -1;
on_fire_tagnums[2] = -1;
}
if ( mass >= 200 )
number_of_fires_to_add = 3;
else if ( mass >= 100 )
number_of_fires_to_add = 2;
else
number_of_fires_to_add = 1;
if ( mass > 200 )
scale = 1.25f;
else if ( mass >= 300 )
scale = 1.75f;
else
scale = 1.00f;
chanceToChange = 0.05f;
number_of_tags = gi.NumTags( edict->s.modelindex );
if ( number_of_tags )
{
for ( i = 0 ; i < number_of_fires_to_add ; i++ )
{
if ( !on_fire || ( G_Random() < chanceToChange ) )
{
if ( on_fire && ( on_fire_tagnums[i] >= 0 ) )
{
tag_name = gi.Tag_NameForNum( edict->s.modelindex, on_fire_tagnums[i] );
if ( _displayFireEffect )
{
event = new Event( EV_RemoveAttachedModel );
event->AddString( tag_name );
event->AddFloat( 0.0f );
event->AddString( "models/fx_catchfire.tik" );
ProcessEvent( event );
}
}
on_fire_tagnums[i] = G_Random( number_of_tags );
tag_name = gi.Tag_NameForNum( edict->s.modelindex, on_fire_tagnums[i] );
if ( _displayFireEffect )
{
event = new Event( EV_AttachModel );
event->AddString( "fx_catchfire.tik" );
event->AddString( tag_name );
event->AddFloat( scale );
ProcessEvent( event );
}
}
}
if ( !on_fire )
{
on_fire = true;
fire_owner = attacker;
PostEvent( EV_Sentient_OnFire, FRAMETIME );
next_catch_on_fire_time = level.time + .15f + G_Random( .2f );
on_fire_stop_time = level.time + min_time + G_Random( add_time );
}
}
}
}
void Sentient::OnFire( Event * )
{
float damage;
// See if we should stop being on fire
if ( !on_fire || ( on_fire_stop_time <= level.time ) || ( gi.pointcontents( origin, 0 ) & MASK_WATER ) )
{
// Stop the fire
ProcessEvent( EV_Sentient_StopOnFire );
return;
}
// Cause a little bit of damage
if ( this->isSubclassOf( Player ) )
damage = 0.05;
else
damage = 0.5;
if ( fire_owner && fire_owner->isSubclassOf( Player ) )
{
Player *player = (Player *)(Entity *)fire_owner;
damage = player->getDamageDone( damage, MOD_ON_FIRE, false );
}
Damage( fire_owner, fire_owner, damage, vec_zero, vec_zero, vec_zero, 0, 0, MOD_ON_FIRE );
// Try to set other sentients on fire
if ( next_catch_on_fire_time <= level.time )
{
MeleeAttack( centroid, centroid, 0.05f, fire_owner, MOD_FIRE, maxs[0] + 10, -maxs[2] / 2, maxs[2] / 2, 0.0f, false );
next_catch_on_fire_time = level.time + .15f + G_Random( .2f );
}
// Call this event again next frame
PostEvent( EV_Sentient_OnFire, FRAMETIME );
}
void Sentient::StopOnFire( Event * )
{
int i;
const char *tag_name;
Event *event;
for( i = 0 ; i < 3 ; i++ )
{
if ( ( on_fire_tagnums[i] >= 0 ) && ( on_fire_tagnums[i] < gi.NumTags( edict->s.modelindex ) ) )
{
tag_name = gi.Tag_NameForNum( edict->s.modelindex, on_fire_tagnums[i] );
if ( _displayFireEffect )
{
event = new Event( EV_RemoveAttachedModel );
event->AddString( tag_name );
event->AddFloat( 0.0f );
event->AddString( "models/fx_catchfire.tik" );
ProcessEvent( event );
}
}
}
on_fire_tagnums[ 0 ] = -1;
on_fire_tagnums[ 1 ] = -1;
on_fire_tagnums[ 2 ] = -1;
on_fire = false;
CancelEventsOfType( EV_Sentient_OnFire );
}
void Sentient::SpawnBloodyGibs( Event *ev )
{
str gib_name;
int number_of_gibs;
float scale;
Entity *ent;
str real_gib_name;
if ( !com_blood->integer )
return;
//if ( GetActorFlag( ACTOR_FLAG_FADING_OUT ) )
// return;
gib_name = GetGibName();
if ( !gib_name.length() )
return;
// Determine the number of gibs to spawn
if ( ev->NumArgs() > 0 )
{
number_of_gibs = ev->GetInteger( 1 );
}
else
{
if ( max_gibs == 0 )
return;
if ( deadflag )
//number_of_gibs = G_Random( max_gibs / 2 ) + 1;
number_of_gibs = max_gibs;
else
number_of_gibs = (int)G_Random( (float)max_gibs ) + 1;
}
// Make sure we don't have too few or too many gibs
if ( ( number_of_gibs <= 0 ) || ( number_of_gibs > 9 ) )
return;
if ( ev->NumArgs() > 1 )
{
scale = ev->GetFloat( 2 );
}
else
{
// Calculate a good scale value
if ( mass <= 50 )
scale = 1.0;
else if ( mass <= 100 )
scale = 1.1;
else if ( mass <= 250 )
scale = 1.2;
else
scale = 1.3;
}
// Spawn the gibs
real_gib_name = gib_name;
real_gib_name += number_of_gibs;
real_gib_name += ".tik";
ent = new Entity( ENTITY_CREATE_FLAG_ANIMATE );
ent->setModel( real_gib_name.c_str() );
ent->setScale( scale );
ent->setOrigin( centroid );
ent->animate->RandomAnimate( "idle" );
ent->PostEvent( EV_Remove, 1.0f );
Sound( "snd_decap", CHAN_BODY, 1.0f, 300.0f );
}
void Sentient::SetMaxGibs( Event *ev )
{
max_gibs = ev->GetInteger( 1 );
}
void Sentient::GetStateAnims( Container<const char *> *c )
{
Q_UNUSED(c);
}
void Sentient::CheckAnimations( Event * )
{
int i,j;
Container<const char *>co;
const char *cs;
GetStateAnims( &co );
gi.DPrintf( "Unused Animations in TIKI\n" );
gi.DPrintf( "-------------------------\n" );
for( i=0; i<animate->NumAnims(); i++ )
{
const char *c;
c = gi.Anim_NameForNum( edict->s.modelindex, i );
for ( j=1; j<=co.NumObjects(); j++ )
{
cs = co.ObjectAt( j );
if ( !stricmp( c, cs ) )
{
goto out;
}
else if ( !strnicmp( c, cs, strlen( cs ) ) ) // partial match
{
int state_len = strlen( cs );
// Animation in tik file is longer than the state machine's anim
if ( (int)strlen( c ) > state_len )
{
if ( c[state_len] != '_' ) // If next character is an '_' then no match
{
goto out;
}
}
else
{
goto out;
}
}
}
// No match made
gi.DPrintf( "%s used in TIK file but not statefile\n", c );
out:
;
}
gi.DPrintf( "Unknown Animations in Statefile\n" );
gi.DPrintf( "-------------------------------\n" );
for ( j=1; j<=co.NumObjects(); j++ )
{
if ( !animate->HasAnim( co.ObjectAt( j ) ) )
{
gi.DPrintf( "%s in statefile is not in TIKI\n", co.ObjectAt( j ) );
}
}
}
void Sentient::SetStateFile( Event *ev )
{
Event *temp=ev;
if (ev != NULL)
{
temp=NULL;
}
// This function is not defined for a Sentient
// If Sentient was an abstract class then this would be pure virtual
}
void Sentient::ReceivedItem( Item * )
{
}
void Sentient::RemovedItem( Item * )
{
}
void Sentient::AmmoAmountChanged( Ammo * ammo, int ammo_in_clip )
{
Q_UNUSED(ammo);
Q_UNUSED(ammo_in_clip);
}
//
// Armor Stuff
//
void Sentient::SetArmorValue(int armorVal)
{
if ( currentBaseArmor )
currentBaseArmor->setAmount( armorVal );
}
int Sentient::GetArmorValue()
{
if ( currentBaseArmor )
return currentBaseArmor->getAmount();
return 0;
}
//----------------------------------------------------------------
// Name: setViewMode
// Class: Sentient
//
// Description: Sets the players current view mode
//
// Parameters: Event *ev - event (the name of the view mode to go to)
//
// Returns: None
//----------------------------------------------------------------
void Sentient::setViewMode( Event *ev )
{
str viewMode;
bool override;
viewMode = ev->GetString( 1 );
if ( ev->NumArgs() > 1 )
override = ev->GetBoolean( 2 );
else
override = true;
if ( _viewMode && !override )
return;
setViewMode( ev->GetString( 1 ) );
}
//----------------------------------------------------------------
// Name: setViewMode
// Class: Sentient
//
// Description: Sets the players current view mode
//
// Parameters: const str &viewModeName - the name of the view mode to go to
//
// Returns: None
//----------------------------------------------------------------
void Sentient::setViewMode( const str &viewModeName )
{
unsigned int newViewMode;
newViewMode = gi.GetViewModeMask( viewModeName.c_str() );
if ( _viewMode != newViewMode )
{
_viewMode = newViewMode;
if ( this->isSubclassOf( Player ) )
{
gi.centerprintf ( edict, CENTERPRINT_IMPORTANCE_NORMAL, "$$SwitchedTo$$ $$ViewMode-%s$$ $$Viewmode$$", viewModeName.c_str() );
}
}
}
//----------------------------------------------------------------
// Name: getViewMode
// Class: Sentient
//
// Description: Gets the current view mode of this sentient
//
// Parameters: None
//
// Returns: unsigned int - the current view mode bit(s)
//----------------------------------------------------------------
unsigned int Sentient::getViewMode( void )
{
return _viewMode;
}
//----------------------------------------------------------------
// Name: getActiveWeaponName
// Class: Sentient
//
// Description: Gets the name of the active weapon
//
// Parameters: Event *ev - contains name of the hand (left, right, or dual)
//
// Returns: str (through the event) - name of the active weapon
//----------------------------------------------------------------
void Sentient::getActiveWeaponName( Event *ev )
{
weaponhand_t hand = WEAPON_ANY;
str weaponName;
// See which hand we are concerned about
if ( ev->NumArgs() > 0 )
{
hand = WeaponHandNameToNum( ev->GetString( 1 ) );
}
else
{
hand = WEAPON_ANY;
}
getActiveWeaponName(hand, weaponName);
ev->ReturnString(weaponName);
}
void Sentient::getActiveWeaponName( weaponhand_t hand, str& weaponName)
{
Weapon* weapon;
weaponName = "None";
// Get the name of the weapon
if ( hand == WEAPON_ANY )
{
// Try the left hand
weapon = GetActiveWeapon( WEAPON_LEFT );
if ( weapon )
weaponName = weapon->getName();
// Try the right hand
if ( !weapon )
{
weapon = GetActiveWeapon( WEAPON_RIGHT );
if ( weapon )
weaponName = weapon->getName();
}
// Try the both hands
if ( !weapon )
{
weapon = GetActiveWeapon( WEAPON_DUAL );
if ( weapon )
weaponName = weapon->getName();
}
}
else if ( ( hand == WEAPON_LEFT ) || ( hand == WEAPON_RIGHT ) || ( hand == WEAPON_DUAL ) )
{
weapon = GetActiveWeapon( hand );
if ( weapon )
weaponName = weapon->getName();
}
}
//--------------------------------------------------------------
//
// Name: SwipeOn
// Class: Sentient
//
// Description: Turn the weapon swipes on for this sentient's weapons
//
// Parameters: Event *ev
//
// Returns: None
//
//--------------------------------------------------------------
void Sentient::SwipeOn( Event *ev )
{
Weapon *weapon;
weaponhand_t hand;
hand = WeaponHandNameToNum( ev->GetString( 1 ) );
if ( hand == WEAPON_ERROR )
return;
weapon = GetActiveWeapon( hand );
if ( weapon )
weapon->SetAnim( "swipeon", NULL );
}
//--------------------------------------------------------------
//
// Name: SwipeOff
// Class: Sentient
//
// Description: Turn the weapon swipes off for this sentient's weapons
//
// Parameters: Event *ev
//
// Returns: None
//
//--------------------------------------------------------------
void Sentient::SwipeOff( Event *ev )
{
Weapon *weapon;
weaponhand_t hand;
hand = WeaponHandNameToNum( ev->GetString( 1 ) );
if ( hand == WEAPON_ERROR )
return;
weapon = GetActiveWeapon( hand );
if ( weapon )
weapon->SetAnim( "swipeoff", NULL );
}
//--------------------------------------------------------------
// Name: AddHealthOverTime()
// Class: Sentient
//
// Description: Adds the specified percentage of health right away,
// then posts an event to AddHealthAtInterval to
// kick start a health regeneration event chain
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Sentient::AddHealthOverTime( Event *ev )
{
float percentageToAdd = ev->GetFloat( 1 );
float percentageToRegen = ev->GetFloat( 2 );
float interval = ev->GetFloat( 3 );
float maxPercentage = ev->GetFloat( 4 );
float addHealth;
float maxHealth;
Event *regenEvent;
//Translate our percentages into real numbers
addHealth = percentageToAdd * max_health;
maxHealth = maxPercentage * max_health;
//Check if adding the initial health will put us
//over our maximum allowed
if ( (addHealth + health) >= maxHealth )
{
health = maxHealth;
return;
}
else
health+= addHealth;
//Now Generate our AddHealthAtInterval Event
//which will continue the regeneration process
regenEvent = new Event(EV_Sentient_HealAtInterval);
if ( !regenEvent)
{
assert(regenEvent);
return;
}
regenEvent->AddFloat(percentageToRegen);
regenEvent->AddFloat(interval);
regenEvent->AddFloat(maxPercentage);
PostEvent( regenEvent, interval );
}
//--------------------------------------------------------------
// Name: AddHealthAtInterval()
// Class: Sentient
//
// Description: Adds the regen percentage to the health
// of the percentage... if the health is
// still not the maxPercentage, then it
// will generate a new event of its own
// type
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Sentient::AddHealthAtInterval( Event *ev )
{
float percentageToRegen = ev->GetFloat( 1 );
float interval = ev->GetFloat( 2 );
float maxPercentage = ev->GetFloat( 3 );
float addHealth;
float maxHealth;
Event *regenEvent;
//Translate our percentages into real numbers
addHealth = percentageToRegen * max_health;
maxHealth = maxPercentage * max_health;
//Check if adding the initial health will put us
//over our maximum allowed
if ( (addHealth + health) >= maxHealth )
{
SetHealth(maxHealth);
return;
}
else
SetHealth(health + addHealth);
//Now Generate a new AddHealthAtInterval Event
//which will continue the regeneration process
regenEvent = new Event(EV_Sentient_HealAtInterval);
if ( !regenEvent)
{
assert(regenEvent);
return;
}
regenEvent->AddFloat(percentageToRegen);
regenEvent->AddFloat(interval);
regenEvent->AddFloat(maxPercentage);
PostEvent( regenEvent, interval );
}
void Sentient::SetHealth( float newHealth )
{
health = newHealth;
if ( ( health / max_health ) > GetCriticalHealthPercentage() )
_canSendInjuredEvent = true;
}
//----------------------------------------------------------------
// Name: HeadWatchAllowed
// Class: Sentient
//
// Description: Sets whether to headwatch or not, defaults to true
//
// Parameters: Event *ev
//
// Returns: None
//----------------------------------------------------------------
void Sentient::HeadWatchAllowed(Event *ev)
{
if ( ev->NumArgs() > 0 )
HeadWatchAllowed( ev->GetBoolean( 1 ) );
else
HeadWatchAllowed( true );
}
void Sentient::HeadWatchAllowed( bool allowed )
{
_headWatchAllowed = allowed;
}
//--------------------------------------------------------------
//
// Name: SetWeaponAnim
// Class: Sentient
//
// Description: Put the weapon in the specified animation
//
// Parameters: Event *ev
//
// Returns: None
//
//--------------------------------------------------------------
void Sentient::SetWeaponAnim( Event *ev )
{
Weapon *weapon;
weaponhand_t hand;
str animname;
animname = ev->GetString( 1 );
hand = WeaponHandNameToNum( ev->GetString( 2 ) );
if ( hand == WEAPON_ERROR )
return;
weapon = GetActiveWeapon( hand );
if ( weapon )
weapon->playAnim( animname );
}
//--------------------------------------------------------------
// Name: SetDamageThreshold()
// Class: Sentient
//
// Description: Sets Values on our damageThreshold structure
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Sentient::SetDamageThreshold( Event *ev )
{
_damageThreshold.maxDamage = ev->GetFloat( 1 );
_damageThreshold.duration = ev->GetFloat( 2 );
_damageThreshold.startTime = 0;
}
//--------------------------------------------------------------
// Name: CheckDamageThreshold()
// Class: Sentient
//
// Description: Checks if we have hit our damageThreshold
// meaning, we have taken at least X damage
// in Y amount of time.
//
// Parameters: float damageValue
//
// Returns: None
//--------------------------------------------------------------
void Sentient::CheckDamageThreshold( float damageValue )
{
//First Check if we even need to bother doing further
//checks
float timeDiff;
if ( _damageThreshold.maxDamage < 0 )
return;
if( _damageThreshold.startTime == 0 )
_damageThreshold.startTime = level.time;
timeDiff = level.time - _damageThreshold.startTime;
//Now we check if our timeDiff is greater than our
//damageThreshold's duration. If it is, then obviously
//we didn't hit our threshold, so we need to update
//our starttime and currentDamage;
if ( _damageThreshold.duration >= 0 && timeDiff > _damageThreshold.duration )
{
_damageThreshold.startTime = level.time;
_damageThreshold.currentDamage = damageValue;
return;
}
//Well, we're still within our timeframe to hit our
//threshold, so we'll add the damageValue to our
//damageThreshold's currentDamage and check to see
//if we hit the wall.
_damageThreshold.currentDamage += damageValue;
if ( _damageThreshold.currentDamage >= _damageThreshold.maxDamage )
{
//Yay, more casting... I'm so excited about this
if ( this->isSubclassOf(Actor) )
{
Actor *actor;
actor = (Actor*)this;
actor->AddStateFlag(STATE_FLAG_DAMAGE_THRESHOLD_EXCEEDED);
}
//Clear out our structure
ClearDamageThreshold();
}
}
//--------------------------------------------------------------
// Name: DisplayFireEffect
// Class: Sentient
//
// Description: Sets the flag to display the fire effect while this entity
// is on fire. Note: this doesn't prevent the "on fire" state,
// nor does it prevent the damage from the fire. It's just
// the fire effect itself that's not displayed.
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Sentient::DisplayFireEffect( Event *ev )
{
if ( ev->NumArgs() > 0 )
_displayFireEffect = ev->GetBoolean( 1 );
else
_displayFireEffect = true;
}
void Sentient::ClearDamageThreshold( Event * )
{
ClearDamageThreshold();
}
void Sentient::ClearDamageThreshold()
{
_damageThreshold.startTime = level.time;
_damageThreshold.currentDamage = 0.0f;
}
//--------------------------------------------------------------
//
// Name: DropItemEvent
// Class: Sentient
//
// Description: Drops the specified item to the ground as a pickup
//
// Parameters: Event *ev
// -- str itenName
//
// Returns: None
//
//--------------------------------------------------------------
void Sentient::DropItemEvent( Event *ev )
{
int num, i;
Item *item;
str itemName = ev->GetString( 1 );
num = inventory.NumObjects();
for( i = num; i >= 1; i-- )
{
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
if ( item->getName() == itemName )
{
DropItem(item);
return;
}
}
}
//--------------------------------------------------------------
//
// Name: DropItem
// Class: Sentient
//
// Description: Drops the specified item. This function
// may grow.
//
// Parameters: Item *itemToDrop -- Item pointer to drop
//
// Returns: None
//
//--------------------------------------------------------------
void Sentient::DropItem( Item *itemToDrop )
{
if ( !itemToDrop )
return;
itemToDrop->Drop();
}
//--------------------------------------------------------------
//
// Name: DropItemsOnDeath
// Class: Sentient
//
// Description: Drops the item/weapon when the sentient dies
//
// Parameters: None
//
// Returns: None
//
//--------------------------------------------------------------
void Sentient::DropItemsOnDeath()
{
Weapon *weap = 0;
weap = GetActiveWeapon(WEAPON_RIGHT);
if ( !weap )
weap = GetActiveWeapon(WEAPON_LEFT);
if ( weap )
{
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
if ( gpm->hasProperty(weap->getArchetype(), "dropchance") )
{
float dropchance = gpm->getFloatValue(weap->getArchetype(), "dropchance");
if ( G_Random() < dropchance )
DropItem(weap);
}
if ( gpm->hasProperty(getArchetype(), "potionchance") )
{
str model;
float potionchance = gpm->getFloatValue(getArchetype(), "potionchance");
if ( G_Random() < potionchance )
{
model = gpm->getStringValue( "HealthPotion", "model");
}
else if ( G_Random() < potionchance * 2.0f )
{
model = gpm->getStringValue( "RedPotion", "model" );
}
if ( model.length() )
{
// Spawn the potion
SpawnArgs args;
Entity *ent;
Item *item;
args.setArg( "model", model );
ent = args.Spawn();
if ( !ent || !ent->isSubclassOf( Item ) )
return;
item = (Item *)ent;
item->setOrigin( centroid );
item->ProcessPendingEvents();
item->PlaceItem();
item->setOrigin( centroid );
item->velocity = Vector( G_CRandom( 100.0f ), G_CRandom( 100.0f ), 200.0f + G_Random( 200.0f ) );
item->edict->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
item->targetname = targetname;
item->targetname += "_item";
item->SetTargetName( item->targetname );
}
}
}
}
//--------------------------------------------------------------
//
// Name: WeaponEffectsAndSound
// Class: Sentient
//
// Description: Plays a weapon specified tiki effect at pos.
// Also plays a sound. All this comes from the gameplay database
//
// Parameters: Entity *weapon -- The weapon to use (only need Entity portion)
// const str& objname -- Object name to reference
// Vector pos -- location for the tiki effect
//
// Returns: None
//
//--------------------------------------------------------------
void Sentient::WeaponEffectsAndSound( Entity *weapon, const str& objname, Vector pos )
{
if ( !weapon )
return;
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
str scopestr = weapon->getArchetype() + "." + objname;
str effect = gpm->getStringValue(scopestr, "tikifx");
if ( effect.length() )
Entity::SpawnEffect(effect, pos, vec_zero, 2.0f);
str snd = gpm->getStringValue(scopestr, "wav");
if ( snd.length() )
{
float minpitch = gpm->getFloatValue(scopestr, "minpitch");
float maxpitch = gpm->getFloatValue(scopestr, "maxpitch");
float pitch = G_Random(maxpitch - minpitch) + minpitch;
int channel = CHAN_BODY;
float volume = -1.0f;
float mindist = -1.0f;
if ( gpm->hasProperty(scopestr,"channel") )
channel = (int)gpm->getFloatValue(scopestr, "channel");
if ( gpm->hasProperty(scopestr,"volume") )
volume = (int)gpm->getFloatValue(scopestr, "volume");
if ( gpm->hasProperty(scopestr,"mindist") )
mindist = (int)gpm->getFloatValue(scopestr, "mindist");
weapon->Sound(snd, channel, volume, mindist, NULL, pitch);
}
}
void Sentient::SetArmorActiveStatus( Event *ev )
{
bool status = ev->GetBoolean( 1 );
SetArmorActiveStatus( status );
}
void Sentient::SetArmorActiveStatus(bool status)
{
if ( currentBaseArmor )
{
Event* statusEvent;
statusEvent = new Event ( EV_Armor_SetActiveStatus );
statusEvent->AddInteger( status );
currentBaseArmor->ProcessEvent ( statusEvent );
}
}
void Sentient::SetArmorMultiplier( Event *ev )
{
if ( currentBaseArmor )
{
float multiplier = ev->GetFloat( 1 );
SetArmorMultiplier( multiplier );
}
}
void Sentient::SetArmorMultiplier( float multiplier )
{
Event* multiplierEvent;
multiplierEvent = new Event ( EV_Armor_SetMultiplier );
multiplierEvent->AddFloat( multiplier );
if ( currentBaseArmor )
currentBaseArmor->ProcessEvent ( multiplierEvent );
}
void Sentient::AddToMyArmor( Event *ev )
{
AddToMyArmor( ev->GetFloat( 1 ) );
}
void Sentient::AddToMyArmor( float amountToAdd )
{
if ( currentBaseArmor )
currentBaseArmor->setAmount( currentBaseArmor->getAmount() + amountToAdd );
}
void Sentient::SetMyArmorAmount( Event *ev )
{
SetMyArmorAmount( ev->GetFloat(1) );
}
void Sentient::SetMyArmorAmount( float amount )
{
if ( currentBaseArmor )
currentBaseArmor->setAmount( amount );
}
void Sentient::ArmorEvent( Event *ev )
{
if ( !currentBaseArmor )
return;
Event *e;
e = new Event( ev->GetToken( 1 ) );
for( int i=2; i<=ev->NumArgs(); i++ )
e->AddToken( ev->GetToken( i ) );
currentBaseArmor->ProcessEvent( e );
}
void Sentient::SetHateModifier( Event *ev )
{
float modifier;
modifier = ev->GetFloat( 1 );
SetHateModifier ( modifier );
}
void Sentient::SetHateModifier( float modifier )
{
_hateModifier = modifier;
}
float Sentient::GetHateModifier()
{
return _hateModifier;
}
void Sentient::cacheStateMachineAnims( Event *ev )
{
G_CacheStateMachineAnims( this, ev->GetString( 1 ) );
}
void Sentient::freeConditionals( Container<Conditional *> &conditionalsToDelete )
{
int i;
Conditional *conditional;
for ( i = conditionalsToDelete.NumObjects() ; i > 0 ; i-- )
{
conditional = conditionalsToDelete.ObjectAt( i );
if ( conditional )
{
delete conditional;
}
}
conditionalsToDelete.FreeObjectList();
}