5119 lines
113 KiB
C++
5119 lines
113 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 *ev )
|
|
{
|
|
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 *ev )
|
|
{
|
|
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 *ev )
|
|
{
|
|
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 *ev )
|
|
{
|
|
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;
|
|
|
|
|
|
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 *ev )
|
|
{
|
|
edict->s.renderfx &= ~RF_SHADOW;
|
|
}
|
|
|
|
void Sentient::TurnOnShadow( Event *ev )
|
|
{
|
|
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( ¤tBaseArmor );
|
|
|
|
//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;
|
|
|
|
|
|
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 *ev )
|
|
{
|
|
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 *ev )
|
|
{
|
|
SetMeleeAttack(false);
|
|
}
|
|
|
|
void Sentient::SetMeleeAttack(bool value)
|
|
{
|
|
in_melee_attack = value;
|
|
}
|
|
|
|
void Sentient::RangedAttackStart( Event *ev )
|
|
{
|
|
in_ranged_attack = true;
|
|
}
|
|
|
|
void Sentient::RangedAttackEnd( Event *ev )
|
|
{
|
|
in_ranged_attack = false;
|
|
}
|
|
|
|
void Sentient::BlockStart( Event *ev )
|
|
{
|
|
in_block = true;
|
|
}
|
|
|
|
void Sentient::BlockEnd( Event *ev )
|
|
{
|
|
in_block = false;
|
|
}
|
|
|
|
void Sentient::StunStart( Event *ev )
|
|
{
|
|
in_stun = true;
|
|
}
|
|
|
|
void Sentient::StunEnd( Event *ev )
|
|
{
|
|
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 *ev )
|
|
{
|
|
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 *ev )
|
|
{
|
|
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 *ev )
|
|
{
|
|
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 )
|
|
{
|
|
}
|
|
|
|
void Sentient::CheckAnimations( Event *ev )
|
|
{
|
|
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 ( 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 * item )
|
|
{
|
|
}
|
|
|
|
void Sentient::RemovedItem( Item * item )
|
|
{
|
|
}
|
|
|
|
void Sentient::AmmoAmountChanged( Ammo * ammo, int 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 *ev )
|
|
{
|
|
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();
|
|
}
|