mirror of
https://github.com/unknownworlds/NS.git
synced 2024-12-21 01:41:13 +00:00
c89fe4e37e
o Added +use ability to hives which teleports the player to the next hive. - Hives are round robin in entity id order ( order will not change with hive creation and destruction ). - Hives under attack are not given priority. - Increased the range of +use by 2x for hives ( more options here, including 2d distance checks and traces ) git-svn-id: https://unknownworlds.svn.cloudforge.com/ns1@416 67975925-1194-0748-b3d5-c16f83f1a3a1
988 lines
29 KiB
C++
988 lines
29 KiB
C++
//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. =========
|
|
//
|
|
// The copyright to the contents herein is the property of Charles G. Cleveland.
|
|
// The contents may be used and/or copied only with the written permission of
|
|
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
|
|
// the agreement/contract under which the contents have been supplied.
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $Workfile: AvHHive.cpp $
|
|
// $Date: 2002/11/22 21:28:16 $
|
|
//
|
|
//-------------------------------------------------------------------------------
|
|
// $Log: AvHHive.cpp,v $
|
|
// Revision 1.20 2002/11/22 21:28:16 Flayra
|
|
// - mp_consistency changes
|
|
//
|
|
// Revision 1.19 2002/11/06 01:38:37 Flayra
|
|
// - Added ability for buildings to be enabled and disabled, for turrets to be shut down
|
|
// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.)
|
|
//
|
|
// Revision 1.18 2002/11/03 04:50:26 Flayra
|
|
// - Hard-coded gameplay constants instead of putting in skill.cfg
|
|
//
|
|
// Revision 1.17 2002/10/24 21:26:37 Flayra
|
|
// - Fixed hive wound animation when dying
|
|
// - Hives now choose a random spawn point instead of the first
|
|
//
|
|
// Revision 1.16 2002/10/16 20:53:21 Flayra
|
|
// - Hives have more health while growing
|
|
//
|
|
// Revision 1.15 2002/10/16 00:57:19 Flayra
|
|
// - Fixed hive not going solid sometimes (when player was in the way I think when construction complete)
|
|
// - Fixed exploit where hives can be manually sped up (oops!)
|
|
//
|
|
// Revision 1.14 2002/10/03 18:50:27 Flayra
|
|
// - Trigger "hive complete" alert
|
|
// - Trigger "hive is dying" alert
|
|
//
|
|
// Revision 1.13 2002/09/23 22:16:44 Flayra
|
|
// - Removed resource donation at hives
|
|
//
|
|
// Revision 1.12 2002/09/09 19:52:57 Flayra
|
|
// - Animations play properly
|
|
// - Hive can be hit once it starts gestating (hive becomes solid when gestating, not when complete)
|
|
// - Respawn fixes
|
|
//
|
|
// Revision 1.11 2002/08/16 02:36:01 Flayra
|
|
// - New damage system
|
|
// - Fixed bug where hive was absorbing too much damage in armor
|
|
//
|
|
// Revision 1.10 2002/08/02 21:59:36 Flayra
|
|
// - New alert system
|
|
//
|
|
// Revision 1.9 2002/07/26 23:04:19 Flayra
|
|
// - Generate numerical feedback for damage events
|
|
//
|
|
// Revision 1.8 2002/07/23 17:06:09 Flayra
|
|
// - Added ability for aliens to donate their resources at the hive, bind technology to a hive (so builders can choose the route), fixed bug where death animation played repeatedly
|
|
//
|
|
// Revision 1.7 2002/07/08 17:03:04 Flayra
|
|
// - Refactored reinforcements
|
|
//
|
|
// Revision 1.6 2002/07/01 21:33:48 Flayra
|
|
// - Hives can no longer be "used" to speed construction, wound sounds play on CHAN_BODY
|
|
//
|
|
// Revision 1.5 2002/06/25 18:00:14 Flayra
|
|
// - Play sequence for non-active hives
|
|
//
|
|
// Revision 1.4 2002/06/03 16:47:49 Flayra
|
|
// - Hives are base buildables now (bug with allowing use to speed building), added other hive anims for hurt, death, bad-touch, fixed bug where hives didn't get full health when they were initially built (after being killed once)
|
|
//
|
|
// Revision 1.3 2002/05/28 17:46:05 Flayra
|
|
// - Mark hives as persistent so they aren't deleted on level cleanup, new hive sight support, reinforcement refactoring and fixes
|
|
//
|
|
// Revision 1.2 2002/05/23 02:33:42 Flayra
|
|
// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development.
|
|
//
|
|
//===============================================================================
|
|
|
|
#include "mod/AvHHive.h"
|
|
#include "mod/AvHGamerules.h"
|
|
#include "mod/AvHServerUtil.h"
|
|
#include "mod/AvHSharedUtil.h"
|
|
#include "mod/AvHAlienAbilityConstants.h"
|
|
#include "mod/AvHAlienEquipmentConstants.h"
|
|
#include "mod/AvHHulls.h"
|
|
#include "mod/AvHMovementUtil.h"
|
|
#include "mod/AvHSoundListManager.h"
|
|
#include "mod/AvHServerVariables.h"
|
|
#include "mod/AvHParticleConstants.h"
|
|
#include "mod/AvHSpecials.h"
|
|
#include "mod/AvHPlayerUpgrade.h"
|
|
#include "util/MathUtil.h"
|
|
#include <vector>
|
|
|
|
extern AvHSoundListManager gSoundListManager;
|
|
BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot );
|
|
|
|
LINK_ENTITY_TO_CLASS( keTeamHive, AvHHive );
|
|
|
|
extern int gRegenerationEventID;
|
|
const int kScaredAnimationIndex = 9;
|
|
|
|
AvHHive::AvHHive() : AvHBaseBuildable(TECH_HIVE, ALIEN_BUILD_HIVE, kesTeamHive, AVH_USER3_HIVE)
|
|
{
|
|
// This value should be the default in the .fgd
|
|
this->mMaxSpawnDistance = 2000;
|
|
this->mMaxHitPoints = 0;
|
|
this->mActive = false;
|
|
this->mSolid = false;
|
|
this->mSpawning = false;
|
|
this->mTimeLastWoundSound = -1;
|
|
this->mTechnology = MESSAGE_NULL;
|
|
this->mEnergy = 0.0f;
|
|
this->mLastTimeScannedHives=-1.0f;
|
|
this->mTeleportHiveIndex=-1;;
|
|
|
|
}
|
|
|
|
bool AvHHive::CanBecomeActive() const
|
|
{
|
|
return !this->mActive;// && (!this->mHasBeenKilled || !GetGameRules()->GetIsTournamentMode() || GetGameRules()->GetCheatsEnabled());
|
|
}
|
|
|
|
void AvHHive::ConstructUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue)
|
|
{
|
|
int a = 0;
|
|
}
|
|
|
|
void AvHHive::DonateUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue)
|
|
{
|
|
// Player is trying to donate his resources to the pool
|
|
if(this->GetIsActive())
|
|
{
|
|
AvHPlayer* inActivatingPlayer = dynamic_cast<AvHPlayer*>(inActivator);
|
|
if(inActivatingPlayer && (inActivator->pev->team == this->pev->team))
|
|
{
|
|
// Take some resources, give some resources
|
|
const float kResourcesToDonatePerUse = .4f;
|
|
float theResourcesToGive = min(inActivatingPlayer->GetResources(), kResourcesToDonatePerUse);
|
|
|
|
if(theResourcesToGive > 0.0f)
|
|
{
|
|
AvHTeam* theTeam = inActivatingPlayer->GetTeamPointer();
|
|
if(theTeam)
|
|
{
|
|
inActivatingPlayer->SetResources(inActivatingPlayer->GetResources() - theResourcesToGive);
|
|
theTeam->SetTeamResources(theTeam->GetTeamResources() + theResourcesToGive);
|
|
|
|
if(g_engfuncs.pfnRandomLong(0, 20) == 0)
|
|
{
|
|
PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );
|
|
|
|
// Just say "resources donated"
|
|
inActivatingPlayer->PlaybackNumericalEvent(kNumericalInfoResourcesDonatedEvent, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
AvHTeamNumber AvHHive::GetTeamNumber() const
|
|
{
|
|
return (AvHTeamNumber)this->pev->team;
|
|
}
|
|
|
|
bool AvHHive::GetIsActive() const
|
|
{
|
|
return this->mActive;
|
|
}
|
|
|
|
bool AvHHive::GetIsOrganic() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool AvHHive::GetIsSpawning() const
|
|
{
|
|
return this->mSpawning;
|
|
}
|
|
|
|
int AvHHive::GetMaxSpawnDistance() const
|
|
{
|
|
return this->mMaxSpawnDistance;
|
|
}
|
|
|
|
int AvHHive::GetMoveType() const
|
|
{
|
|
return MOVETYPE_NONE;
|
|
}
|
|
|
|
float AvHHive::GetTimeLastContributed()
|
|
{
|
|
return this->mTimeLastContributed;
|
|
}
|
|
|
|
void AvHHive::SetTimeLastContributed(float inTime)
|
|
{
|
|
this->mTimeLastContributed = inTime;
|
|
}
|
|
|
|
int AvHHive::GetIdle1Animation() const
|
|
{
|
|
int theAnimation = -1;
|
|
|
|
if(this->GetIsBuilt())
|
|
{
|
|
theAnimation = 2;
|
|
}
|
|
|
|
return theAnimation;
|
|
}
|
|
|
|
int AvHHive::GetIdle2Animation() const
|
|
{
|
|
int theAnimation = -1;
|
|
|
|
if(this->GetIsBuilt())
|
|
{
|
|
theAnimation = 3;
|
|
}
|
|
|
|
return theAnimation;
|
|
}
|
|
|
|
int AvHHive::GetTakeDamageAnimation() const
|
|
{
|
|
int theAnimation = -1;
|
|
|
|
// Choose animation based on global time, so animation doesn't interrupt itself
|
|
float theTime = gpGlobals->time;
|
|
int theOffset = (int)(ceil(theTime) - theTime + .5f);
|
|
|
|
if(this->GetIsActive())
|
|
{
|
|
// Play wound animation.
|
|
theAnimation = 5 + theOffset;
|
|
}
|
|
else
|
|
{
|
|
// Use still-building flinch anims
|
|
theAnimation = 7 + theOffset;
|
|
}
|
|
|
|
return theAnimation;
|
|
}
|
|
|
|
int AvHHive::GetPointValue(void) const
|
|
{
|
|
return BALANCE_VAR(kScoringHiveValue);
|
|
}
|
|
|
|
int AvHHive::GetSpawnAnimation() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
AvHMessageID AvHHive::GetTechnology() const
|
|
{
|
|
return this->mTechnology;
|
|
}
|
|
|
|
void AvHHive::SetTechnology(AvHMessageID inMessageID)
|
|
{
|
|
this->mTechnology = inMessageID;
|
|
}
|
|
|
|
void AvHHive::HiveAliveThink(void)
|
|
{
|
|
// For some reason, velocity is non-zero when created (meant they were showing up on motion-tracking)
|
|
this->pev->velocity = Vector(0, 0, 0);
|
|
|
|
if(GetGameRules()->GetGameStarted())
|
|
{
|
|
if(!this->mActive)
|
|
{
|
|
bool theIsBuilding, theIsResearching;
|
|
float thePercentage;
|
|
AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, thePercentage);
|
|
|
|
float theBuildTime = GetGameRules()->GetBuildTimeForMessageID(this->GetMessageID());
|
|
float theBuildPercentage = kHiveAliveThinkInterval/theBuildTime;
|
|
|
|
float theNewPercentage = min(thePercentage + theBuildPercentage, 1.0f);
|
|
this->SetNormalizedBuildPercentage(theNewPercentage);
|
|
}
|
|
else
|
|
{
|
|
this->ProcessHealing();
|
|
|
|
// Play idle anims
|
|
AvHBaseBuildable::AnimateThink();
|
|
}
|
|
|
|
this->UpdateReinforcements();
|
|
|
|
//this->UpdateUmbra();
|
|
}
|
|
|
|
// Set next think
|
|
this->pev->nextthink = gpGlobals->time + kHiveAliveThinkInterval;
|
|
}
|
|
|
|
void AvHHive::UpdateUmbra()
|
|
{
|
|
bool theIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(this->entindex());
|
|
if(theIsUnderAttack)
|
|
{
|
|
if(this->mTimeOfNextUmbra == -1)
|
|
{
|
|
this->mTimeOfNextUmbra = gpGlobals->time + RANDOM_LONG(5, 15);
|
|
}
|
|
}
|
|
|
|
if((this->mTimeOfNextUmbra != -1) && (gpGlobals->time > this->mTimeOfNextUmbra))
|
|
{
|
|
// If we're under attack, sometimes create umbra at hive
|
|
vec3_t theUmbraOrigin = this->pev->origin;
|
|
|
|
// else create umbra at random spawn
|
|
// if(!theIsUnderAttack)
|
|
// {
|
|
// CBaseEntity* theSpawnPoint = GetGameRules()->GetRandomHiveSpawnPoint(this, this->pev->origin, this->GetMaxSpawnDistance());
|
|
// if(theSpawnPoint)
|
|
// {
|
|
// VectorCopy(theSpawnPoint->pev->origin, theUmbraOrigin);
|
|
// }
|
|
// }
|
|
|
|
// Create umbra around it, play "scared" anim
|
|
//this->CreateUmbra(theUmbraOrigin);
|
|
|
|
this->PlayAnimationAtIndex(kScaredAnimationIndex, true);
|
|
}
|
|
}
|
|
|
|
void AvHHive::KeyValue(KeyValueData* pkvd)
|
|
{
|
|
this->SetPersistent();
|
|
|
|
if(FStrEq(pkvd->szKeyName, "maxspawndistance"))
|
|
{
|
|
this->mMaxSpawnDistance = atoi(pkvd->szValue);
|
|
pkvd->fHandled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
AvHBaseBuildable::KeyValue(pkvd);
|
|
}
|
|
}
|
|
|
|
void AvHHive::TriggerDeathAudioVisuals()
|
|
{
|
|
AvHSUPlayParticleEvent(kpsHiveDeath, this->edict(), this->pev->origin);
|
|
|
|
AvHSUExplodeEntity(this, matFlesh);
|
|
|
|
EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kHiveDeathSound, 1.0, ATTN_IDLE);
|
|
|
|
// Play death animation (increment time just to make sure there's no idle anim played after killed and before death)
|
|
const float kDeathAnimationLength = 1.2f;
|
|
|
|
this->PlayAnimationAtIndex(10, true);
|
|
|
|
// Then explode
|
|
//SetThink(&AvHHive::DeathThink);
|
|
//this->pev->nextthink = gpGlobals->time + kDeathAnimationLength;
|
|
|
|
this->FireDeathTarget();
|
|
}
|
|
|
|
void AvHHive::Precache(void)
|
|
{
|
|
AvHBaseBuildable::Precache();
|
|
|
|
PRECACHE_UNMODIFIED_SOUND(kHiveSpawnSound);
|
|
PRECACHE_UNMODIFIED_SOUND(kHiveAmbientSound);
|
|
PRECACHE_UNMODIFIED_SOUND(kHiveDeathSound);
|
|
|
|
PRECACHE_UNMODIFIED_MODEL(kHiveModel);
|
|
|
|
CBreakable::PrecacheAll();
|
|
}
|
|
|
|
void AvHHive::ProcessHealing()
|
|
{
|
|
// Regenerate nearby friendly aliens
|
|
CBaseEntity* theEntity = NULL;
|
|
const int theHiveHealRadius = BALANCE_VAR(kHiveHealRadius);
|
|
|
|
while((theEntity = UTIL_FindEntityInSphere(theEntity, this->pev->origin, theHiveHealRadius)) != NULL)
|
|
{
|
|
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(theEntity);
|
|
if(thePlayer)
|
|
{
|
|
if(thePlayer->GetIsRelevant() && (thePlayer->GetTeam() == this->GetTeamNumber()) && !thePlayer->GetIsBeingDigested())
|
|
{
|
|
// Hive heals percentage of player health
|
|
float theRegenPercentage = BALANCE_VAR(kHiveRegenerationPercentage);
|
|
int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3, thePlayer->GetExperienceLevel());
|
|
float theRegenAmount = (theRegenPercentage*theMaxHealth);
|
|
thePlayer->Heal(theRegenAmount, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Regenerate self
|
|
bool theDidHeal = false;
|
|
|
|
// If we aren't at full health, heal health
|
|
if(this->pev->health < this->mMaxHitPoints)
|
|
{
|
|
float theHiveRegenAmount = BALANCE_VAR(kHiveRegenerationAmount);
|
|
float theCombatModeScalar = /*GetGameRules()->GetIsCombatMode() ? (1.0f/BALANCE_VAR(kCombatModeTimeScalar)) :*/ 1.0f;
|
|
|
|
this->pev->health = min((float)this->mMaxHitPoints, this->pev->health + theHiveRegenAmount*theCombatModeScalar);
|
|
theDidHeal = true;
|
|
}
|
|
|
|
// Play regen event
|
|
if(theDidHeal)
|
|
{
|
|
// Play regeneration event
|
|
PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );
|
|
}
|
|
}
|
|
|
|
void AvHHive::ResetEntity(void)
|
|
{
|
|
AvHReinforceable::ResetEntity();
|
|
AvHBaseBuildable::ResetEntity();
|
|
|
|
this->ResetCloaking();
|
|
|
|
this->SetInactive();
|
|
|
|
this->pev->health = this->mMaxHitPoints;
|
|
|
|
this->pev->takedamage = DAMAGE_NO;
|
|
|
|
// Reset parasites, etc.
|
|
this->pev->iuser4 = 0;
|
|
this->SetPersistent();
|
|
|
|
this->mTimeOfNextUmbra = -1;
|
|
|
|
// Reset fuser1 progress
|
|
this->pev->fuser1 = 0;
|
|
}
|
|
|
|
void AvHHive::ResetReinforcingPlayer(bool inSuccess)
|
|
{
|
|
AvHReinforceable::ResetReinforcingPlayer(inSuccess);
|
|
|
|
this->mEnergy = 0.0f;
|
|
}
|
|
|
|
bool AvHHive::SetSolid(bool inForce)
|
|
{
|
|
// Check to make sure there aren't any players in the destination area
|
|
CBaseEntity* pList[128];
|
|
|
|
// Crank up the area just to be safe
|
|
Vector theMinArea = this->pev->origin + kHiveMinSize;
|
|
Vector theMaxArea = this->pev->origin + kHiveMaxSize;
|
|
|
|
// TODO: If players are blocking this area for too long, spawn hive and kill them
|
|
int theNumBlockingEntities = UTIL_EntitiesInBox(pList, 128, theMinArea, theMaxArea, FL_CLIENT);
|
|
if((theNumBlockingEntities == 0) || inForce)
|
|
{
|
|
this->pev->solid = SOLID_BBOX;
|
|
this->pev->movetype = MOVETYPE_NONE;
|
|
|
|
UTIL_SetSize(this->pev, kHiveMinSize, kHiveMaxSize);
|
|
|
|
// pev->frame = 0;
|
|
// pev->body = 3;
|
|
// pev->sequence = 0;
|
|
// // ResetSequenceInfo( );
|
|
// pev->framerate = 0;
|
|
//
|
|
// UTIL_SetOrigin(pev, pev->origin);
|
|
// UTIL_SetSize(pev, Vector(-20, -20, 0), Vector(20, 20, 28) );
|
|
|
|
SetTouch(&AvHHive::HiveTouch);
|
|
|
|
this->mSolid = true;
|
|
}
|
|
|
|
return this->mSolid;
|
|
}
|
|
|
|
void AvHHive::SetHasBeenBuilt()
|
|
{
|
|
AvHBuildable::SetHasBeenBuilt();
|
|
|
|
GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_HIVE_COMPLETE, this->entindex());
|
|
|
|
// Make hive support any unassigned upgrade technologies (happens after a hive supporting a technology is destroyed and then rebuilt)
|
|
AvHTeamNumber theTeam = (AvHTeamNumber)this->pev->team;
|
|
AvHTeam* theTeamPointer = GetGameRules()->GetTeam(theTeam);
|
|
if(theTeamPointer)
|
|
{
|
|
AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades();
|
|
|
|
if(AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_DEFENSE) > 0)
|
|
{
|
|
AvHSUUpdateHiveTechology(theTeam, ALIEN_BUILD_DEFENSE_CHAMBER);
|
|
}
|
|
|
|
if(AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_MOVEMENT) > 0)
|
|
{
|
|
AvHSUUpdateHiveTechology(theTeam, ALIEN_BUILD_MOVEMENT_CHAMBER);
|
|
}
|
|
|
|
if(AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_SENSORY) > 0)
|
|
{
|
|
AvHSUUpdateHiveTechology(theTeam, ALIEN_BUILD_SENSORY_CHAMBER);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AvHHive::StartSpawningForTeam(AvHTeamNumber inTeam, bool inForce)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
if(this->SetSolid(inForce))
|
|
{
|
|
this->pev->team = inTeam;
|
|
this->pev->takedamage = DAMAGE_YES;
|
|
|
|
this->pev->rendermode = kRenderNormal;
|
|
this->pev->renderamt = 0;
|
|
|
|
SetBits(pev->flags, FL_MONSTER);
|
|
SetUpgradeMask(&this->pev->iuser4, MASK_BUILDABLE);
|
|
|
|
this->mSpawning = true;
|
|
this->pev->health = kBaseHealthPercentage*this->mBaseHealth;
|
|
|
|
// Looping, growing animation
|
|
this->pev->sequence = 0;
|
|
this->pev->frame = 0;
|
|
ResetSequenceInfo();
|
|
|
|
this->pev->nextthink = gpGlobals->time + kHiveAliveThinkInterval;
|
|
SetThink(&AvHHive::HiveAliveThink);
|
|
SetUse(&AvHHive::TeleportUse);
|
|
|
|
theSuccess = true;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
|
|
void AvHHive::Spawn()
|
|
{
|
|
this->Precache();
|
|
|
|
AvHBaseBuildable::Spawn();
|
|
|
|
this->pev->classname = MAKE_STRING(kesTeamHive);
|
|
//this->pev->movetype = MOVETYPE_FLY;
|
|
|
|
this->pev->movetype = MOVETYPE_FLY;
|
|
this->pev->solid = SOLID_NOT;
|
|
this->pev->flags = 0;
|
|
this->pev->iuser3 = AVH_USER3_HIVE;
|
|
|
|
this->mMaxHitPoints = GetGameRules()->GetBaseHealthForMessageID(ALIEN_BUILD_HIVE);
|
|
|
|
SET_MODEL( ENT(this->pev), kHiveModel);
|
|
//this->pev->scale = 2;
|
|
|
|
// this->pev->sequence = 0;
|
|
// this->pev->frame = 0;
|
|
// ResetSequenceInfo();
|
|
|
|
this->ResetEntity();
|
|
|
|
}
|
|
|
|
void AvHHive::SetActive()
|
|
{
|
|
AvHBaseBuildable::SetActive();
|
|
|
|
if(!this->mActive)
|
|
{
|
|
this->mActive = true;
|
|
this->mSpawning = false;
|
|
|
|
this->mTimeLastContributed = gpGlobals->time;
|
|
|
|
// Start animating
|
|
this->pev->sequence = 1;
|
|
this->pev->frame = 0;
|
|
ResetSequenceInfo();
|
|
//AvHSUSetCollisionBoxFromSequence(this->pev);
|
|
|
|
// Play spawn sound here
|
|
EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kHiveSpawnSound, 1.0, ATTN_IDLE);
|
|
|
|
// Note: this isn't being created for the first hive because this sound plays before the map is totally up
|
|
UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kHiveAmbientSound, 1.0f, 2.0, 0, 100);
|
|
|
|
this->FireSpawnTarget();
|
|
|
|
//SetUse(&AvHHive::DonateUse);
|
|
}
|
|
}
|
|
|
|
void AvHHive::SetInactive()
|
|
{
|
|
AvHBaseBuildable::SetInactive();
|
|
|
|
// Set this so hives can be drawn translucently at hive locations for aliens
|
|
this->pev->effects &= ~EF_NODRAW;
|
|
|
|
this->ResetReinforcingPlayer(false);
|
|
|
|
this->mActive = false;
|
|
this->mSpawning = false;
|
|
this->mSolid = false;
|
|
this->mTimeLastContributed = -1;
|
|
this->mTechnology = MESSAGE_NULL;
|
|
|
|
this->pev->health = 0;
|
|
this->pev->takedamage = DAMAGE_NO;
|
|
this->pev->dmgtime = gpGlobals->time;
|
|
this->pev->solid = SOLID_NOT;
|
|
this->pev->team = TEAM_IND;
|
|
SetUpgradeMask(&this->pev->iuser4, MASK_BUILDABLE, false);
|
|
|
|
this->pev->rendermode = kRenderTransAlpha;
|
|
this->pev->renderamt = 0;
|
|
|
|
// Stop animation
|
|
this->pev->sequence = 0;
|
|
this->pev->frame = 0;
|
|
this->pev->framerate = 0;
|
|
|
|
// No longer built at all
|
|
this->pev->fuser1 = 0.0f;
|
|
SetThink(NULL);
|
|
SetUse(NULL);
|
|
// Stop looping
|
|
UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kHiveAmbientSound, 1.0f, .5, SND_STOP, 100);
|
|
|
|
ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place???
|
|
|
|
SetTouch(NULL);
|
|
}
|
|
|
|
int AvHHive::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
|
{
|
|
const float kWoundSoundInterval = 1.0f;
|
|
|
|
int theReturnCode = 0;
|
|
|
|
if(this->mActive || this->mSpawning)
|
|
{
|
|
//CBaseEntity* theAttackingEntity = CBaseEntity::Instance(pevAttacker);
|
|
//if(GetGameRules()->CanEntityDoDamageTo(theAttackingEntity, this))
|
|
//{
|
|
theReturnCode = AvHBaseBuildable::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
|
|
|
|
if(theReturnCode > 0)
|
|
{
|
|
const float kDyingThreshold = .4f;
|
|
if(this->pev->health < kDyingThreshold*this->mMaxHitPoints)
|
|
{
|
|
GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_HIVE_DYING, this->entindex());
|
|
}
|
|
else
|
|
{
|
|
GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_UNDER_ATTACK, this->entindex());
|
|
}
|
|
|
|
if((this->mTimeLastWoundSound == -1) || ((this->mTimeLastWoundSound + kWoundSoundInterval) < gpGlobals->time))
|
|
{
|
|
// Pick a random wound sound to play
|
|
//int theIndex = RANDOM_LONG(0, kNumWoundSounds - 1);
|
|
//char* theSoundToPlay = kWoundSoundList[theIndex];
|
|
//EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kHiveDeathSound, 1.0, ATTN_IDLE);
|
|
|
|
//EMIT_SOUND(ENT(this->pev), CHAN_AUTO, "misc/hive_wound1.wav", 1.0, ATTN_IDLE);
|
|
|
|
// Emit hive damaged sound
|
|
gSoundListManager.PlaySoundInList(kHiveWoundSoundList, this, CHAN_BODY);
|
|
|
|
this->mTimeLastWoundSound = gpGlobals->time;
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
|
|
return theReturnCode;
|
|
}
|
|
|
|
|
|
|
|
bool AvHHive::GetCanReinforce() const
|
|
{
|
|
return (this->GetIsBuilt() && this->IsAlive() && !GetGameRules()->GetIsCombatMode());
|
|
}
|
|
|
|
bool AvHHive::GetSpawnLocationForPlayer(CBaseEntity* inPlayer, Vector& outLocation) const
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
CBaseEntity* theSpawnPoint = GetGameRules()->GetRandomHiveSpawnPoint(inPlayer, this->pev->origin, this->GetMaxSpawnDistance());
|
|
if(theSpawnPoint)
|
|
{
|
|
outLocation = theSpawnPoint->pev->origin;
|
|
theSuccess = true;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
bool AvHHive::GetTriggerAlertOnDamage() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
AvHTeamNumber AvHHive::GetReinforceTeamNumber() const
|
|
{
|
|
return AvHBaseBuildable::GetTeamNumber();
|
|
}
|
|
|
|
|
|
//void AvHHive::UpdateReinforcements()
|
|
//{
|
|
// // If hive is active
|
|
// if(this->GetIsActive())
|
|
// {
|
|
// // Test to make sure our reinforcing player is still valid
|
|
// AvHPlayer* theReinforcingPlayer = this->GetReinforcingPlayer();
|
|
// if(theReinforcingPlayer)
|
|
// {
|
|
// AvHPlayMode thePlayMode = theReinforcingPlayer->GetPlayMode();
|
|
// if((theReinforcingPlayer->pev->team != this->pev->team) || (thePlayMode == PLAYMODE_UNDEFINED) || (thePlayMode == PLAYMODE_READYROOM) || (thePlayMode == PLAYMODE_OBSERVER))
|
|
// {
|
|
// this->mReinforcingPlayer = -1;
|
|
// }
|
|
// }
|
|
//
|
|
// // If hive isn't spawning a player in currently
|
|
// if(this->mReinforcingPlayer == -1)
|
|
// {
|
|
// // Find player on this team that's been waiting the longest
|
|
// AvHPlayer* thePlayerToSpawn = NULL;
|
|
//
|
|
// FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*);
|
|
// if(theEntity->GetTeam() == this->GetTeamNumber())
|
|
// {
|
|
// if(theEntity->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT)
|
|
// {
|
|
// if(!thePlayerToSpawn || (theEntity->GetTimeLastPlayModeSet() < thePlayerToSpawn->GetTimeLastPlayModeSet()))
|
|
// {
|
|
// thePlayerToSpawn = theEntity;
|
|
// break;
|
|
// }
|
|
// }
|
|
// }
|
|
// END_FOR_ALL_ENTITIES(kAvHPlayerClassName);
|
|
//
|
|
// // Spawn the one waiting the longest
|
|
// if(thePlayerToSpawn)
|
|
// {
|
|
// // Set the player to be reinforcing
|
|
// this->mReinforcingPlayer = thePlayerToSpawn->entindex();
|
|
//
|
|
// thePlayerToSpawn->SetPlayMode(PLAYMODE_REINFORCING);
|
|
//
|
|
// // Play hive animation, play effect for player?
|
|
// this->pev->sequence = 4;
|
|
// this->pev->frame = 0;
|
|
// ResetSequenceInfo();
|
|
// }
|
|
// }
|
|
// // else hive is spawning a player
|
|
// else
|
|
// {
|
|
// // Is player still valid, or has he left the server/team?
|
|
// AvHPlayer* thePlayer = this->GetReinforcingPlayer();
|
|
// if(thePlayer && (thePlayer->GetTeam() == this->GetTeamNumber()))
|
|
// {
|
|
// if(thePlayer->GetPlayMode() == PLAYMODE_REINFORCING)
|
|
// {
|
|
// // Has enough time passed to bring the player in?
|
|
// const float kHiveRespawnTime = GetGameRules()->GetAlienRespawnTime();
|
|
//
|
|
// if(gpGlobals->time > (thePlayer->GetTimeLastPlayModeSet() + kHiveRespawnTime))
|
|
// {
|
|
// this->ResetReinforcingPlayer(true);
|
|
//
|
|
// // Take away points from the player if possible
|
|
// float theNewPointTotal = max(thePlayer->GetResources() - GetGameRules()->GetGameplay().GetAlienRespawnCost(), 0.0f);
|
|
// thePlayer->SetResources(theNewPointTotal);
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// this->mReinforcingPlayer = -1;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
void AvHHive::CreateUmbra(vec3_t& inOrigin)
|
|
{
|
|
AvHSUCreateUmbraCloud(inOrigin, AvHTeamNumber(this->pev->team), this);
|
|
|
|
// Don't create another for a bit
|
|
this->mTimeOfNextUmbra = -1;
|
|
}
|
|
|
|
void AvHHive::CueRespawnEffect(AvHPlayer* inPlayer)
|
|
{
|
|
// Play hive animation, play effect for player?
|
|
this->pev->sequence = 4;
|
|
this->pev->frame = 0;
|
|
ResetSequenceInfo();
|
|
|
|
// Create umbra around spawning players, but not until after late-join period (to avoid a ton of umbras all at once)
|
|
if(!GetGameRules()->GetArePlayersAllowedToJoinImmediately())
|
|
{
|
|
//this->CreateUmbra(inPlayer->pev->origin);
|
|
}
|
|
}
|
|
|
|
float AvHHive::GetReinforceTime() const
|
|
{
|
|
const float kMaxRespawnTime = BALANCE_VAR(kAlienRespawnTime);
|
|
|
|
float theRespawnTime = (kMaxRespawnTime - kMaxRespawnTime*this->mEnergy);
|
|
|
|
// puzl 0000854
|
|
// Decrease respawn wait time for aliens (NS: Classic)
|
|
// With one hive, for every player above six on the alien team,
|
|
// reduce the per-player respawn wait time by two-thirds of a second.
|
|
// With two hives, make the reduction one-third of a second.
|
|
// With three (or more, in the case of weird custom maps) hives, do not apply it.
|
|
|
|
AvHTeam* theTeam = GetGameRules()->GetTeam(GetTeamNumber());
|
|
ASSERT(theTeam);
|
|
|
|
int thePlayerModifier = theTeam->GetPlayerCount() - BALANCE_VAR(kAlienRespawnPlayerModifier);
|
|
int theHiveCount = GetGameRules()->GetNumActiveHives(GetTeamNumber());
|
|
|
|
if ( thePlayerModifier > 0 && theHiveCount < 3 )
|
|
{
|
|
float theTimeModifier = BALANCE_VAR(kAlienRespawnTimeModifier);
|
|
|
|
// For one hive double the modifier
|
|
if ( theHiveCount == 1 )
|
|
{
|
|
theTimeModifier *= 2.0f;
|
|
}
|
|
|
|
theRespawnTime -= theTimeModifier * (float)thePlayerModifier;
|
|
}
|
|
|
|
theRespawnTime = min(max(theRespawnTime, 0.0f), kMaxRespawnTime);
|
|
|
|
return theRespawnTime;
|
|
}
|
|
|
|
bool AvHHive::Energize(float inEnergyAmount)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
// Only energize when a player is in the cue
|
|
if(this->GetIsBuilt() && this->GetReinforcingPlayer())
|
|
{
|
|
if(this->mEnergy < 1.0f)
|
|
{
|
|
this->mEnergy += inEnergyAmount;
|
|
this->mEnergy = min(max(0.0f, this->mEnergy), 1.0f);
|
|
theSuccess = true;
|
|
}
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
void AvHHive::HiveTouch(CBaseEntity* inOther)
|
|
{
|
|
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
|
|
if(thePlayer && (thePlayer->pev->team != this->pev->team))
|
|
{
|
|
if(this->GetIsActive())
|
|
{
|
|
// Play scared animation, it recoils from human touch
|
|
this->PlayAnimationAtIndex(kScaredAnimationIndex, true);
|
|
}
|
|
}
|
|
|
|
AvHBaseBuildable::BuildableTouch(inOther);
|
|
}
|
|
|
|
void AvHHive::TeleportUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue)
|
|
{
|
|
if ( this->GetIsSpawning() )
|
|
return;
|
|
|
|
const float kHiveScanInterval = 1.0f;
|
|
|
|
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inActivator);
|
|
|
|
vector<int> theHives;
|
|
|
|
if(thePlayer && (thePlayer->pev->team == this->pev->team) && (thePlayer->GetUser3() != AVH_USER3_ALIEN_EMBRYO))
|
|
{
|
|
if((this->mLastTimeScannedHives == -1) || (gpGlobals->time > (this->mLastTimeScannedHives + kHiveScanInterval)))
|
|
{
|
|
this->mTeleportHiveIndex = -1;
|
|
float theFarthestDistance = 0.0f; //sqrt((kMaxMapDimension*2)*(kMaxMapDimension*2));
|
|
// Loop through the hives for this team, look for the farthest one (hives under attack take precedence)
|
|
FOR_ALL_ENTITIES(kesTeamHive, AvHHive*)
|
|
if((theEntity->pev->team == this->pev->team) && theEntity != this )
|
|
{
|
|
bool theHiveIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntity->entindex());
|
|
// allow teleport to any built hive, or unbuilt hives under attack.
|
|
if(!theEntity->GetIsSpawning() || ( theEntity->GetIsSpawning() && theHiveIsUnderAttack ) )
|
|
{
|
|
theHives.push_back(theEntity->entindex());
|
|
}
|
|
}
|
|
END_FOR_ALL_ENTITIES(kesTeamHive)
|
|
|
|
this->mLastTimeScannedHives = gpGlobals->time;
|
|
}
|
|
|
|
if ( theHives.size() > 0 ) {
|
|
int myIndex=this->entindex();
|
|
for ( int i=0; i < theHives.size(); i++ ) {
|
|
int hiveIndex=theHives[i];
|
|
if ( hiveIndex > myIndex ) {
|
|
this->mTeleportHiveIndex=hiveIndex;
|
|
break;
|
|
}
|
|
}
|
|
if ( this->mTeleportHiveIndex == -1 ) {
|
|
this->mTeleportHiveIndex=theHives[0];
|
|
}
|
|
}
|
|
|
|
// If we have a valid hive index, jump the player to it
|
|
if(this->mTeleportHiveIndex != -1)
|
|
{
|
|
// Play sound at this entity
|
|
EMIT_SOUND(this->edict(), CHAN_AUTO, kAlienSightOnSound, 1.0f, ATTN_NORM);
|
|
|
|
// Move him to it!
|
|
AvHHive* theHive = NULL;
|
|
AvHSUGetEntityFromIndex(this->mTeleportHiveIndex, theHive);
|
|
if(theHive)
|
|
{
|
|
CBaseEntity* theSpawnEntity = GetGameRules()->GetRandomHiveSpawnPoint(thePlayer, theHive->pev->origin, theHive->GetMaxSpawnDistance());
|
|
if(theSpawnEntity)
|
|
{
|
|
Vector theMinSize;
|
|
Vector theMaxSize;
|
|
thePlayer->GetSize(theMinSize, theMaxSize);
|
|
|
|
int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(thePlayer->pev->iuser3));
|
|
Vector theOriginToSpawn = theSpawnEntity->pev->origin;
|
|
theOriginToSpawn.z += theOffset;
|
|
|
|
if(AvHSUGetIsEnoughRoomForHull(theOriginToSpawn, AvHMUGetHull(false, thePlayer->pev->iuser3), thePlayer->edict()))
|
|
{
|
|
thePlayer->SetPosition(theOriginToSpawn);
|
|
thePlayer->pev->velocity = Vector(0, 0, 0);
|
|
|
|
// Play teleport sound before and after
|
|
EMIT_SOUND(inActivator->edict(), CHAN_AUTO, kAlienSightOffSound, 1.0f, ATTN_NORM);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|