//======== (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: AvHAlienEquipment.cpp $ // $Date: 2002/11/22 21:28:15 $ // //------------------------------------------------------------------------------- // $Log: AvHAlienEquipment.cpp,v $ // Revision 1.17 2002/11/22 21:28:15 Flayra // - mp_consistency changes // // Revision 1.16 2002/11/15 04:47:30 Flayra // - Def chambers only up to three targets per tick // // Revision 1.15 2002/10/16 00:46:51 Flayra // - Movement chambers not useable until 3 seconds after built // - Moved and removed some building sounds, added one for chamber death // - Don't allow using movement chamber while gestating // // Revision 1.14 2002/10/03 18:31:29 Flayra // - Movement chambers now teleport you to the farthest hive // // Revision 1.13 2002/09/25 20:42:14 Flayra // - Defensive chamber performance/gameplay tweak // // Revision 1.12 2002/09/23 22:07:11 Flayra // - Removed alien building fading // - Removed bug where builders got rewarded twice for building a structure // // Revision 1.11 2002/09/09 19:47:47 Flayra // - Alien buildings now animate // - Sensory chamber no longer hurts players when touched // // Revision 1.10 2002/08/31 18:01:00 Flayra // - Work at VALVe // // Revision 1.9 2002/08/16 02:32:09 Flayra // - Added damage types // - Swapped umbra and bile bomb // //=============================================================================== #include "AvHAlienEquipment.h" #include "AvHAlienEquipmentConstants.h" #include "AvHServerUtil.h" #include "AvHGamerules.h" #include "AvHTeam.h" #include "AvHServerUtil.h" #include "AvHSharedUtil.h" #include "util/MathUtil.h" #include "AvHAlienAbilityConstants.h" #include "AvHAlienEquipmentConstants.h" #include "AvHAlienWeaponConstants.h" #include "AvHMovementUtil.h" LINK_ENTITY_TO_CLASS(kwAlienResourceTower, AvHAlienResourceTower); AvHAlienResourceTower::AvHAlienResourceTower() : AvHResourceTower(TECH_ALIEN_RESOURCE_NODE, ALIEN_BUILD_RESOURCES, kwsAlienResourceTower) { this->SetAverageUseSoundLength(kAverageAlienUseSoundLength); } int AvHAlienResourceTower::GetSequenceForBoundingBox() const { return 2; } int AvHAlienResourceTower::GetActiveAnimation() const { int theAnim = AvHBaseBuildable::GetActiveAnimation(); int theRandomNumber = RANDOM_LONG(0, 10); if(theRandomNumber == 10) { theAnim = 3; } else if(theRandomNumber > 7) { theAnim = 2; } return theAnim; } char* AvHAlienResourceTower::GetDeploySound() const { return kChamberDeploySound; } bool AvHAlienResourceTower::GetIsOrganic() const { return true; } int AvHAlienResourceTower::GetPointValue() const { return 3; } char* AvHAlienResourceTower::GetModelName() const { return kAlienResourceTowerModel; } char* AvHAlienResourceTower::GetActiveSoundList() const { char* theActiveSoundList = NULL; // Don't play active sounds until kAlienResourceTowerSoundDelayTime seconds have passed, to prevent marines from knowing where aliens start int theTimeToWaitBeforeSounds = BALANCE_VAR(kAlienResourceTowerSoundDelayTime); int theGameTime = GetGameRules()->GetGameTime(); if((theGameTime > 0) && (theGameTime >= theTimeToWaitBeforeSounds)) { theActiveSoundList = kAlienResourceTowerSoundList; } else { int a = 0; } return theActiveSoundList; } void AvHAlienResourceTower::Killed(entvars_t* pevAttacker, int iGib) { EMIT_SOUND(this->edict(), CHAN_AUTO, kChamberDieSound, 1.0f, ATTN_NORM); AvHResourceTower::Killed(pevAttacker, iGib); } void AvHAlienResourceTower::Precache() { AvHResourceTower::Precache(); PRECACHE_UNMODIFIED_SOUND(kChamberDieSound); } void AvHAlienResourceTower::AlienResourceThink() { if(!this->GetIsBuilt()) { this->UpdateAutoBuild(kAlienBuildingThinkInterval*kAutoBuildScalar); this->pev->nextthink = gpGlobals->time + kAlienBuildingThinkInterval; } } void AvHAlienResourceTower::Materialize() { AvHResourceTower::Materialize(); this->pev->iuser3 = AVH_USER3_ALIENRESTOWER; SetThink(&AvHAlienResourceTower::AlienResourceThink); this->pev->nextthink = gpGlobals->time + kAlienBuildingThinkInterval; } void AvHAlienResourceTower::Spawn() { AvHResourceTower::Spawn(); } AvHAlienUpgradeBuilding::AvHAlienUpgradeBuilding(AvHMessageID inMessageID, AvHTechID inTechID, char* inClassName, AvHUser3 inUser3) : AvHBaseBuildable(inTechID, inMessageID, inClassName, inUser3) { this->SetAverageUseSoundLength(kAverageAlienUseSoundLength); } bool AvHAlienUpgradeBuilding::GetIsOrganic() const { return true; } int AvHAlienUpgradeBuilding::GetPointValue() const { return BALANCE_VAR(kScoringAlienUpgradeChamberValue); } void AvHAlienUpgradeBuilding::Killed(entvars_t* pevAttacker, int iGib) { //AvHSUExplodeEntity(this, matFlesh); EMIT_SOUND(this->edict(), CHAN_AUTO, kChamberDieSound, 1.0f, ATTN_NORM); if(this->GetIsBuilt()) { // Get team AvHTeamNumber theTeamNumber = AvHTeamNumber(this->pev->team); AvHTeam* theTeam = GetGameRules()->GetTeam(theTeamNumber); ASSERT(theTeam); theTeam->RemoveAlienUpgradeCategory(this->GetUpgradeCategory()); } AvHBaseBuildable::Killed(pevAttacker, iGib); } void AvHAlienUpgradeBuilding::AlienBuildingThink() { if(!this->GetIsBuilt()) { this->UpdateAutoBuild(kAlienBuildingThinkInterval*kAutoBuildScalar); this->pev->nextthink = gpGlobals->time + kAlienBuildingThinkInterval; } else { SetThink(&AvHBaseBuildable::AnimateThink); this->pev->nextthink = gpGlobals->time + .1f; } } void AvHAlienUpgradeBuilding::Precache() { AvHBaseBuildable::Precache(); PRECACHE_UNMODIFIED_SOUND(kChamberDieSound); } void AvHAlienUpgradeBuilding::Materialize() { //Vector theMinSize, theMaxSize; //if(AvHSHUGetSizeForTech(this->GetMessageID(), theMinSize, theMaxSize)) //{ // UTIL_SetSize(this->pev, theMinSize, theMaxSize); //} AvHBaseBuildable::Materialize(); SetThink(&AvHAlienUpgradeBuilding::AlienBuildingThink); this->pev->nextthink = gpGlobals->time + kAlienBuildingThinkInterval; this->pev->rendermode = kRenderNormal; this->pev->renderamt = 0; // SET_MODEL(ENT(this->pev), this->GetModelName()); // // this->pev->solid = SOLID_BBOX; // this->pev->movetype = MOVETYPE_TOSS; // // //UTIL_SetSize(pev, kResourceMinSize, kResourceMaxSize); // // this->pev->classname = MAKE_STRING(this->GetClassName()); // // this->pev->takedamage = DAMAGE_YES; // SetBits(this->pev->flags, FL_MONSTER); // // this->pev->iuser3 = AVH_USER3_BUILDABLE; // this->pev->fuser1 = 0.0f; // //this->pev->iuser4 = AVH_USER4_RESTOWER; // // // Start animating // this->pev->sequence = 0; // this->pev->frame = 0; // ResetSequenceInfo(); // AvHSUSetCollisionBoxFromSequence(this->pev); } void AvHAlienUpgradeBuilding::Spawn() { AvHBaseBuildable::Spawn(); } void AvHAlienUpgradeBuilding::SetHasBeenBuilt() { AvHBaseBuildable::SetHasBeenBuilt(); AvHAlienUpgradeCategory theCategory = this->GetUpgradeCategory(); // Find the team associated with the building AvHTeamNumber theTeamNumber = AvHTeamNumber(this->pev->team); AvHTeam* theTeam = GetGameRules()->GetTeam(theTeamNumber); ASSERT(theTeam); theTeam->AddTeamUpgrade(theCategory); //this->SetHasBeenBuilt(); //this->pev->rendermode = kRenderNormal; //this->pev->renderamt = 0; } //LINK_ENTITY_TO_CLASS(kwOffenseChamber, AvHOffenseChamber); //AvHOffenseChamber::AvHOffenseChamber() : AvHAlienUpgradeBuilding(ALIEN_BUILD_OFFENSE_CHAMBER, kwsOffenseChamber) //{ //} // //char* AvHOffenseChamber::GetDeploySound() const //{ // return kChamberDeploySound; //} // //char* AvHOffenseChamber::GetModelName() const //{ // return kOffenseChamberModel; //} // // // //AvHAlienUpgradeCategory AvHOffenseChamber::GetUpgradeCategory() const //{ // return ALIEN_UPGRADE_CATEGORY_OFFENSE; //} //LINK_ENTITY_TO_CLASS(kwOffenseChamber, AvHOffenseChamber); //AvHOffenseChamber::AvHOffenseChamber() : AvHTurret(TECH_NULL, ALIEN_BUILD_OFFENSE_CHAMBER, kwsOffenseChamber, AVH_USER3_NONE) //{ //} // //char* AvHOffenseChamber::GetActiveSound() const //{ // return NULL; //} // //char* AvHOffenseChamber::GetAlertSound() const //{ // return NULL; //} // //char* AvHOffenseChamber::GetPingSound() const //{ // return NULL; //} //int AvHOffenseChamber::GetActiveAnimation() const; //int AvHOffenseChamber::GetDeployAnimation() const; //int AvHOffenseChamber::GetIdle1Animation() const; //int AvHOffenseChamber::GetIdle2Animation() const; //int AvHOffenseChamber::GetKilledAnimation() const; //int AvHOffenseChamber::GetSpawnAnimation() const; //int AvHOffenseChamber::GetTakeDamageAnimation() const; //bool AvHOffenseChamber::GetIsValidTarget(CBaseEntity* inEntity) const; //int AvHOffenseChamber::IRelationship(CBaseEntity* inTarget); //int AvHOffenseChamber::GetRange() const //{ // return 100; //} // //void AvHOffenseChamber::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) //{ //} // //void AvHOffenseChamber::Spawn() //{ // AvHTurret::Spawn(); // // // TODO: Set any other variables? //} LINK_ENTITY_TO_CLASS(kwDefenseChamber, AvHDefenseChamber); AvHDefenseChamber::AvHDefenseChamber() : AvHAlienUpgradeBuilding(ALIEN_BUILD_DEFENSE_CHAMBER, TECH_DEFENSE_CHAMBER, kwsDefenseChamber, AVH_USER3_DEFENSE_CHAMBER) { } char* AvHDefenseChamber::GetDeploySound() const { return kChamberDeploySound; } char* AvHDefenseChamber::GetModelName() const { return kDefenseChamberModel; } void AvHDefenseChamber::SetHasBeenBuilt() { AvHAlienUpgradeBuilding::SetHasBeenBuilt(); SetThink(&AvHDefenseChamber::RegenAliensThink); this->pev->nextthink = gpGlobals->time + BALANCE_VAR(kDefenseChamberThinkInterval); } void AvHDefenseChamber::RegenAliensThink() { // Loop through all players CBaseEntity* theBaseEntity = NULL; int theNumEntsHealed = 0; while(((theBaseEntity = UTIL_FindEntityInSphere(theBaseEntity, this->pev->origin, BALANCE_VAR(kDefensiveChamberHealRange))) != NULL) && (theNumEntsHealed < BALANCE_VAR(kAlienChamberMaxPlayers))) { if(theBaseEntity->pev->team == this->pev->team) { AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); float thePercent=BALANCE_VAR(kDefensiveChamberRegenPercent)/100.0f; float amount=BALANCE_VAR(kDefensiveChamberRegenAmount) + (theBaseEntity->pev->max_health*thePercent); if(thePlayer && thePlayer->IsAlive()) { if(thePlayer->Heal(amount, true, true)) { theNumEntsHealed++; } } else if(theBuildable && theBuildable->GetIsBuilt() && (theBuildable != this)) { if(theBuildable->Regenerate(amount, true, true)) { theNumEntsHealed++; } } } } // Set next think this->pev->nextthink = gpGlobals->time + BALANCE_VAR(kDefenseChamberThinkInterval); // Play a random idle animation int theIdle = this->GetIdle1Animation(); if(RANDOM_LONG(0, 1)) { theIdle = this->GetIdle2Animation(); } this->PlayAnimationAtIndex(theIdle); } AvHAlienUpgradeCategory AvHDefenseChamber::GetUpgradeCategory() const { return ALIEN_UPGRADE_CATEGORY_DEFENSE; } LINK_ENTITY_TO_CLASS(kwSensoryChamber, AvHSensoryChamber); AvHSensoryChamber::AvHSensoryChamber() : AvHAlienUpgradeBuilding(ALIEN_BUILD_SENSORY_CHAMBER, TECH_SENSORY_CHAMBER, kwsSensoryChamber, AVH_USER3_SENSORY_CHAMBER) { } char* AvHSensoryChamber::GetDeploySound() const { return kChamberDeploySound; } bool AvHSensoryChamber::GetIsEntityInSight(CBaseEntity* inEntity) { bool theEntityIsInSight = false; // Do a traceline from sensory chamber to entity TraceResult tr; UTIL_TraceLine(this->pev->origin, inEntity->pev->origin, ignore_monsters, dont_ignore_glass, this->edict(), &tr); if(tr.flFraction == 1.0f) { theEntityIsInSight = true; } return theEntityIsInSight; } char* AvHSensoryChamber::GetModelName() const { return kSensoryChamberModel; } AvHAlienUpgradeCategory AvHSensoryChamber::GetUpgradeCategory() const { return ALIEN_UPGRADE_CATEGORY_SENSORY; } void AvHSensoryChamber::Precache() { AvHAlienUpgradeBuilding::Precache(); //PRECACHE_UNMODIFIED_SOUND(kParasiteHitSound); } void AvHSensoryChamber::SetHasBeenBuilt() { AvHAlienUpgradeBuilding::SetHasBeenBuilt(); } LINK_ENTITY_TO_CLASS(kwMovementChamber, AvHMovementChamber); AvHMovementChamber::AvHMovementChamber() : AvHAlienUpgradeBuilding(ALIEN_BUILD_MOVEMENT_CHAMBER, TECH_MOVEMENT_CHAMBER, kwsMovementChamber, AVH_USER3_MOVEMENT_CHAMBER) { this->mLastTimeScannedHives = -1; this->mTeleportHiveIndex = -1; } char* AvHMovementChamber::GetDeploySound() const { return kChamberDeploySound; } char* AvHMovementChamber::GetModelName() const { return kMovementChamberModel; } void AvHMovementChamber::ResetEntity() { AvHAlienUpgradeBuilding::ResetEntity(); this->mLastTimeScannedHives = -1; this->mTeleportHiveIndex = -1; } AvHAlienUpgradeCategory AvHMovementChamber::GetUpgradeCategory() const { return ALIEN_UPGRADE_CATEGORY_MOVEMENT; } void AvHMovementChamber::Precache() { AvHAlienUpgradeBuilding::Precache(); PRECACHE_UNMODIFIED_SOUND(kAlienSightOffSound); PRECACHE_UNMODIFIED_SOUND(kAlienSightOnSound); PRECACHE_UNMODIFIED_SOUND(kAlienEnergySound); } void AvHMovementChamber::SetHasBeenBuilt() { AvHAlienUpgradeBuilding::SetHasBeenBuilt(); // TODO: Include a "warm-up" time so movement chambers don't teleport the builder immediately //SetThink(&AvHMovementChamber::TeleportUseThink); SetThink(&AvHMovementChamber::EnergyAliensThink); this->pev->nextthink = gpGlobals->time + BALANCE_VAR(kMovementChamberThinkInterval); } void AvHMovementChamber::EnergyAliensThink() { // Don't teleport until it's "warmed up" SetUse(&AvHMovementChamber::TeleportUse); // Loop through all players CBaseEntity* theBaseEntity = NULL; int theNumEntsProcessed = 0; while(((theBaseEntity = UTIL_FindEntityInSphere(theBaseEntity, this->pev->origin, BALANCE_VAR(kMovementChamberEnergyRange))) != NULL) && (theNumEntsProcessed < BALANCE_VAR(kAlienChamberMaxPlayers))) { if(theBaseEntity->pev->team == this->pev->team) { float theEnergizeAmount = BALANCE_VAR(kMovementChamberEnergyAmount); AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); if(thePlayer && thePlayer->IsAlive()) { if(thePlayer->Energize(theEnergizeAmount)) { theNumEntsProcessed++; } } // Energize alien buildables // else if(theBuildable) // { // if(theBuildable->Energize(theEnergizeAmount)) // { // theNumEntsProcessed++; // } // } } } // Play an animation (spin the arms if energizing) int theIdle = this->GetIdle2Animation(); // Play sound if(theNumEntsProcessed > 0) { EMIT_SOUND(this->edict(), CHAN_AUTO, kAlienEnergySound, 1.0f, ATTN_NORM); theIdle = this->GetIdle1Animation(); } this->PlayAnimationAtIndex(theIdle); // Set next think this->pev->nextthink = gpGlobals->time + BALANCE_VAR(kMovementChamberThinkInterval); } void AvHMovementChamber::TeleportUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue) { const float kHiveScanInterval = 1.0f; AvHPlayer* thePlayer = dynamic_cast(inActivator); 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)); bool theIsDone = false; // 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) && !theIsDone) { bool theHiveIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntity->entindex()); if(theEntity->GetIsActive() || theHiveIsUnderAttack || theEntity->GetEmergencyUse() ) { float theCurrentDistance = VectorDistance(theEntity->pev->origin, inActivator->pev->origin); bool theHiveIsFarther = (theCurrentDistance > theFarthestDistance); // Undefined which attacked hive is chosen if multiples are under attack if((this->mTeleportHiveIndex == -1) || theHiveIsFarther || theHiveIsUnderAttack) { this->mTeleportHiveIndex = theEntity->entindex(); theFarthestDistance = theCurrentDistance; // If there's a hive under attack, we're done if(theHiveIsUnderAttack) { theIsDone = true; } } } } END_FOR_ALL_ENTITIES(kesTeamHive) this->mLastTimeScannedHives = gpGlobals->time; } // 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); thePlayer->TriggerUncloak(); // Play teleport sound before and after EMIT_SOUND(inActivator->edict(), CHAN_AUTO, kAlienSightOffSound, 1.0f, ATTN_NORM); } } } } } } void AvHMovementChamber::TeleportUseThink() { // Set our new use, don't set next think SetUse(&AvHMovementChamber::TeleportUse); // Start animating SetThink(&AvHBaseBuildable::AnimateThink); this->pev->nextthink = gpGlobals->time + .1f; }