//======== (C) Copyright 2001 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: AvHTeam.cpp $ // $Date: 2002/11/26 20:35:00 $ // //------------------------------------------------------------------------------- // $Log: AvHTeam.cpp,v $ // Revision 1.55 2002/11/26 20:35:00 Flayra // - Hurt fix // // Revision 1.54 2002/11/26 15:58:31 Flayra // - When the alien team no longer has any growing or active hives, all aliens slowly take damage. This is done to speed up the end game and prevent hiding llamas. // // Revision 1.53 2002/11/22 23:26:59 Flayra // - Don't play kill lingering aliens with cheats on (when testing) // // Revision 1.52 2002/11/22 21:15:37 Flayra // - Remove owner of entities when player leaves the team // - Do damage to aliens without hives // - Fixed bug where a vote could affect a commander that logged in after a vote had been started. // // Revision 1.51 2002/11/15 23:31:26 Flayra // - Added "ready" verification for tourny mode // // Revision 1.50 2002/11/12 02:29:33 Flayra // - Added better standardized logging // // Revision 1.49 2002/11/06 03:08:56 Flayra // - Undid +1 for resources, it's too drastic // // Revision 1.48 2002/11/06 02:23:49 Flayra // - Removed faulty minimum trickle that was giving 1 extra resource point to both teams // // Revision 1.47 2002/11/06 01:39:04 Flayra // - Show true resources going to team // // Revision 1.46 2002/11/03 04:52:55 Flayra // - Hard-coded resources // // Revision 1.45 2002/10/24 21:43:29 Flayra // - Alien easter eggs // - Voting fix // // Revision 1.44 2002/10/19 21:28:38 Flayra // - Debugging info for linux // // Revision 1.43 2002/10/19 21:09:56 Flayra // - Debugging info for linux // // Revision 1.42 2002/10/19 20:56:06 Flayra // - Debugging info for linux // // Revision 1.41 2002/10/19 20:19:26 Flayra // - Debugging info for linux // // Revision 1.40 2002/10/19 20:04:14 Flayra // - Debugging info for linux // // Revision 1.39 2002/10/18 22:23:04 Flayra // - Added beginnings of alien easter eggs // - Added "we need builders" alert // // Revision 1.38 2002/10/04 18:03:23 Flayra // - Fixed bug where extra resources were sometimes being wasted on the alien team // // Revision 1.37 2002/10/03 19:09:55 Flayra // - New resource model // - Orders refactoring // - Tech tree changes // - Aliens always get initial points when joining // - Play gamestart sound // - New trait available trigger // - Moved voting stuff to server variables // - Slowed down hints // // Revision 1.36 2002/09/25 20:51:31 Flayra // - Play commander-idle sounds less often // // Revision 1.35 2002/09/23 22:35:43 Flayra // - Removed hive donation and put in new system for builders // - Updated tech tree (jetpacks, heavy armor, moved phase) // - Resource towers set as built on game start // - Added tons of commander hints (low resource alerts, tell commander about pressing jump key, commander ejected, select troops and give orders, don't just sit there, etc.) // // Revision 1.34 2002/09/09 20:08:26 Flayra // - Added commander voting // - Added hive info // - Changed how commander scoring works // // Revision 1.33 2002/08/31 18:01:03 Flayra // - Work at VALVe // // Revision 1.32 2002/08/02 21:45:20 Flayra // - Reworked alerts. // // Revision 1.31 2002/07/28 19:21:27 Flayra // - Balance changes after/during RC4a // // Revision 1.30 2002/07/26 23:08:25 Flayra // - Added numerical feedback // // Revision 1.29 2002/07/25 16:58:00 flayra // - Linux changes // // Revision 1.28 2002/07/24 18:55:53 Flayra // - Linux case sensitivity stuff // // Revision 1.27 2002/07/23 17:32:23 Flayra // - Resource model overhaul (closed system, random initial points), tech tree changes (new marine upgrades), free resource tower at nearest func_resource // // Revision 1.26 2002/07/10 14:45:26 Flayra // - Fixed victory condition bug (bug #171) // // Revision 1.25 2002/07/08 17:19:42 Flayra // - Added handicapping, map validity checking, reinforcements happen independently of teams now // // Revision 1.24 2002/07/01 21:24:07 Flayra // - Brought siege back, removed alpha male resource model // // Revision 1.23 2002/06/25 18:21:33 Flayra // - New enabled/disabled system for alien weapons, better victory detection, updated tech tree (removed old junk, added armory upgrade), fixed bug where a commander that leaves the game hogged the station, update resources less often (optimization) // // Revision 1.22 2002/06/03 17:00:33 Flayra // - Removed mines and siege temporarily while being worked on, removed old code, removed redundant hive class name // // Revision 1.21 2002/05/28 18:09:51 Flayra // - Track number of webs for team, fix for premature victory condition (again), fixed bug where alien upgrades weren't being cleared between levels, causing eventual overflows for alien players after many games, recycling support, spawn in command center when game starts // // Revision 1.20 2002/05/23 02:32:57 Flayra // - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. // //=============================================================================== #include "dlls/extdll.h" #include "dlls/util.h" #include "dlls/cbase.h" #include "mod/AvHTeam.h" #include "mod/AvHPlayer.h" #include "mod/AvHGamerules.h" #include "mod/AvHServerVariables.h" #include "mod/AvHServerUtil.h" #include "mod/AvHMarineEquipment.h" #include "mod/AvHAlienEquipmentConstants.h" #include "mod/AvHMarineEquipmentConstants.h" #include "game_shared/teamconst.h" #include "mod/AvHBuildable.h" #include "mod/AvHServerUtil.h" #include "mod/AvHSharedUtil.h" #include "mod/AvHTitles.h" #include "mod/AvHPlayerUpgrade.h" #include "util/MathUtil.h" #include "mod/AvHNetworkMessages.h" #include "mod/AvHNexusServer.h" extern int gPhaseInEventID; extern cvar_t avh_votecasttime; extern cvar_t avh_votedowntime; extern cvar_t avh_votepercentneeded; extern cvar_t avh_minvotesneeded; extern cvar_t avh_initialalienpoints; extern cvar_t avh_eastereggchance; AvHTeam::AvHTeam(AvHTeamNumber inTeamNumber) { this->Init(); this->mTeamNumber = inTeamNumber; this->mResearchManager.SetTeamNumber(inTeamNumber); } void AvHTeam::Init() { this->mTeamType = AVH_CLASS_TYPE_UNDEFINED; this->mTeamName = kInvalidTeamName; this->mTeamNumber = TEAM_IND; this->mCommander = -1; this->mCommanderWhenVoteStarted = -1; this->mPlayerList.clear(); this->mResourceTowerList.clear(); this->mLastResourceUpdateTime = -1; this->mLastCommandScoreUpdateTime = -1; this->mLastServerPlayerDataUpdateTime = -1; this->mLastPlayerUpdateTime = -1; this->mLastInjectionTime = 0; this->mLastHiveSpawnTime = 0; // Alerts this->mAlertList.clear(); this->mTimeOfLastTeamHint = -1; this->mTimeLastHintUpdate = -1; this->mTeamResources = 0; this->mHandicap = 1.0f; this->mResearchManager.Reset(); this->mAlienUpgrades.clear(); this->mTeamWideUpgrades = 0; this->mNumWebStrands = 0; this->mIsReady = false; this->mTotalResourcesGathered = 0; this->mClientTotalResourcesGathered = 0; this->mMenuTechSlots = 0; this->mTimeReinforcementWaveComplete = -1; } bool AvHTeam::AddPlayer(int inPlayerIndex) { bool theSuccess = false; if(!this->GetIsPlayerOnTeam(inPlayerIndex)) { // Add player this->mPlayerList.push_back(inPlayerIndex); theSuccess = true; } return theSuccess; } bool AvHTeam::AddResourceTower(int inResourceTowerIndex) { bool theSuccess = false; EntityListType::const_iterator theIter = std::find(this->mResourceTowerList.begin(), this->mResourceTowerList .end(), inResourceTowerIndex); if(theIter == this->mResourceTowerList.end()) { this->mResourceTowerList.push_back(inResourceTowerIndex); theSuccess = true; } return theSuccess; } bool AvHTeam::RemoveResourceTower(int inResourceTowerIndex) { bool theSuccess = false; EntityListType::iterator theIter = std::find(this->mResourceTowerList.begin(), this->mResourceTowerList .end(), inResourceTowerIndex); if(theIter != this->mResourceTowerList.end()) { this->mResourceTowerList.erase(theIter); theSuccess = true; } return theSuccess; } bool AvHTeam::GetIsReady() const { return this->mIsReady; } int AvHTeam::GetMenuTechSlots() const { return this->mMenuTechSlots; } void AvHTeam::SetIsReady(bool bIsReady) { this->mIsReady = bIsReady; } int AvHTeam::GetNumBuiltCommandStations() const { return this->mNumBuiltCommandStations; } int AvHTeam::GetNumActiveHives() const { return this->mNumActiveHives; } float AvHTeam::GetMaxResources(AvHUser3 inUser3) const { float theMaxResources = -1; if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { //if(inUser3 == AVH_USER3_ALIEN_PLAYER2) //{ // Needed so builders can create hives (also means that having more builders diverts resources from combat/offense) theMaxResources = kMaxAlienResources; //} //else //{ // int theNumActiveHives = max(this->GetNumActiveHives(), 1); // theMaxResources = (theNumActiveHives/(float)kMaxAlienHives)*kMaxAlienResources; //} } return theMaxResources; } bool AvHTeam::GetTeamHasAbilityToRespawn() const { bool theAbilityToRespawn = false; // If game hasn't started if(!GetGameRules()->GetGameStarted()) { // return true theAbilityToRespawn = true; } // else if we're a marine team else if(this->GetTeamType() == AVH_CLASS_TYPE_MARINE) { // Do we have any built infantry portals? FOR_ALL_ENTITIES(kwsInfantryPortal, AvHInfantryPortal*) if(theEntity->GetIsBuilt()) { AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); if(theTeamNumber == this->mTeamNumber) { theAbilityToRespawn = true; break; } } END_FOR_ALL_ENTITIES(kwsInfantryPortal); } // else if we're an alien team else if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) { // Do we have any active hives? FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) if(theEntity->GetIsActive()) { AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); if(theTeamNumber == this->mTeamNumber) { theAbilityToRespawn = true; break; } } END_FOR_ALL_ENTITIES(kesTeamHive); } return theAbilityToRespawn; } bool AvHTeam::GetHasAtLeastOneActivePlayer(bool* outHasAtLeastOnePlayerOnTeam) const { bool theHasAtLeastOneActivePlayer = false; // Loop through all players on team for(EntityListType::const_iterator theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) { const AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); ASSERT(thePlayer); if(outHasAtLeastOnePlayerOnTeam) { *outHasAtLeastOnePlayerOnTeam = true; } if((thePlayer->IsAlive() && !thePlayer->GetIsBeingDigested()) || (thePlayer->GetUser3() == AVH_USER3_COMMANDER_PLAYER)) { theHasAtLeastOneActivePlayer = true; break; } } return theHasAtLeastOneActivePlayer; } bool AvHTeam::GetHasTeamLost() const { bool theTeamHasLost = false; bool theHasAtLeastOnePlayerOnTeam = false; bool theHasAtLeastOneActivePlayer = this->GetHasAtLeastOneActivePlayer(&theHasAtLeastOnePlayerOnTeam); if(GetGameRules()->GetIsCombatMode()) { if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) { if((this->GetNumActiveHives() == 0) || !theHasAtLeastOnePlayerOnTeam) { theTeamHasLost = true; } } else if(this->GetTeamType() == AVH_CLASS_TYPE_MARINE) { if((this->GetNumBuiltCommandStations() == 0) || !theHasAtLeastOnePlayerOnTeam) { theTeamHasLost = true; } } } else { // We need at least one alive player, OR, at the ability to respawn bool theHasAbilityToRespawn = this->GetTeamHasAbilityToRespawn(); if((!theHasAtLeastOneActivePlayer && !theHasAbilityToRespawn) || !theHasAtLeastOnePlayerOnTeam) { theTeamHasLost = true; } } if(theTeamHasLost) { //UTIL_LogPrintf("Team %d has lost. theAtLeastOneAlivePlayer: %d, theAbilityToRespawn: %d, theHasAtLeastOnePlayerOnTeam: %d\n", this->mTeamNumber, theHasAtLeastOneAlivePlayer, theHasAbilityToRespawn, theHasAtLeastOnePlayerOnTeam); UTIL_LogPrintf("Team %d has lost.\n", this->mTeamNumber); //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) // UTIL_LogPrintf(" Victory player info: ent: %d, team: %d, role: %d, playmode: %d\n", theEntity->entindex(), theEntity->pev->team, (int)theEntity->GetRole(), (int)theEntity->GetPlayMode()); //END_FOR_ALL_ENTITIES(kAvHPlayerClassName) } return theTeamHasLost; } HiveInfoListType AvHTeam::GetHiveInfoList() const { return this->mHiveInfoList; } const AvHResearchManager& AvHTeam::GetResearchManager() const { return this->mResearchManager; } AvHResearchManager& AvHTeam::GetResearchManager() { return this->mResearchManager; } float AvHTeam::GetTotalResourcesGathered() const { return this->mTotalResourcesGathered; } void AvHTeam::AddResourcesGathered(float inResources) { this->mTotalResourcesGathered += inResources; } bool AvHTeam::GetIsPlayerOnTeam(int inPlayerIndex) const { bool theSuccess = false; EntityListType::const_iterator theIter = std::find(this->mPlayerList.begin(), this->mPlayerList .end(), inPlayerIndex); if(theIter != this->mPlayerList.end()) { theSuccess = true; } return theSuccess; } AvHPlayer* AvHTeam::GetPlayerFromIndex(int inPlayerIndex) { AvHPlayer* thePlayer = NULL; CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex)); thePlayer = dynamic_cast<AvHPlayer*>(theBaseEntity); return thePlayer; } const AvHPlayer* AvHTeam::GetPlayerFromIndex(int inPlayerIndex) const { const AvHPlayer* thePlayer = NULL; const CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex)); thePlayer = dynamic_cast<const AvHPlayer*>(theBaseEntity); return thePlayer; } void AvHTeam::GetOrders(OrderListType& outOrderList) const { outOrderList = this->mOrderList; } bool AvHTeam::GetDoesPlayerHaveOrder(int inPlayerIndex) { bool thePlayerHasOrders = false; for(OrderListType::iterator theIter = this->mOrderList.begin(); theIter != this->mOrderList.end(); theIter++) { EntityInfo theReceiver = theIter->GetReceiver(); if(theReceiver == inPlayerIndex) { thePlayerHasOrders = true; } } return thePlayerHasOrders; } //void AvHTeam::GetPlayersCompletingOrders(const EntityListType& outPlayerList) const //{ // outPlayerList = this->mPlayersCompletingOrder; //} void AvHTeam::SetOrder(const AvHOrder& inOrder) { AvHChangeOrder(this->mOrderList, inOrder); } void AvHTeam::AddTechNode(AvHMessageID inMessageID, AvHTechID inTechID, AvHTechID inPrereq1, AvHTechID inPrereq2, bool inAllowMultiples, bool inResearched) { int theCost = GetGameRules()->GetCostForMessageID(inMessageID); int theBuildTime = GetGameRules()->GetBuildTimeForMessageID(inMessageID); this->mResearchManager.AddTechNode(inMessageID, inTechID, inPrereq1, inPrereq2, theCost, theBuildTime, inResearched, inAllowMultiples); } void AvHTeam::InitializeTechNodes() { // Clear them first this->mResearchManager.Reset(); //this->mUpgradeCosts.clear(); this->mTechSlotManager.Clear(); // Depending on game mode, set tech tree if(GetGameRules()->GetIsCombatMode()) { this->InitializeCombatTechNodes(); } else { // else regular NS if(this->mTeamType == AVH_CLASS_TYPE_MARINE) { this->AddTechNode(MESSAGE_NULL, TECH_COMMAND_CENTER, TECH_NULL, TECH_NULL, true, true); this->AddTechNode(BUILD_RECYCLE, TECH_NULL, TECH_NULL); this->AddTechNode(BUILD_COMMANDSTATION, TECH_NULL, TECH_NULL); this->AddTechNode(BUILD_INFANTRYPORTAL, TECH_INFANTRYPORTAL, TECH_COMMAND_CENTER); //this->AddTechNode(RESEARCH_FASTER_REINFORCEMENTS, TECH_RESEARCH_FASTER_REINFORCEMENTS, TECH_INFANTRYPORTAL, TECH_OBSERVATORY, false, false); this->AddTechNode(MESSAGE_CANCEL, TECH_NULL, TECH_NULL, TECH_NULL, true); this->AddTechNode(BUILD_AMMO, TECH_NULL, TECH_NULL); this->AddTechNode(BUILD_HEALTH, TECH_NULL, TECH_NULL); this->AddTechNode(BUILD_CAT, TECH_NULL, TECH_RESEARCH_CATALYSTS, TECH_NULL, true, false); this->AddTechNode(BUILD_RESOURCES, TECH_COMMAND_CENTER, TECH_NULL, TECH_NULL, true, false); this->AddTechNode(BUILD_HEALTH, TECH_COMMAND_CENTER, TECH_NULL); // CC //this->AddTechNode(RESEARCH_HEALTH, TECH_RESEARCH_HEALTH, TECH_COMMAND_CENTER, TECH_NULL, false); // Turret factory route this->AddTechNode(BUILD_TURRET_FACTORY, TECH_TURRET_FACTORY); this->AddTechNode(BUILD_TURRET, TECH_NULL, TECH_TURRET_FACTORY); this->AddTechNode(RESEARCH_ELECTRICAL, TECH_RESEARCH_ELECTRICAL, TECH_TURRET_FACTORY, TECH_NULL, true); this->AddTechNode(TURRET_FACTORY_UPGRADE, TECH_ADVANCED_TURRET_FACTORY, TECH_TURRET_FACTORY, TECH_NULL, true); this->AddTechNode(BUILD_SIEGE, TECH_NULL, TECH_ADVANCED_TURRET_FACTORY); // Arms lab this->AddTechNode(BUILD_ARMSLAB, TECH_ARMSLAB, TECH_ARMORY); this->AddTechNode(RESEARCH_ARMOR_ONE, TECH_RESEARCH_ARMOR_ONE, TECH_ARMSLAB, TECH_NULL, false); this->AddTechNode(RESEARCH_ARMOR_TWO, TECH_RESEARCH_ARMOR_TWO, TECH_RESEARCH_ARMOR_ONE, TECH_NULL, false); this->AddTechNode(RESEARCH_ARMOR_THREE, TECH_RESEARCH_ARMOR_THREE, TECH_RESEARCH_ARMOR_TWO, TECH_NULL, false); this->AddTechNode(RESEARCH_WEAPONS_ONE, TECH_RESEARCH_WEAPONS_ONE, TECH_ARMSLAB, TECH_NULL, false); this->AddTechNode(RESEARCH_WEAPONS_TWO, TECH_RESEARCH_WEAPONS_TWO, TECH_RESEARCH_WEAPONS_ONE, TECH_NULL, false); this->AddTechNode(RESEARCH_WEAPONS_THREE, TECH_RESEARCH_WEAPONS_THREE, TECH_RESEARCH_WEAPONS_TWO, TECH_NULL, false); this->AddTechNode(RESEARCH_CATALYSTS, TECH_RESEARCH_CATALYSTS, TECH_ARMSLAB, TECH_NULL, false, false); this->AddTechNode(RESEARCH_HEAVYARMOR, TECH_RESEARCH_HEAVYARMOR, TECH_PROTOTYPE_LAB, TECH_NULL, false); // Prototype lab this->AddTechNode(BUILD_PROTOTYPE_LAB, TECH_PROTOTYPE_LAB, TECH_ARMSLAB, TECH_ADVANCED_ARMORY); this->AddTechNode(RESEARCH_JETPACKS, TECH_RESEARCH_JETPACKS, TECH_PROTOTYPE_LAB, TECH_NULL, false, false); // Armor add-ons this->AddTechNode(BUILD_JETPACK, TECH_NULL, TECH_RESEARCH_JETPACKS, TECH_PROTOTYPE_LAB); this->AddTechNode(BUILD_HEAVY, TECH_NULL, TECH_RESEARCH_HEAVYARMOR, TECH_PROTOTYPE_LAB); // Weapon factory route this->AddTechNode(BUILD_ARMORY, TECH_ARMORY); this->AddTechNode(ARMORY_UPGRADE, TECH_ADVANCED_ARMORY, TECH_ARMORY); this->AddTechNode(RESEARCH_GRENADES, TECH_RESEARCH_GRENADES, TECH_ARMORY, TECH_NULL, false, false); //this->AddTechNode(BUILD_NUKE_PLANT, TECH_NUKE_PLANT, TECH_ADVANCED_ARMORY); // Observatory/knowledge/phase gate route this->AddTechNode(BUILD_OBSERVATORY, TECH_OBSERVATORY, TECH_ARMORY); this->AddTechNode(BUILD_SCAN, TECH_NULL, TECH_OBSERVATORY); this->AddTechNode(RESEARCH_MOTIONTRACK, TECH_RESEARCH_MOTIONTRACK, TECH_OBSERVATORY, TECH_NULL, false); this->AddTechNode(RESEARCH_DISTRESSBEACON, TECH_RESEARCH_DISTRESSBEACON, TECH_OBSERVATORY, TECH_NULL); this->AddTechNode(RESEARCH_PHASETECH, TECH_RESEARCH_PHASETECH, TECH_INFANTRYPORTAL, TECH_OBSERVATORY, false); this->AddTechNode(BUILD_PHASEGATE, TECH_NULL, TECH_OBSERVATORY, TECH_RESEARCH_PHASETECH); // Weapons this->AddTechNode(BUILD_SHOTGUN, TECH_NULL, TECH_ARMORY); this->AddTechNode(BUILD_WELDER, TECH_NULL, TECH_ARMORY); this->AddTechNode(BUILD_MINES, TECH_NULL, TECH_ARMORY); this->AddTechNode(BUILD_HMG, TECH_NULL, TECH_ADVANCED_ARMORY); this->AddTechNode(BUILD_GRENADE_GUN, TECH_NULL, TECH_ADVANCED_ARMORY, TECH_NULL); //this->AddTechNode(BUILD_NUKE, TECH_NULL, TECH_NUKE_PLANT, TECH_ADVANCED_ARMORY); // Specials //this->AddTechNode(BUILD_MEDKIT, TECH_NULL, TECH_MEDLAB); // Add tech to allow resource adjustment (allow it to be researched multiple times) //this->AddTechNode(RESOURCE_UPGRADE, TECH_NULL, TECH_NULL, TECH_NULL); // Initialize tech slots for marines this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_COMMANDER_STATION, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_TURRET_FACTORY, RESEARCH_ELECTRICAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, TURRET_FACTORY_UPGRADE, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ADVANCED_TURRET_FACTORY, RESEARCH_ELECTRICAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ARMORY, ARMORY_UPGRADE, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_GRENADES, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ADVANCED_ARMORY, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_GRENADES, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ARMSLAB, RESEARCH_WEAPONS_ONE, RESEARCH_WEAPONS_TWO, RESEARCH_WEAPONS_THREE, RESEARCH_CATALYSTS, RESEARCH_ARMOR_ONE, RESEARCH_ARMOR_TWO, RESEARCH_ARMOR_THREE, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_PROTOTYPE_LAB, RESEARCH_JETPACKS, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_HEAVYARMOR, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_OBSERVATORY, BUILD_SCAN, RESEARCH_PHASETECH, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_DISTRESSBEACON, RESEARCH_MOTIONTRACK, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_TURRET, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_SIEGETURRET, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_RESTOWER, RESEARCH_ELECTRICAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_INFANTRYPORTAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_PHASEGATE, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MARINE_PLAYER)); // Initialize tech slots for top-level menus. Note that the data for these four menus is stored in the command station iuser1 this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_BUILD, BUILD_RESOURCES, BUILD_INFANTRYPORTAL, BUILD_ARMORY, BUILD_COMMANDSTATION, BUILD_TURRET_FACTORY, BUILD_TURRET, BUILD_SIEGE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_BUILD_ADVANCED, BUILD_OBSERVATORY, BUILD_ARMSLAB, BUILD_PROTOTYPE_LAB, BUILD_PHASEGATE)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_ASSIST, BUILD_AMMO, BUILD_HEALTH, BUILD_CAT)); this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_EQUIP, BUILD_MINES, BUILD_SHOTGUN, BUILD_HMG, BUILD_GRENADE_GUN, BUILD_WELDER, BUILD_JETPACK, BUILD_HEAVY)); //this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_ORDERS, BUILD_HEALTH)); } else { // These upgrades are per alien this->AddTechNode(ALIEN_LIFEFORM_ONE, TECH_SKULK, TECH_NULL, TECH_NULL, true, true); this->AddTechNode(ALIEN_LIFEFORM_TWO, TECH_GORGE, TECH_NULL, TECH_NULL, true, true); this->AddTechNode(ALIEN_LIFEFORM_THREE, TECH_LERK, TECH_SKULK, TECH_NULL, true, true); this->AddTechNode(ALIEN_LIFEFORM_FOUR, TECH_FADE, TECH_LERK, TECH_NULL, true, true); this->AddTechNode(ALIEN_LIFEFORM_FIVE, TECH_ONOS, TECH_FADE, TECH_NULL, true, true); // Only gorge can build this->AddTechNode(ALIEN_BUILD_RESOURCES, TECH_ALIEN_RESOURCE_NODE, TECH_GORGE, TECH_NULL, true, false); this->AddTechNode(ALIEN_BUILD_HIVE, TECH_HIVE, TECH_GORGE, TECH_NULL, true, false); this->AddTechNode(ALIEN_BUILD_MOVEMENT_CHAMBER, TECH_MOVEMENT_CHAMBER, TECH_GORGE, TECH_NULL, true, false); this->AddTechNode(ALIEN_BUILD_DEFENSE_CHAMBER, TECH_DEFENSE_CHAMBER, TECH_GORGE, TECH_NULL, true, false); this->AddTechNode(ALIEN_BUILD_SENSORY_CHAMBER, TECH_SENSORY_CHAMBER, TECH_GORGE, TECH_NULL, true, false); this->AddTechNode(ALIEN_BUILD_OFFENSE_CHAMBER, TECH_OFFENSE_CHAMBER, TECH_GORGE, TECH_NULL, true, false); // Upgrades this->AddTechNode(ALIEN_EVOLUTION_ONE, TECH_DEFENSE_CHAMBER, TECH_NULL, TECH_NULL, true, false); this->AddTechNode(ALIEN_EVOLUTION_TWO, TECH_DEFENSE_CHAMBER, TECH_NULL, TECH_NULL, true, false); this->AddTechNode(ALIEN_EVOLUTION_THREE, TECH_DEFENSE_CHAMBER, TECH_NULL, TECH_NULL, true, false); this->AddTechNode(ALIEN_EVOLUTION_SEVEN, TECH_MOVEMENT_CHAMBER, TECH_NULL, TECH_NULL, true, false); this->AddTechNode(ALIEN_EVOLUTION_EIGHT, TECH_MOVEMENT_CHAMBER, TECH_NULL, TECH_NULL, true, false); this->AddTechNode(ALIEN_EVOLUTION_NINE, TECH_MOVEMENT_CHAMBER, TECH_NULL, TECH_NULL, true, false); this->AddTechNode(ALIEN_EVOLUTION_TEN, TECH_SENSORY_CHAMBER, TECH_NULL, TECH_NULL, true, false); this->AddTechNode(ALIEN_EVOLUTION_ELEVEN, TECH_SENSORY_CHAMBER, TECH_NULL, TECH_NULL, true, false); this->AddTechNode(ALIEN_EVOLUTION_TWELVE, TECH_SENSORY_CHAMBER, TECH_NULL, TECH_NULL, true, false); } } } const AvHTechTree& AvHTeam::GetTechNodes() const { return this->mResearchManager.GetTechNodes(); } AvHTechTree& AvHTeam::GetTechNodes() { return this->mResearchManager.GetTechNodes(); } const AvHTechSlotManager& AvHTeam::GetTechSlotManager() const { return this->mTechSlotManager; } AvHTechSlotManager& AvHTeam::GetTechSlotManager() { return this->mTechSlotManager; } // For marine teams, this means a player is trying to vote down the commander bool AvHTeam::PlayerVote(int inPlayerIndex, string& outPlayerMessage) { bool theSuccess = false; // If we are a marine team and we have a commander if((this->GetTeamType() == AVH_CLASS_TYPE_MARINE) && (this->GetCommander() != -1) && GetGameRules()->GetGameStarted()) { // If player is allowed to vote (can only vote every x minutes) EntityListType::iterator theIterator = std::find(this->mVotingPlayers.begin(), this->mVotingPlayers.end(), inPlayerIndex); if(theIterator == this->mVotingPlayers.end()) { AvHPlayer* theVotingPlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex))); if(theVotingPlayer) { theVotingPlayer->SendMessage(kVoteCast); // Increment vote this->mVotes++; this->mVotingPlayers.push_back(inPlayerIndex); theSuccess = true; AvHPlayer* theCommander = GetCommanderPlayer(); if(theCommander) { theCommander->LogPlayerActionPlayer(theVotingPlayer, "votedown"); } } } else { // Display message that player has already voted outPlayerMessage = kAlreadyVoted; } } else { outPlayerMessage = kVoteIllegal; } return theSuccess; } void AvHTeam::PlayFunHUDSoundOnce(AvHHUDSound inHUDSound) { if(std::find(this->mFunSoundsPlayed.begin(), this->mFunSoundsPlayed.end(), inHUDSound) == this->mFunSoundsPlayed.end()) { this->PlayHUDSoundForAlivePlayers(inHUDSound); this->mFunSoundsPlayed.push_back(inHUDSound); } } void AvHTeam::PlayHUDSoundForAlivePlayers(AvHHUDSound inHUDSound) const { FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) if(theEntity->GetIsRelevant() && (theEntity->pev->team == this->mTeamNumber)) { theEntity->PlayHUDSound(inHUDSound); } END_FOR_ALL_ENTITIES(kAvHPlayerClassName); } // joev: Bug 0000767 void AvHTeam::PlayHUDSoundForAllPlayers(AvHHUDSound inHUDSound) const { FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) theEntity->PlayHUDSound(inHUDSound); END_FOR_ALL_ENTITIES(kAvHPlayerClassName); } // :joev // Assumes rank change has been approved from on high. It just does the best job it can, // it doesn't change any other ranks than the one indicated bool AvHTeam::ProcessRankChange(AvHPlayer* inPlayer, AvHUser3 inOldUser3, AvHUser3 inNewUser3) { bool theSuccess = false; // Someone just stepped up or down, change the player's squad int thePlayerIndex = inPlayer->entindex(); // if player is now a soldier if(inNewUser3 == AVH_USER3_MARINE_PLAYER) { if(this->GetCommander() == thePlayerIndex) { this->SetCommander(-1); } } // else if player is now commander else if(inNewUser3 == AVH_USER3_COMMANDER_PLAYER) { // Set him as commander this->SetCommander(thePlayerIndex); theSuccess = true; } return theSuccess; } void AvHTeam::ResetGame() { AvHTeamNumber theTeamNumber = this->mTeamNumber; AvHClassType theTeamType = this->mTeamType; this->Init(); this->mTeamNumber = theTeamNumber; this->mTeamType = theTeamType; this->mLastInjectionTime = gpGlobals->time; this->mLastResourceUpdateTime = -1; this->mLastCommandScoreUpdateTime = -1; this->mStartingLocation.x = this->mStartingLocation.y = this->mStartingLocation.z = 0; this->mNumBuiltCommandStations = 0; this->mNumActiveHives = 0; if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { // Spawn the initial hives int theNumHives = GetGameRules()->GetGameplay().GetInitialHives(); for(int i = 0; i < theNumHives; i++) { this->SpawnHive(&this->mStartingLocation, true); } if(GetGameRules()->GetIsCombatMode()) { for(int i= 0; i < 3; i++) { this->AddTeamUpgrade(ALIEN_UPGRADE_CATEGORY_DEFENSE); this->AddTeamUpgrade(ALIEN_UPGRADE_CATEGORY_MOVEMENT); this->AddTeamUpgrade(ALIEN_UPGRADE_CATEGORY_SENSORY); } } } if(!GetGameRules()->GetIsCombatMode()) { // Put down command station FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) if(theEntity->pev->team == (int)(this->mTeamNumber)) { theEntity->SetConstructionComplete(true); DROP_TO_FLOOR(ENT(theEntity->pev)); //PLAYBACK_EVENT_FULL(0, theEntity->edict(), gPhaseInEventID, 0, theEntity->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); VectorCopy(theEntity->pev->origin, this->mStartingLocation); } END_FOR_ALL_ENTITIES(kwsTeamCommand) if(this->mTeamType == AVH_CLASS_TYPE_MARINE) { this->SetTeamResources(GetGameRules()->GetGameplay().GetInitialMarinePoints()); } // else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) // { // this->SetTeamResources(GetGameRules()->GetGameplay().GetInitialAlienPoints()); // } this->SpawnResourceTower(); } this->mOrderList.clear(); this->ResetServerPlayerData(); //this->InitializeRandomResourceShares(); this->mHiveInfoList.clear(); this->mTimeOfLastStructureUpdate = -1; this->mVoteStarted = false; this->mPreviousVotes = this->mVotes = 0; this->mVotingPlayers.clear(); this->mTimeVoteStarted = -1; this->mVotesNeeded = 0; this->mLowResourceAlerts.clear(); this->mCommandersToldAboutJumpKey.clear(); this->mFunSoundsPlayed.clear(); this->mPlayedSeeDeadPeople = false; int theEasterEggChance = max(0.0f, avh_eastereggchance.value); this->mPlayFunSoundsThisGame = (RANDOM_LONG(0, theEasterEggChance) == 0); // Don't give hints right away this->mTimeOfLastTeamHint = gpGlobals->time; this->mTimeLastHintUpdate = -1; // Reset group info for(int i = 0; i < kNumHotkeyGroups; i++) { this->mGroups[i].clear(); this->mGroupTypes[i] = AVH_USER3_NONE; } this->mSelectAllGroup.clear(); } void AvHTeam::SpawnResourceTower() { string theResourceEntity; AvHMessageID theResourceEntityMessageID = MESSAGE_NULL; if(this->mTeamType == AVH_CLASS_TYPE_MARINE) { theResourceEntity = kwsResourceTower; theResourceEntityMessageID = BUILD_RESOURCES; } else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { theResourceEntity = kwsAlienResourceTower; theResourceEntityMessageID = ALIEN_BUILD_RESOURCES; } // Create a resource tower for the team, nearest their spawn Vector theLocationToBuild; // Look for func resource nearest to team start CBaseEntity* theNearestFuncResource = NULL; float theNearestDistance = -1; FOR_ALL_ENTITIES(kesFuncResource, AvHFuncResource*) if(!theEntity->GetIsOccupied()) { float theDistance = VectorDistance(this->mStartingLocation, theEntity->pev->origin); if((theNearestDistance == -1) || (theDistance < theNearestDistance)) { theNearestFuncResource = theEntity; theNearestDistance = theDistance; } } END_FOR_ALL_ENTITIES(kesFuncResource); if(theNearestFuncResource) { Vector theLocation = theNearestFuncResource->pev->origin; theLocation.z += 35; CBaseEntity* theBaseEntity = CBaseEntity::Create(theResourceEntity.c_str(), theLocation, AvHSUGetRandomBuildingAngles()); theBaseEntity->pev->team = this->mTeamNumber; AvHSUBuildingJustCreated(theResourceEntityMessageID, theBaseEntity, NULL); // Set it complete immediately. If not, aliens are slowed down noticeably because they need builders to tower building AvHResourceTower* theResourceTower = dynamic_cast<AvHResourceTower*>(theBaseEntity); ASSERT(theResourceTower); // The first resource tower is active immediately theResourceTower->SetActivateTime(0); //DROP_TO_FLOOR(ENT(theBaseBuildable->pev)); theResourceTower->SetConstructionComplete(true); } } // Delete any server player data that has expired void AvHTeam::ResetServerPlayerData() { // Run through list for(AvHServerPlayerDataListType::iterator theIter = this->mServerPlayerData.begin(); theIter != this->mServerPlayerData.end(); /* no inc */) { // Delete it unless the player has been voted down if(theIter->second.GetTimeVotedDown() == -1) { AvHServerPlayerDataListType::iterator theTempIter = theIter; theTempIter++; this->mServerPlayerData.erase(theIter); theIter = theTempIter; } else { theIter++; } } } int AvHTeam::GetDesiredSpawnCount() const { int theDesiredSpawns = 0; if(this->mTeamType == AVH_CLASS_TYPE_MARINE) { // 16 for the marine start theDesiredSpawns = 16; } else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { // 16 for each hive theDesiredSpawns = 16*3; } return theDesiredSpawns; } float AvHTeam::GetInitialPlayerPoints(edict_t* inEdict) { float theInitialPoints = 0; AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(inEdict)); ASSERT(thePlayer); // Marines don't get any individual points, aliens get random amount // Use WONID to make sure player doesn't try to get more points by quitting and rejoining if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { if(GetGameRules()->GetArePlayersAllowedToJoinImmediately() /*&& !thePlayer->GetHasLeftReadyRoom()*/) { theInitialPoints = GetGameRules()->GetGameplay().GetInitialAlienPoints(); } // If WON id already exists (because we already gave points out to that player) AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(inEdict); if(theServerPlayerData && (theServerPlayerData->GetResources() != -1)) { // Give them the number of points they last had theInitialPoints = theServerPlayerData->GetResources(); } } return theInitialPoints; } float AvHTeam::GetInitialExperience(edict_t* inEdict) { float theInitialExperience = 0.0f; //AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(inEdict); //if(theServerPlayerData && (theServerPlayerData->GetExperience() > 0.0f)) //{ // // Give them experience they last had // theInitialExperience = theServerPlayerData->GetExperience(); //} return theInitialExperience; } void AvHTeam::SetGameStarted(bool inGameStarted) { if(inGameStarted) { if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { this->mLastHiveSpawnTime = gpGlobals->time; } this->mLastInjectionTime = gpGlobals->time; //#ifndef DEBUG // If team is alien, give them all game started hud sound FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) if(theEntity->pev->team == this->mTeamNumber) { theEntity->PlayHUDSound(HUD_SOUND_GAMESTART); // Send Combat objective if(GetGameRules()->GetIsCombatMode()) { string theMessage; if(GetGameRules()->GetCombatAttackingTeamNumber() == this->mTeamNumber) { theMessage = kYouAreAttacking; } else { theMessage = kYouAreDefending; } theEntity->SendMessage(theMessage.c_str(), NORMAL); } } END_FOR_ALL_ENTITIES(kAvHPlayerClassName); //#endif } } void AvHTeam::KillCS() { AvHCommandStation* theCS = NULL; FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) if(theEntity->pev->health > 0) { theEntity->TakeDamage(theEntity->pev, theEntity->pev, 20000, DMG_GENERIC); } END_FOR_ALL_ENTITIES(kwsTeamCommand) } void AvHTeam::KillHive() { if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { AvHHive* theHive = AvHSUGetRandomActiveHive(this->mTeamNumber); if(theHive) { theHive->Killed(NULL, 0); } } } void AvHTeam::SpawnHive(Vector* outLocation, bool inForce) { if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { AvHHive* theHive = AvHSUGetRandomActivateableHive(this->mTeamNumber); if(theHive) { if(theHive->StartSpawningForTeam(this->mTeamNumber, inForce)) { theHive->SetConstructionComplete(true); this->mNumActiveHives++; if(outLocation) { VectorCopy(theHive->pev->origin, *outLocation); } } else { ALERT(at_console, "Error spawning hive. Is it blocked by something?\n"); } } } } int AvHTeam::GetCommander() const { return this->mCommander; } AvHPlayer* AvHTeam::GetCommanderPlayer() { AvHPlayer* theCommanderPlayer = NULL; if(this->mCommander > 0) { theCommanderPlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mCommander))); } return theCommanderPlayer; } AvHTeamNumber AvHTeam::GetTeamNumber(void) const { return this->mTeamNumber; } void AvHTeam::SetTeamNumber(AvHTeamNumber inTeamNumber) { this->mTeamNumber = inTeamNumber; } AvHClassType AvHTeam::GetTeamType(void) const { return this->mTeamType; } const char* AvHTeam::GetTeamTypeString(void) const { const char* theTeamString = "Unknown"; switch(this->mTeamType) { case AVH_CLASS_TYPE_MARINE: theTeamString = "marine"; break; case AVH_CLASS_TYPE_ALIEN: theTeamString = "alien"; break; } return theTeamString; } void AvHTeam::AddTeamUpgrade(AvHAlienUpgradeCategory inCategory) { int theNumUpgradeCategories = AvHGetNumUpgradesInCategoryInList(this->mAlienUpgrades, inCategory); // If we don't have an upgrade of this type, trigger an alert if(theNumUpgradeCategories == 0) { GetGameRules()->TriggerAlert(this->mTeamNumber, ALERT_NEW_TRAIT, -1); } this->mAlienUpgrades.push_back(inCategory); } AvHAlienUpgradeListType AvHTeam::GetAlienUpgrades() const { return this->mAlienUpgrades; } bool AvHTeam::RemoveAlienUpgradeCategory(AvHAlienUpgradeCategory inCategory) { bool theRemoved = AvHRemoveUpgradeCategory(inCategory, this->mAlienUpgrades); return theRemoved; } int AvHTeam::GetTeamWideUpgrades() const { return this->mTeamWideUpgrades; } void AvHTeam::ProcessTeamUpgrade(AvHMessageID inMessageID, bool inGive) { ProcessGenericUpgrade(this->mTeamWideUpgrades, inMessageID, inGive); } void AvHTeam::InitializeRandomResourceShares() { this->mRandomResourceShares.clear(); this->mTotalShareWeight = 0; // Populate random resource shares with random value (like cards in Bridge) for(int i = 0; i < kTotalShares; i++) { const int kSlope = 2; int theBoundaryOne = kTotalShares/3; int theBoundaryTwo = (2*kTotalShares)/3; int theCardValue = 1; if((i > theBoundaryOne) && (i < theBoundaryTwo)) { theCardValue = 2*kSlope; } else if(i > theBoundaryTwo) { theCardValue = 3*kSlope; } this->mRandomResourceShares.push_back(theCardValue); this->mTotalShareWeight += theCardValue; } } float AvHTeam::WithdrawPointsViaRandomShares(int inPlayerIndex) { int theNumSharesLeft = this->mRandomResourceShares.size(); ASSERT(theNumSharesLeft >= kNumSharesPerPlayer); // Pick some random shares and add them up int theRandomShares = 0; for(int i = 0; i < kNumSharesPerPlayer; i++) { // Pick a random "card", add it's shares, remove card from pool int theRandomNumber = g_engfuncs.pfnRandomLong(0, this->mRandomResourceShares.size() - 1); RandomResourceShareListType::iterator theIter = this->mRandomResourceShares.begin() + theRandomNumber; theRandomShares = theRandomShares + *theIter; // Remember which "cards" we've given this player so we can add them back in later this->mPlayerShares[inPlayerIndex].push_back(*theIter); this->mRandomResourceShares.erase(theIter); } // Return this percentage of the team resources for this player float thePercentage = theRandomShares/(float)this->mTotalShareWeight; float theInitialPoints = thePercentage*this->GetTeamResources(); this->SetTeamResources(this->GetTeamResources() - theInitialPoints); return theInitialPoints; } void AvHTeam::SetTeamType(AvHClassType inType) { this->mTeamType = inType; switch(inType) { case AVH_CLASS_TYPE_MARINE: if( this->GetTeamNumber() == TEAM_ONE ) { this->mTeamName = kMarine1Team; } else { this->mTeamName = kMarine2Team; } this->mTeamPrettyName = kMarinePrettyName; break; case AVH_CLASS_TYPE_ALIEN: if( this->GetTeamNumber() == TEAM_TWO ) { this->mTeamName = kAlien1Team; } else { this->mTeamName = kAlien2Team; } this->mTeamPrettyName = kAlienPrettyName; break; default: this->mTeamName = kUndefinedTeam; this->mTeamPrettyName = kUndefPrettyName; break; } // Make sure team names aren't longer than MAX_TEAM_NAME ASSERT(this->mTeamName.length() < MAX_TEAM_NAME); } int AvHTeam::GetNumWebStrands() const { return this->mNumWebStrands; } void AvHTeam::SetNumWebStrands(int inNumWebStrands) { this->mNumWebStrands = inNumWebStrands; } float AvHTeam::GetTeamResources() const { return this->mTeamResources; } void AvHTeam::SetTeamResources(float inTeamResources) { this->mTeamResources = inTeamResources; } float AvHTeam::GetHandicap() const { return this->mHandicap; } void AvHTeam::SetHandicap(float inHandicap) { this->mHandicap = inHandicap; } const char* AvHTeam::GetTeamName(void) const { return this->mTeamName.c_str(); } const char* AvHTeam::GetTeamPrettyName(void) const { return this->mTeamPrettyName.c_str(); } void AvHTeam::SetCommander(int inPlayerNumber) { if(inPlayerNumber != this->mCommander) { // Reset commander hints this->mTimeOfLastTeamHint = -1; this->mCommander = inPlayerNumber; } } void AvHTeam::SetTeamName(const char* inTeamName) { this->mTeamName = inTeamName; } void AvHTeam::SetTeamPrettyName(const char* inTeamName) { this->mTeamPrettyName = inTeamName; } int AvHTeam::GetPlayerCount(bool inCountOnlyDead) const { int theNumPlayers = this->mPlayerList.size(); if(inCountOnlyDead) { theNumPlayers = 0; FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) if((AvHTeamNumber)theEntity->pev->team == this->mTeamNumber) { AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(theEntity); ASSERT(thePlayer); if(!thePlayer->IsAlive()) { theNumPlayers++; } } END_FOR_ALL_ENTITIES(kAvHPlayerClassName) } return theNumPlayers; } bool AvHTeam::RemovePlayer(int inPlayerIndex) { bool theSuccess = false; if(this->GetIsPlayerOnTeam(inPlayerIndex)) { // Remove player EntityListType::iterator thePlayerIter = std::find(this->mPlayerList.begin(), this->mPlayerList .end(), inPlayerIndex); if(thePlayerIter != this->mPlayerList.end()) { AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex))); ASSERT(thePlayer); // Add cards back into pool // RandomResourceShareListType& theVector = this->mPlayerShares[inPlayerIndex]; // for(RandomResourceShareListType::iterator theCardIter = theVector.begin(); theCardIter != theVector.end(); theCardIter++) // { // this->mRandomResourceShares.push_back(*theCardIter); // } // Make sure no entities have this player as an owner edict_t* thePlayerEdict = thePlayer->edict(); FOR_ALL_BASEENTITIES(); if(theBaseEntity->pev->owner == thePlayerEdict) { theBaseEntity->pev->owner = NULL; } AvHBuildable* theBuildable = dynamic_cast<AvHBuildable*>(theBaseEntity); if(theBuildable && (theBuildable->GetBuilder() == thePlayer->entindex())) { theBuildable->SetBuilder(-1); } END_FOR_ALL_BASEENTITIES(); AvHSURemoveEntityFromHotgroupsAndSelection(inPlayerIndex); // theVector.clear(); this->mPlayerList.erase(thePlayerIter); theSuccess = true; } } if(this->mCommander == inPlayerIndex) { this->SetCommander(-1); } return theSuccess; } void AvHTeam::TriggerAddTech(AvHTechID inTechID) { if(!GetGameRules()->GetIsCombatMode()) { this->mResearchManager.TriggerAddTech(inTechID); // Get list of completed research that depended on this tech TechNodeMap nodes = this->mResearchManager.GetResearchNodesDependentOn(inTechID); // For all researched nodes TechNodeMap::const_iterator current, end = nodes.end(); for( current = nodes.begin(); current != end; ++current ) { if( current->second->getIsResearched() ) { GetGameRules()->ProcessTeamUpgrade( current->first, this->mTeamNumber, 0, true ); } } } } void AvHTeam::TriggerRemoveTech(AvHTechID inTechID) { if(!GetGameRules()->GetIsCombatMode()) { // Run through world, looking for anything alive that could still be giving us this tech bool theFoundActiveTech = false; FOR_ALL_BASEENTITIES(); AvHBuildable* theBuildable = dynamic_cast<AvHBuildable*>(theBaseEntity); if(theBuildable) { if((theBuildable->GetSupportsTechID(inTechID)) && (theBuildable->GetTeamNumber() == this->mTeamNumber)) { if(theBuildable->GetIsTechActive()) { theFoundActiveTech = true; break; } } } END_FOR_ALL_BASEENTITIES(); if(!theFoundActiveTech) { // If we can't find anything, remove it from the tech nodes this->mResearchManager.TriggerRemoveTech(inTechID); // Get list of completed research that depended on this tech TechNodeMap nodes = this->mResearchManager.GetResearchNodesDependentOn(inTechID); // For all researched nodes TechNodeMap::iterator current, end = nodes.end(); for( current = nodes.begin(); current != end; ++current ) { if( current->second->getIsResearched() ) { // Disable because of loss of this tech and remove them GetGameRules()->ProcessTeamUpgrade( current->first, this->mTeamNumber, 0, false ); } } } } } void AvHTeam::Update() { PROFILE_START() this->UpdateHints(); PROFILE_END(kAvHTeamUpdateHints) PROFILE_START() this->UpdateInventoryEnabledState(); PROFILE_END(kAvHTeamUpdateInventoryEnabledState) PROFILE_START() this->UpdateReinforcementWave(); PROFILE_END(kAvHTeamUpdateReinforcementWave) PROFILE_START() this->UpdateOrders(); PROFILE_END(kAvHTeamUpdateOrders) PROFILE_START() this->UpdateResearch(); PROFILE_END(kAvHTeamUpdateResearch) PROFILE_START() this->UpdateResources(); PROFILE_END(kAvHTeamUpdateResources) PROFILE_START() this->UpdateAlerts(); PROFILE_END(kAvHTeamUpdateAlerts) PROFILE_START() this->UpdateServerPlayerData(); PROFILE_END(kAvHTeamUpdateServerPlayerData) PROFILE_START() this->UpdateTeamStructures(); PROFILE_END(kAvHTeamUpdateTeamStructures) PROFILE_START() this->UpdateVoting(); PROFILE_END(kAvHTeamUpdateVoting) PROFILE_START() this->UpdatePlayers(); PROFILE_END(kAvHTeamUpdatePlayers) } void AvHTeam::UpdateTeamStructures() { const float kStructureUpdateRate = 1.0f; if((this->mTimeOfLastStructureUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastStructureUpdate + kStructureUpdateRate))) { this->mTimeOfLastStructureUpdate = gpGlobals->time; if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) { this->mHiveInfoList.clear(); // Search for free hives, or hives on our team FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) if((theEntity->pev->team == 0) || ((AvHTeamNumber)theEntity->pev->team == this->mTeamNumber)) { AvHHiveInfo theHiveInfo; theHiveInfo.mPosX = theEntity->pev->origin.x; theHiveInfo.mPosY = theEntity->pev->origin.y; theHiveInfo.mPosZ = theEntity->pev->origin.z; if (theEntity->pev->max_health > 0) { theHiveInfo.mHealthPercentage = (int)(100 * theEntity->pev->health / theEntity->pev->max_health); } else { theHiveInfo.mHealthPercentage = 0; } int theEntityIndex = theEntity->entindex(); // Fill in hive status int theStatus = kHiveInfoStatusBuilt; // Unbuilt hives if(theEntity->pev->team == 0) { theStatus = kHiveInfoStatusUnbuilt; } // building hives else if(!theEntity->GetIsBuilt()) { float theNormalizedBuildPercentage = theEntity->GetNormalizedBuildPercentage(); if(theNormalizedBuildPercentage > 0.0f) { theStatus = kHiveInfoStatusBuildingStage1; int theSteps = (int)(theNormalizedBuildPercentage*5.0f); theStatus += theSteps; } else { theStatus = kHiveInfoStatusUnbuilt; } } theHiveInfo.mStatus = theStatus; theHiveInfo.mTechnology = (int)(theEntity->GetTechnology()); // Under attack? theHiveInfo.mUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntityIndex); // Add onto the end, so we don't resend unnecessarily this->mHiveInfoList.insert(this->mHiveInfoList.end(), theHiveInfo); } END_FOR_ALL_ENTITIES(kesTeamHive); // Count number of active hives int theNumHives = 0; FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) if(theEntity->GetIsActive()) { AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); if(theTeamNumber == this->mTeamNumber) { theNumHives++; } } END_FOR_ALL_ENTITIES(kesTeamHive); this->mNumActiveHives = theNumHives; } else if(this->GetTeamType() == AVH_CLASS_TYPE_MARINE) { // Count # of command stations int theNumActiveCommandStations = 0; FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) if(theEntity->GetIsBuilt()) { AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); if(theTeamNumber == this->mTeamNumber) { theNumActiveCommandStations++; } } END_FOR_ALL_ENTITIES(kwsTeamCommand); this->mNumBuiltCommandStations = theNumActiveCommandStations; } } } void AvHTeam::UpdatePlayers() { //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) // GetGameRules()->ClientCommand(theEntity, ); //END_FOR_ALL_ENTITIES(kAvHPlayerClassName); // Update every once in awhile const float kPlayerUpdateRate = 2.0f; if((this->mLastPlayerUpdateTime == -1) || (gpGlobals->time > (this->mLastPlayerUpdateTime + kPlayerUpdateRate))) { if(!GetGameRules()->GetCheatsEnabled() && !GetGameRules()->GetIsTournamentMode() && !GetGameRules()->GetIsCombatMode()) { // If the team is alien and has no hives left if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { // Do we have any active hives? int theNumGrowingOrActiveHives = 0; FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); if(theTeamNumber == this->mTeamNumber) { if(theEntity->GetIsActive() || theEntity->GetIsSpawning()) { theNumGrowingOrActiveHives++; } } END_FOR_ALL_ENTITIES(kesTeamHive); if(theNumGrowingOrActiveHives == 0) { CBaseEntity* theWorld = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(0)); if(theWorld) { entvars_t* theInflictor = theWorld->pev; // Do some damage to remaining players FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) if((theEntity->pev->team == this->mTeamNumber) && theEntity->IsAlive()) { AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(theEntity); ASSERT(thePlayer); float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(theEntity->pev->iuser4, theEntity->GetUser3(), thePlayer->GetExperienceLevel()); float theDamage = theMaxHealth/8; theEntity->TakeDamage(theInflictor, theInflictor, theDamage, DMG_DROWN | DMG_IGNOREARMOR); } theEntity->PlayHUDSound(HUD_SOUND_COUNTDOWN); END_FOR_ALL_ENTITIES(kAvHPlayerClassName); } } } } this->mLastPlayerUpdateTime = gpGlobals->time; } // Update team score if we're in tournament mode this->SendResourcesGatheredScore(true); } bool AvHTeam::SendResourcesGatheredScore(bool inThisTeamOnly) { bool theSentMessage = false; if(GetGameRules()->GetIsTournamentMode()) { // Only send message to players on our team, or spectating players int theTeamScore = (int)this->GetTotalResourcesGathered(); if(theTeamScore != this->mClientTotalResourcesGathered) { if(inThisTeamOnly) { for(EntityListType::iterator thePlayerIter = this->mPlayerList.begin(); thePlayerIter != this->mPlayerList.end(); thePlayerIter++) { AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*thePlayerIter))); if(thePlayer) { NetMsg_TeamScore( thePlayer->pev, this->GetTeamName(), theTeamScore, 0 ); } } } else { NetMsg_TeamScore( this->GetTeamName(), theTeamScore, 0 ); } this->mClientTotalResourcesGathered = theTeamScore; theSentMessage = true; } } return theSentMessage; } void AvHTeam::UpdateVoting() { string theMessageForPlayers; AvHHUDSound theSoundForPlayers = HUD_SOUND_INVALID; bool theSendOnlyToNonvotingPlayers = false; // If we have more then 1 vote against commander if(this->mVotes > 0) { // If a vote hasn't started if(!this->mVoteStarted) { // Tell all players on team that a vote has started theMessageForPlayers = kVoteStarted; theSendOnlyToNonvotingPlayers = true; // Start vote this->mVoteStarted = true; this->mCommanderWhenVoteStarted = this->mCommander; this->mTimeVoteStarted = gpGlobals->time; } else { bool theResetVote = false; // If a new comm logs in after vote started, cancel vote if((this->mCommander > 0) && (this->mCommander != this->mCommanderWhenVoteStarted)) { theMessageForPlayers = kVoteCancelled; theResetVote = true; } else { // If we have enough votes to kick commander float theVotePercent = avh_votepercentneeded.value; int theMinVotesRequired = (int)avh_minvotesneeded.value; int theVotesNeeded = max((float)theMinVotesRequired, this->mPlayerList.size()*theVotePercent); // #545: If the voting has changed, indicate so in the HUD if ((this->mVotesNeeded != theVotesNeeded) || (this->mVotes != this->mPreviousVotes)) { // tankefugl: 0000545 char theVoteMessage[512]; sprintf(theVoteMessage, "Eject commander: %d of %d votes needed.", this->mVotes, theVotesNeeded); FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) if(theEntity->pev->team == this->mTeamNumber) { UTIL_SayText(theVoteMessage, theEntity); // , theEntity->entindex()); } END_FOR_ALL_ENTITIES(kAvHPlayerClassName); // UTIL_ClientPrintAll(HUD_PRINTTALK, theVoteMessage); UTIL_LogPrintf(theVoteMessage); this->mVotesNeeded = theVotesNeeded; this->mPreviousVotes = this->mVotes; // :tankefugl } if(this->mVotes >= theVotesNeeded) { // Kick commander AvHPlayer* theCommander = this->GetCommanderPlayer(); if(theCommander) { theCommander->StopTopDownMode(); theCommander->SetUser3(AVH_USER3_MARINE_PLAYER); // Remember when we were voted down, so they can't command again AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(theCommander->edict()); if(theServerPlayerData) { theServerPlayerData->SetTimeVotedDown(gpGlobals->time); } // Tell all players on team that commander has been ejected theMessageForPlayers = kVoteSucceeded; theSoundForPlayers = HUD_SOUND_MARINE_COMMANDER_EJECTED; theResetVote = true; } } else { // else if enough time has elapsed without a vote going through int theVoteCastTimeInSeconds = avh_votecasttime.value*60; if(gpGlobals->time > (this->mTimeVoteStarted + theVoteCastTimeInSeconds)) { // Tell all players on team that the vote has failed theMessageForPlayers = kVoteFailed; theResetVote = true; } } } if(theResetVote) { // Reset vote (don't reset players that voted, they can only vote once per game) this->mVotes = 0; this->mVoteStarted = false; this->mTimeVoteStarted = -1; // clear the list of voting players to allow more than one vote each game (#545) this->mVotingPlayers.clear(); this->mVotesNeeded = 0; this->mPreviousVotes = 0; } } } // Send message to players if(theMessageForPlayers != "") { // Send message to players on team FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) if(theEntity->GetTeam() == this->GetTeamNumber()) { int thePlayerIndex = theEntity->entindex(); EntityListType::iterator theIterator = std::find(this->mVotingPlayers.begin(), this->mVotingPlayers.end(), thePlayerIndex); bool thePlayerHasVoted = (theIterator != this->mVotingPlayers.end()); if(!theSendOnlyToNonvotingPlayers || !thePlayerHasVoted) { theEntity->SendMessage(theMessageForPlayers.c_str()); // Play "commander has been ejected" sound for all players if(theSoundForPlayers != HUD_SOUND_INVALID) { theEntity->PlayHUDSound(theSoundForPlayers); } } } END_FOR_ALL_ENTITIES(kAvHPlayerClassName); } } void AvHTeam::UpdateHints() { if(GetGameRules()->GetGameStarted() && (this->GetPlayerCount() > 1) && !GetGameRules()->GetIsCombatMode()) { const float kHintUpdateInterval = 2.0f; if((this->mTimeLastHintUpdate == -1) || (gpGlobals->time > (this->mTimeLastHintUpdate + kHintUpdateInterval))) { this->mTimeLastHintUpdate = gpGlobals->time; float kHintInterval = 20.0f; if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) { kHintInterval = 30.0f; } if((this->mTimeOfLastTeamHint == -1) || (gpGlobals->time > (this->mTimeOfLastTeamHint + kHintInterval))) { bool thePlayedHint = false; // Update commander hints AvHPlayer* theCommanderPlayer = this->GetCommanderPlayer(); if(theCommanderPlayer) { const float kGiveOrdersHintDelay = 20.0f; float theTimeAsCommander = gpGlobals->time - theCommanderPlayer->GetTimeStartedTopDown(); if(theTimeAsCommander > kGiveOrdersHintDelay) { // Remove warning because tech tree doesn't currently require it //// Do we have zero infantry portals, and we haven't played "need infantry portal" sound for awhile //bool theTeamHasLiveInfantryPortal = false; // //FOR_ALL_ENTITIES(kwsInfantryPortal, AvHInfantryPortal*) // if(theEntity->pev->team == this->mTeamNumber) // { // if(theEntity->GetIsBuilt()) // { // theTeamHasLiveInfantryPortal = true; // break; // } // } //END_FOR_ALL_ENTITIES(kwsInfantryPortal); // //if(!theTeamHasLiveInfantryPortal) //{ // // Play "need infantry portal" sound // theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_NEEDPORTAL); // thePlayedHint = true; //} if(!thePlayedHint) { // Did commander just join and hasn't given any orders? Play "select your troops and give them orders". if(!theCommanderPlayer->GetHasGivenOrder()) { theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_GIVEORDERS); thePlayedHint = true; } } if(!thePlayedHint) { // If we just played an alert, tell them how to go to it (but only tell every commander once) const float kMinAlertNotifyInterval = 5.0f; float theTimeOfLastAudioAlert = -1; const AvHAlert* theLastAlert = this->GetLastAudioAlert(); if(theLastAlert) { theTimeOfLastAudioAlert = theLastAlert->GetTime(); } if((theTimeOfLastAudioAlert == -1) || ((gpGlobals->time - theTimeOfLastAudioAlert) < kMinAlertNotifyInterval)) { int theCurrentCommander = theCommanderPlayer->entindex(); if(std::find(this->mCommandersToldAboutJumpKey.begin(), this->mCommandersToldAboutJumpKey.end(), theCurrentCommander) == this->mCommandersToldAboutJumpKey.end()) { theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_GOTOALERT); this->mCommandersToldAboutJumpKey.push_back(theCurrentCommander); } } } // Detect shell-shocked commander. Play "do something" sound. if(!thePlayedHint) { const float kShellShockedInterval = 130.0f; float theTimeOfLastSignificantCommanderAction = theCommanderPlayer->GetTimeOfLastSignificantCommanderAction(); float theTimeSinceLastSignificantAction = gpGlobals->time - theTimeOfLastSignificantCommanderAction; if((theTimeOfLastSignificantCommanderAction == -1) || (theTimeSinceLastSignificantAction > kShellShockedInterval)) { theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_COMMANDERIDLE); thePlayedHint = true; } } } } // Update alien team hints if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { int theNumBuilders = 0; int theNumAliveTeamMembers = 0; // If we have no builders, tell the team FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) if(theEntity->GetIsRelevant() && (theEntity->pev->team == this->mTeamNumber)) { // If we have builders, or players that are evolving to builders... if(theEntity->GetUser3() == AVH_USER3_ALIEN_PLAYER2) { theNumBuilders++; } else if(theEntity->GetEvolution() == ALIEN_LIFEFORM_TWO) { theNumBuilders++; } else if((theEntity->GetUser3() == AVH_USER3_ALIEN_EMBRYO) && (theEntity->GetPreviousUser3() == AVH_USER3_ALIEN_PLAYER2)) { AvHMessageID theEvolution = theEntity->GetEvolution(); switch(theEvolution) { case ALIEN_EVOLUTION_ONE: case ALIEN_EVOLUTION_TWO: case ALIEN_EVOLUTION_THREE: case ALIEN_EVOLUTION_SEVEN: case ALIEN_EVOLUTION_EIGHT: case ALIEN_EVOLUTION_NINE: case ALIEN_EVOLUTION_TEN: case ALIEN_EVOLUTION_ELEVEN: case ALIEN_EVOLUTION_TWELVE: theNumBuilders++; break; } } if(theEntity->IsAlive()) { theNumAliveTeamMembers++; } } END_FOR_ALL_ENTITIES(kAvHPlayerClassName); // Only play if there is more then one person alive on team (player could be last one left or could be saving up) if((theNumBuilders == 0) && (theNumAliveTeamMembers > 1)) { this->PlayHUDSoundForAlivePlayers(HUD_SOUND_ALIEN_NEED_BUILDERS); thePlayedHint = true; } // Now, fun easter eggs, but not in tourny mode if(!thePlayedHint && !GetGameRules()->GetIsTournamentMode() && this->mPlayFunSoundsThisGame) { // If a hive is under attack and the marines are way ahead, play "game over man" AvHHive* theRandomHive = AvHSUGetRandomActiveHive(this->mTeamNumber); if(theRandomHive) { const float kRadius = 600; int theClutterEntities = 0; int theDeadTeammates = 0; int theDeadPlayers = 0; CBaseEntity* theEntity = NULL; while((theEntity = UTIL_FindEntityInSphere(theEntity, theRandomHive->pev->origin, kRadius)) != NULL) { // If there are a ton of dead alien players, sometimes play "we need better players" if(theEntity->IsPlayer() && !theEntity->IsAlive()) { if(theEntity->pev->team == this->mTeamNumber) { theDeadTeammates++; } theDeadPlayers++; } else { // If there are tons of aliens buildings nearby, play "place is a mess" sound for all aliens nearby AvHBaseBuildable* theBuildable = dynamic_cast<AvHBaseBuildable*>(theEntity); CBasePlayerItem* theItem = dynamic_cast<CBasePlayerItem*>(theEntity); if(theBuildable || theItem) { theClutterEntities++; } } } if((theDeadPlayers >= 14) && !this->mPlayedSeeDeadPeople) { EMIT_SOUND(theRandomHive->edict(), CHAN_AUTO, kAlienSeeDead, 1.0f, ATTN_NORM); this->mPlayedSeeDeadPeople; thePlayedHint = true; } else if(theDeadTeammates >= 8) { this->PlayFunHUDSoundOnce(HUD_SOUND_ALIEN_NEED_BETTER); thePlayedHint = true; } else if(theClutterEntities >= 25) { this->PlayFunHUDSoundOnce(HUD_SOUND_ALIEN_MESS); thePlayedHint = true; } } // If aliens have all three hives and all upgrades, play "donce" if(this->GetNumActiveHives() == kMaxHives) { if(this->GetAlienUpgrades().size() >= kMaxUpgradeCategories*kMaxUpgradeLevel) { this->PlayFunHUDSoundOnce(HUD_SOUND_ALIEN_NOW_DONCE); thePlayedHint = true; } } // If there are tons of bodies near a hive, play "I see dead people" } } if(thePlayedHint) { this->mTimeOfLastTeamHint = gpGlobals->time; } } } } } void AvHTeam::UpdateCommanderScore() { // The commander score is the average of his team score const float kUpdateScoreTime = 2.0f; float theCurrentTime = gpGlobals->time; int theCommander = this->GetCommander(); if(theCommander > 0) { if((this->mLastCommandScoreUpdateTime == -1) || (theCurrentTime > this->mLastCommandScoreUpdateTime + kUpdateScoreTime)) { int theScore = 0; int theNumPlayers = 0; // Run through players on team and average score FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) // For each player, update alien inventory with this # of hives if(theEntity->GetTeam() == this->GetTeamNumber()) { if(theEntity->entindex() != theCommander) { theScore += theEntity->GetScore(); theNumPlayers++; } } END_FOR_ALL_ENTITIES(kAvHPlayerClassName); int theCommanderScore = (int)((theScore/(float)theNumPlayers) + .5f); AvHPlayer* theCommanderPlayer = this->GetCommanderPlayer(); if(theCommanderPlayer) { theCommanderPlayer->SetScore(theCommanderScore); } this->mLastCommandScoreUpdateTime = theCurrentTime; } } } void AvHTeam::UpdateInventoryEnabledState() { // Count # of hives int theNumHives = this->GetNumActiveHives(); // Loop through players on team FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) // For each player, update alien inventory with this # of hives if(theEntity->GetTeam() == this->GetTeamNumber()) { if(GetGameRules()->GetIsCombatMode()) { theNumHives = 1; MessageIDListType& thePurchasedCombatUpgrades = theEntity->GetPurchasedCombatUpgrades(); MessageIDListType::iterator theIter = std::find(thePurchasedCombatUpgrades.begin(), thePurchasedCombatUpgrades.end(), ALIEN_HIVE_TWO_UNLOCK); //AvHTechNode theTechNode; //if(theEntity->GetCombatNodes().GetTechNode(ALIEN_HIVE_TWO_UNLOCK, theTechNode) && theTechNode.GetIsResearched()) if(theIter != thePurchasedCombatUpgrades.end()) { theNumHives++; //if(theEntity->GetCombatNodes().GetTechNode(ALIEN_HIVE_THREE_UNLOCK, theTechNode) && theTechNode.GetIsResearched()) theIter = std::find(thePurchasedCombatUpgrades.begin(), thePurchasedCombatUpgrades.end(), ALIEN_HIVE_THREE_UNLOCK); if(theIter != thePurchasedCombatUpgrades.end()) { theNumHives++; } } } theEntity->UpdateInventoryEnabledState(theNumHives); } END_FOR_ALL_ENTITIES(kAvHPlayerClassName) } void AvHTeam::UpdateReinforcementWave() { int theNumPlayersOnTeam = this->GetPlayerCount(); int theNumDeadPlayers = this->GetPlayerCount(true); int theWaveSize = AvHSUCalcCombatSpawnWaveSize(theNumPlayersOnTeam, theNumDeadPlayers); // If we're in combat mode if(GetGameRules()->GetIsCombatMode() && (GetGameRules()->GetVictoryTeam() == TEAM_IND) && !GetGameRules()->GetIsIronMan()) { // If reinforcement wave hasn't started if(this->mTimeReinforcementWaveComplete == -1) { // If team has at least one dead player, start wave for(EntityListType::const_iterator theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) { const AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); ASSERT(thePlayer); float theWaveTime = AvHSUCalcCombatSpawnTime(this->mTeamType, theNumPlayersOnTeam, theNumDeadPlayers, 0); switch(thePlayer->GetPlayMode()) { case PLAYMODE_AWAITINGREINFORCEMENT: case PLAYMODE_REINFORCING: // Set time reinforcement is complete this->mTimeReinforcementWaveComplete = gpGlobals->time + theWaveTime; break; } } } else { // Spawn back in a max of X players per wave (1/4 rounded up) int theNumRespawning = 0; EntityListType::const_iterator theIter; // Count number of people currently respawning for(theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) { AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); ASSERT(thePlayer); if(thePlayer->GetPlayMode() == PLAYMODE_REINFORCING) { theNumRespawning++; } } // Find the player that's been waiting the longest if(theNumRespawning < theWaveSize) { EntityListType::const_iterator theLongestWaitingPlayer = this->mPlayerList.end(); float theLongestWaitingTime = -1; // Loop through players for(theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) { // While we don't allow any more to repsawn or we hit end of list AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); ASSERT(thePlayer); if(thePlayer->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) { float thePlayerWaitTime = (gpGlobals->time - thePlayer->GetTimeLastPlaying()); if((theLongestWaitingTime == -1) || (thePlayerWaitTime > theLongestWaitingTime)) { theLongestWaitingPlayer = theIter; theLongestWaitingTime = thePlayerWaitTime; } } } if(theLongestWaitingPlayer != this->mPlayerList.end()) { AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theLongestWaitingPlayer); ASSERT(thePlayer); thePlayer->SetPlayMode(PLAYMODE_REINFORCING); } } // Respawn players when wave is complete if(gpGlobals->time > this->mTimeReinforcementWaveComplete) { for(theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) { // While we don't allow any more to repsawn or we hit end of list AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); ASSERT(thePlayer); if(thePlayer->GetPlayMode() == PLAYMODE_REINFORCING) { thePlayer->SetPlayMode(PLAYMODE_PLAYING); } } // Reset wave this->mTimeReinforcementWaveComplete = -1; } } } } void AvHTeam::UpdateOrders() { // For each order in our list for(OrderListType::iterator theOrderIter = this->mOrderList.begin(); theOrderIter != this->mOrderList.end(); /* no increment*/) { ASSERT(theOrderIter->GetReceiver() != -1 ); if(theOrderIter->Update()) { bool theOrderCancelled = theOrderIter->GetOrderCancelled(); if(!theOrderCancelled) { EntityInfo theReceiver = theOrderIter->GetReceiver(); if(theReceiver != -1 ) { GetGameRules()->TriggerAlert(this->mTeamNumber, ALERT_ORDER_COMPLETE, theReceiver); } } } // Have any orders been completed for a bit? const float kExpireTime = 1.0f; if(!theOrderIter->GetOrderActive() && (theOrderIter->GetTimeOrderCompleted() != -1) && (gpGlobals->time > (theOrderIter->GetTimeOrderCompleted() + kExpireTime))) { this->mOrderList.erase(theOrderIter); } else { theOrderIter++; } // Is the order complete? // for(OrderListType::iterator theOrderIter = this->mOrderList.begin(); theOrderIter != this->mOrderList.end(); /* no increment */) // { // // Is the order complete? // bool theOrderCancelled = false; // if(theOrderIter->GetIsComplete(&theOrderCancelled)) // { // if(!theOrderCancelled) // { // // For each receiver // EntityListType theReceivers = theOrderIter->GetReceivers(); // for(EntityListType::iterator theReceiverIter = theReceivers.begin(); theReceiverIter != theReceivers.end(); theReceiverIter++) // { // // Get player. This fails occasionally, due to selection issues (ie, a building or other non-player entity will be in here) // AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theReceiverIter))); // //ASSERT(thePlayer); // if(thePlayer) // { // // Set the order complete // thePlayer->PlayOrderComplete(); // } // } // // // Set order complete for commander // int theCommanderIndex = this->GetCommander(); // if(theCommanderIndex >= 0) // { // AvHPlayer* theCommander = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theCommanderIndex))); // if(theCommander) // { // theCommander->PlayOrderComplete(); // } // } // } // // // Set the receivers to none because it's no longer relevant // AvHOrder theCompletedOrder(*theOrderIter); // theCompletedOrder.SetOrderCompleted(); // // this->SetOrder(theCompletedOrder); // } // else // { // theOrderIter++; // } } } //void AvHTeam::UpdateReinforcements() //{ // // If this team is alien // if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) // { // // For each hive on this team // FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) // if(AvHTeamNumber(theEntity->pev->team) == this->mTeamNumber) // { // // Update reinforcements // theEntity->UpdateReinforcements(); // } // END_FOR_ALL_ENTITIES(kesTeamHive); // } // // else if it's marine // else if(this->mTeamType == AVH_CLASS_TYPE_MARINE) // { // // For each infantry portal on this team // FOR_ALL_ENTITIES(kwsInfantryPortal, AvHInfantryPortal*) // if(AvHTeamNumber(theEntity->pev->team) == this->mTeamNumber) // { // // Update reinforcements // theEntity->UpdateReinforcements(); // } // END_FOR_ALL_ENTITIES(kwsInfantryPortal); // } // else // { // ASSERT(false); // } //} void AvHTeam::UpdateResearch() { this->mResearchManager.UpdateResearch(); this->UpdateMenuTechSlots(); } void AvHTeam::UpdateResources() { const AvHGameplay& theGameplay = GetGameRules()->GetGameplay(); float kResourceUpdateInterval = theGameplay.GetTowerInjectionTime(); if(GetGameRules()->GetIsHamboneMode()) { kResourceUpdateInterval /= 2.0f; } // Change here if teams should always get at least one resource float theResourcesGathered = 0.0f; float theCurrentTime = gpGlobals->time; if((this->mLastResourceUpdateTime == -1) || (theCurrentTime > (this->mLastResourceUpdateTime + kResourceUpdateInterval))) { // Add resources for each built-on resource on the team for(EntityListType::iterator theResourceTowerIter = this->mResourceTowerList.begin(); theResourceTowerIter != this->mResourceTowerList.end(); /* nothing */) { CBaseEntity* theResourceTowerEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theResourceTowerIter)); AvHResourceTower* theResourceTower = dynamic_cast<AvHResourceTower*>(theResourceTowerEntity); if(theResourceTower && (theResourceTower->pev->team == this->mTeamNumber)) { if(theResourceTower->GetIsActive()) { // Has enough time elapsed? float theLastTimeResourcesContributed = theResourceTower->GetTimeLastContributed(); float theCurrentTime = gpGlobals->time; if(theCurrentTime > (theLastTimeResourcesContributed + kResourceUpdateInterval)) { // Take into account upgrade level stored in tower float theTowerContribution = theGameplay.GetTowerInjectionAmount(); theResourcesGathered += theTowerContribution; // Decrement the amount of points the resources has theResourceTower->SetTimeLastContributed(theCurrentTime); AvHSUPlayNumericEventAboveStructure(theTowerContribution, theResourceTower); } } theResourceTowerIter++; } else { // Remove it from team also theResourceTowerIter = this->mResourceTowerList.erase(theResourceTowerIter); } } // If we're marines, add it to the team total if(this->mTeamType == AVH_CLASS_TYPE_MARINE) { this->SetTeamResources(this->GetTeamResources() + theResourcesGathered); } else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) { // Else dump it into the pool, and split the whole pool up among all players on our team float theTeamResources = this->GetTeamResources() + theResourcesGathered; int theNumPlayers = this->GetPlayerCount(); float theAmountForPlayer = theTeamResources/(float)theNumPlayers; // Clear resources, assuming all res will be given out. Anything over will gob ack into team pool this->SetTeamResources(0.0f); for(EntityListType::iterator thePlayerIter = this->mPlayerList.begin(); thePlayerIter != this->mPlayerList.end(); thePlayerIter++) { AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*thePlayerIter))); ASSERT(thePlayer); // If the amount goes over the player's max, it goes back into team resources float theResourcesBefore = thePlayer->GetResources(); thePlayer->SetResources(thePlayer->GetResources() + theAmountForPlayer); } } // Increment total resources gathered this->AddResourcesGathered(theResourcesGathered); this->mLastResourceUpdateTime = theCurrentTime; } } AvHServerPlayerData* AvHTeam::GetServerPlayerData(edict_t* inEdict) { string theNetworkID = AvHNexus::getNetworkID(inEdict); return &this->mServerPlayerData[theNetworkID]; } // :puzl void AvHTeam::UpdateServerPlayerData() { const float kServerPlayerDataUpdateInterval = 1.0f; float theCurrentTime = gpGlobals->time; if((this->mLastServerPlayerDataUpdateTime == -1) || (theCurrentTime > (this->mLastServerPlayerDataUpdateTime + kServerPlayerDataUpdateInterval))) { // Update all settings to remember keyed to their WONid FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(theEntity->edict()); if(theServerPlayerData) { // Expire any commander bans float theCommanderVoteDownTime = avh_votedowntime.value; float theTimeVotedDown = theServerPlayerData->GetTimeVotedDown()*60; if(theTimeVotedDown > 0) { if(gpGlobals->time > (theTimeVotedDown + theCommanderVoteDownTime)) { theServerPlayerData->SetTimeVotedDown(-1); } } } END_FOR_ALL_ENTITIES(kAvHPlayerClassName) } } void AvHTeam::UpdateMenuTechSlots() { if(this->mTeamType == AVH_CLASS_TYPE_MARINE) { // Update commander menus. Get tech slots for each menu and add them all in one 32 long list this->mMenuTechSlots = 0; AvHGamerules* theGameRules = GetGameRules(); const AvHTeam* theTeam = theGameRules->GetTeam(this->mTeamNumber); if(theTeam) { for(int j = 0; j < 4; j++) { AvHUser3 theUser3 = AVH_USER3_NONE; int theBaseIndex = 0; switch(j) { case 0: theUser3 = AVH_USER3_MENU_BUILD; theBaseIndex = 0; break; case 1: theUser3 = AVH_USER3_MENU_BUILD_ADVANCED; theBaseIndex = 8; break; case 2: theUser3 = AVH_USER3_MENU_ASSIST; theBaseIndex = 16; break; case 3: theUser3 = AVH_USER3_MENU_EQUIP; theBaseIndex = 24; break; } // For each slot, see if tech is available AvHTechSlots theTechSlots; if(theTeam->GetTechSlotManager().GetTechSlotList(theUser3, theTechSlots)) { // Store results in full 32-bits of iuser1 for(int i = 0; i < kNumTechSlots; i++) { int theCurrentMask = 1 << (theBaseIndex + i); AvHMessageID theMessage = theTechSlots.mTechSlots[i]; if(theMessage != MESSAGE_NULL) { if(this->GetIsTechnologyAvailable(theMessage)) { this->mMenuTechSlots |= theCurrentMask; } } } } } } } } bool AvHTeam::GetIsTechnologyAvailable(AvHMessageID inMessageID) const { bool theTechnologyAvailable = this->GetResearchManager().GetIsMessageAvailable(inMessageID); return theTechnologyAvailable; } bool AvHTeam::GetLastAlert(AvHAlert& outAlert, bool inClearAlert, bool inAnyMessage, AvHMessageID* ioMessageID) { float theLastAlertTime = -1; bool theSuccess = false; // Look for last alert AlertListType::iterator theLastAlertIter; AvHMessageID theOutputMessageID = MESSAGE_NULL; for(MessageAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); theIter++) { if(inAnyMessage || (ioMessageID && (theIter->first == *ioMessageID))) { for(AlertListType::iterator theAlertIter = theIter->second.begin(); theAlertIter != theIter->second.end(); theAlertIter++) { float theCurrentTime = theAlertIter->GetTime(); if(theCurrentTime > theLastAlertTime) { theLastAlertIter = theAlertIter; theLastAlertTime = theCurrentTime; theOutputMessageID = theIter->first; theSuccess = true; } } } } // If we found it, set the alert if(theSuccess) { outAlert = *theLastAlertIter; if(ioMessageID) { *ioMessageID = theOutputMessageID; } } // Clear the alert if specified if(inClearAlert) { for(MessageAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); theIter++) { for(AlertListType::iterator theAlertIter = theIter->second.begin(); theAlertIter != theIter->second.end(); theAlertIter++) { if(theAlertIter == theLastAlertIter) { theIter->second.erase(theLastAlertIter); break; } } } } return theSuccess; } float AvHTeam::GetAudioAlertInterval() const { float theAudioAlertInterval = BALANCE_VAR(kAudioAlertInterval); return theAudioAlertInterval; } void AvHTeam::AddAlert(const AvHAlert& inAlert, AvHMessageID inMessageID) { this->mAlertList[inMessageID].push_back(inAlert); } const AvHAlert* AvHTeam::GetLastAudioAlert() const { // Look for last audio alert const AvHAlert* theLastAudioAlert = NULL; float theLastTime = -1; for(MessageAlertListType::const_iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); theIter++) { for(AlertListType::const_iterator theAlertIter = theIter->second.begin(); theAlertIter != theIter->second.end(); theAlertIter++) { if(theAlertIter->GetPlayedAudio()) { float theCurrentTime = theAlertIter->GetTime(); if(theCurrentTime > theLastTime) { theLastAudioAlert = &(*theAlertIter); theLastTime = theCurrentTime; } } } } return theLastAudioAlert; } AlertListType AvHTeam::GetAlerts(AvHMessageID inMessageID) { return this->mAlertList[inMessageID]; } void AvHTeam::UpdateAlerts() { const float theAlertDuration = BALANCE_VAR(kAlertExpireTime); #ifdef AVH_PLAYTEST_BUILD // Run through our team, and alert everyone if there is a player that's no longer playing or on the server for(EntityListType::const_iterator thePlayerIter = this->mPlayerList.begin(); thePlayerIter != this->mPlayerList.end(); thePlayerIter++) { int thePlayerIndex = *thePlayerIter; bool theSendAlert = false; char theErrorMessage[512]; AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*thePlayerIter))); if(!thePlayer) { theSendAlert = true; strcpy(theErrorMessage, "NULL player"); } else { AvHPlayMode thePlayMode = thePlayer->GetPlayMode(); if((thePlayMode != PLAYMODE_PLAYING) && (thePlayMode != PLAYMODE_AWAITINGREINFORCEMENT) && (thePlayMode != PLAYMODE_REINFORCING)) { theSendAlert = true; sprintf(theErrorMessage, "Invalid play mode: %d\n", thePlayMode); } else if(AvHTeamNumber(thePlayer->pev->team) != this->mTeamNumber) { theSendAlert = true; sprintf(theErrorMessage, "on wrong team (%d instead of %d)", thePlayer->pev->team, this->mTeamNumber); } } if(theSendAlert) { const char* thePlayerName = "<null>"; thePlayerName = STRING(thePlayer->pev->netname); char theFullErrorMesssage[512]; sprintf(theFullErrorMesssage, "Invalid player bug detected <\"%s\">: %d", thePlayerName, theErrorMessage); UTIL_SayTextAll(theFullErrorMesssage, thePlayer); } } #endif // Run through alerts, and expire any past a certain age for(MessageAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); ++theIter) { AlertListType& theAlertList = theIter->second; for(AlertListType::iterator theAlertIter = theAlertList.begin(); theAlertIter != theAlertList.end(); /* no inc */) { float theAlertTime = theAlertIter->GetTime(); if(gpGlobals->time > (theAlertTime + theAlertDuration)) { theAlertIter = theAlertList.erase(theAlertIter); } else { theAlertIter++; } } } } EntityListType AvHTeam::GetGroup(int inGroupNumber) { ASSERT(inGroupNumber >= 0); ASSERT(inGroupNumber < kNumHotkeyGroups); return this->mGroups[inGroupNumber]; } void AvHTeam::SetGroup(int inGroupNumber, EntityListType& inEntityListType) { // Set group ASSERT(inGroupNumber >= 0); ASSERT(inGroupNumber < kNumHotkeyGroups); this->mGroups[inGroupNumber] = inEntityListType; // Update group type AvHUser3 theUser3 = AvHSUGetGroupTypeFromSelection(this->mGroups[inGroupNumber]); this->mGroupTypes[inGroupNumber] = theUser3; } AvHUser3 AvHTeam::GetGroupType(int inGroupNumber) { ASSERT(inGroupNumber >= 0); ASSERT(inGroupNumber < kNumHotkeyGroups); return this->mGroupTypes[inGroupNumber]; } EntityListType AvHTeam::GetSelectAllGroup() { return this->mSelectAllGroup; } void AvHTeam::SetSelectAllGroup(EntityListType& inGroup) { this->mSelectAllGroup = inGroup; }