diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/tf_mapitems.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/tf/tf_mapitems.cpp')
| -rw-r--r-- | game/server/tf/tf_mapitems.cpp | 3127 |
1 files changed, 3127 insertions, 0 deletions
diff --git a/game/server/tf/tf_mapitems.cpp b/game/server/tf/tf_mapitems.cpp new file mode 100644 index 0000000..f24f33f --- /dev/null +++ b/game/server/tf/tf_mapitems.cpp @@ -0,0 +1,3127 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "tf_mapitems.h" +#include "tf_shareddefs.h" +#include "tf_player.h" +#include "tf_gamerules.h" +#include "tf_timer.h" +#include "tf_team.h" + + +#if 0 + +bool ActivateDoResults(CTFGoal *Goal, CTFPlayer *AP, CTFGoal *ActivatingGoal); +bool ActivationSucceeded(CTFGoal *Goal, CTFPlayer *AP, CTFGoal *ActivatingGoal); +void DoResults(CTFGoal *Goal, CTFPlayer *AP, BOOL bAddBonuses); + + +// ---------------------------------------------------------------------------------------- // +// Global helpers. +// ---------------------------------------------------------------------------------------- // + +const char* GetTeamName( int iTeam ) +{ +// if ( iTeam == 0 ) +// { +// return "SPECTATOR"; +// } +// else + { + CTeam *pTeam = GetGlobalTeam( iTeam ); + if ( pTeam ) + { + return pTeam->GetName(); + } + else + { + return "UNKNOWN TEAM"; + } + } +} + + +//=========================================== +int GetTeamCheckTeam( const char *pTargetName ) +{ + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, pTargetName ); + if ( pEntity ) + { + if ( !strcmp( pEntity->GetClassname(), "info_tf_teamcheck" ) ) + return pEntity->GetTeamNumber(); + } + + return 0; +} + + +//========================================================================= +// Displays the state of a GoalItem +void DisplayItemStatus(CTFGoal *Goal, CTFPlayer *Player, CTFGoalItem *Item) +{ + MDEBUG( Msg( "Displaying Item Status\nItem goal_no : %d\n", Item->goal_no) ); + + // If we have a teamcheck entity, use it instead + if ( Item->owned_by_teamcheck != NULL_STRING ) + Item->owned_by = GetTeamCheckTeam( STRING(Item->owned_by_teamcheck) ); + + if (Item->goal_state == TFGS_ACTIVE) + { + MDEBUG( Msg( " Item is ACTIVE\n") ); + + if ( (Goal->team_str_carried != NULL_STRING) || (Goal->non_team_str_carried != NULL_STRING) ) + { + CBaseEntity *pOwner = Item->GetOwnerEntity(); + + if (Player->GetTeamNumber() == Item->owned_by) + { + if (Player == Item->GetOwnerEntity()) + ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->team_str_carried), "you" ); + else + ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->team_str_carried), STRING(pOwner->GetEntityName()) ); + } + else + { + if (Player == Item->GetOwnerEntity()) + ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->non_team_str_carried), "you" ); + else + ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->non_team_str_carried), STRING(pOwner->GetEntityName()) ); + } + } + } + else if (Item->GetAbsOrigin() != Item->oldorigin) + { + MDEBUG( Msg( " Item has MOVED\n") ); + + if ( (Goal->team_str_moved != NULL_STRING) || (Goal->non_team_str_moved != NULL_STRING) ) + { + if (Player->GetTeamNumber() == Item->owned_by) + ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->team_str_moved) ); + else + ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->non_team_str_moved) ); + } + } + else + { + MDEBUG( Msg( " Item is AT HOME\n") ); + + if ( Goal->team_str_home != NULL_STRING || Goal->non_team_str_home != NULL_STRING ) + { + if (Player->GetTeamNumber() == Item->owned_by) + ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->team_str_home) ); + else + ClientPrint( Player, HUD_PRINTTALK, STRING(Goal->non_team_str_home) ); + } + } +} + +#if 0 +//========================================================================= +// Inactivates a Teamspawn point +void InactivateSpawn(CTFSpawn *Spawn) +{ + Spawn->goal_state = TFGS_REMOVED; +} + +//========================================================================= +// Activates a Teamspawn point +void ActivateSpawn(CTFSpawn *Spawn) +{ + Spawn->goal_state = TFGS_INACTIVE; +} +#endif + +//========================================================================= +// Increase the score of a team +void TeamFortress_TeamIncreaseScore(int tno, int scoretoadd) +{ + if ( tno == 0 ) + return; + + CTeam *pTeam = GetGlobalTeam( tno ); + if ( !pTeam ) + return; + + pTeam->AddScore( scoretoadd ); +} + +// Returns true if the AP's carrying at least 1 of the items in the group +bool HasItemFromGroup( CBaseEntity *AP, int iGroupNo ) +{ + // Find all items in the group + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" ); + while ( pEnt ) + { + CTFGoalItem *pGoal = dynamic_cast<CTFGoalItem*>( pEnt ); + if ( (pGoal->group_no == iGroupNo) && (pGoal->GetOwnerEntity() == AP) ) + return true; + + pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" ); + } + + return false; +} + +//========================================================================= +// Returns true if all the goals in the specified group are in the specified state +bool AllGoalsInState( int iGroupNo, int iState ) +{ + // Find all goals in the group + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" ); + while ( pEnt ) + { + CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt ); + if ( pGoal ) + { + if (pGoal->group_no == iGroupNo) + { + // All Goals in the group must be in the specified state + if (pGoal->goal_state != iState) + return false; + } + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" ); + } + + return true; +} + + +// Return the item with a goal_no equal to ino +CTFGoalItem* Finditem(int ino) +{ + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" ); + while ( pEnt ) + { + CTFGoalItem *pGoal = dynamic_cast<CTFGoalItem*>( pEnt ); + if (pGoal && pGoal->goal_no == ino) + return pGoal; + + pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" ); + } + + // Goal does not exist + Warning("Could not find an item with a goal_no of %d.\n", ino); + return NULL; +} + + +//========================================================================= +// Return the TeamSpawn with a goal_no equal to gno +CTFSpawn* Findteamspawn(int gno) +{ + // Search by netname + //TFTODO: I think FindEntityByClassname will do the same thing. + //CBaseEntity *pEnt = UTIL_FindEntityByString( NULL, "netname", "info_player_teamspawn" ); + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_player_teamspawn" ); + while ( pEnt ) + { + CTFSpawn *pSpawn = dynamic_cast<CTFSpawn*>( pEnt ); + if ( pSpawn ) + { + if (pSpawn->goal_no == gno) + return pSpawn; + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_player_teamspawn" ); + } + + // Goal does not exist + Warning("Could not find a Teamspawn with a goal_no of %d.\n", gno); + return NULL; +} + + +//========================================================================= +// Return the goal with a goal_no equal to gno +CTFGoal* Findgoal(int gno) +{ + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" ); + while ( pEnt ) + { + CTFGoal *pGoal = dynamic_cast<CTFGoal*>( pEnt ); + if (pGoal && pGoal->goal_no == gno) + return pGoal; + + pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" ); + } + + // Goal does not exist + Warning("Could not find a goal with a goal_no of %d.\n", gno); + return NULL; +} + + +//========================================================================= +// Remove a Timer/Goal +void RemoveGoal(CTFGoal *pGoal) +{ + pGoal->AddSolidFlags( FSOLID_NOT_SOLID ); + pGoal->goal_state = TFGS_REMOVED; + pGoal->AddEffects( EF_NODRAW ); +} + + +//========================================================================= +// Return true if the player meets the AP criteria +bool APMeetsCriteria(CTFGoal *Goal, CTFPlayer *AP) +{ + MDEBUG(Warning("==========================\n")); + MDEBUG(Warning("AP Criteria Checking\n")); + MDEBUG(Warning(UTIL_VarArgs("Goal: %s", STRING(Goal->edict()->netname)))); + + CTFGoal *pGoal; + CTFGoalItem *pItem; + + if (AP != NULL && AP->Classify() == CLASS_PLAYER) + { + MDEBUG(Warning(UTIL_VarArgs("\nAP : %s\n", AP->GetPlayerName()))); + + // If a player of a specific team can only activate this + if (Goal->GetTeamNumber()) + { + MDEBUG(Warning(" Checking team.")); + if (Goal->GetTeamNumber() != AP->GetTeamNumber()) + return false; + if ( !AP->IsAlive() ) // don't want dead or dying players activating this + return false; + MDEBUG(Warning("passed.\n")); + } + + // If a player in a team specified by a teamcheck entity can activate this + if (Goal->teamcheck != NULL_STRING) + { + MDEBUG(Warning(" Checking teamcheck entity.")); + + if ( AP->GetTeamNumber() != GetTeamCheckTeam( STRING(Goal->teamcheck) ) ) + return false; + + MDEBUG(Warning(" passed.\n")); + } + + // If a player of a specific class can only activate this + if (Goal->playerclass) + { + MDEBUG(Warning(" Checking class.")); + CTFPlayerClass *pPlayerClass = AP->GetPlayerClass(); + if ( pPlayerClass && !pPlayerClass->IsClass( Goal->playerclass ) ) + return false; + MDEBUG(Warning("passed.\n")); + } + + // If this activation needs a GoalItem, make sure the player has it + if (Goal->items_allowed) + { + MDEBUG(Warning(" Checking items.")); + pItem = Finditem(Goal->items_allowed); + if (!pItem) + return false; + if (pItem->GetOwnerEntity() != AP) + return false; + MDEBUG(Warning("passed.\n")); + } + } + + // Check Goal states + if (Goal->if_goal_is_active) + { + MDEBUG(Warning(" Checking if_goal_is_active.")); + pGoal = Findgoal(Goal->if_goal_is_active); + if (!pGoal) + return false; + if (pGoal->goal_state != TFGS_ACTIVE) + return false; + MDEBUG(Warning("passed.\n")); + } + + if (Goal->if_goal_is_inactive) + { + MDEBUG(Warning(" Checking if_goal_is_inactive.")); + pGoal = Findgoal(Goal->if_goal_is_inactive); + if (!pGoal) + return false; + if (pGoal->goal_state != TFGS_INACTIVE) + return false; + MDEBUG(Warning("passed.\n")); + } + + if (Goal->if_goal_is_removed) + { + MDEBUG(Warning(" Checking if_goal_is_removed.")); + pGoal = Findgoal(Goal->if_goal_is_removed); + if (!pGoal) + return false; + if (pGoal->goal_state != TFGS_REMOVED) + return false; + MDEBUG(Warning("passed.\n")); + } + + // Check Group States + if (Goal->if_group_is_active) + { + MDEBUG(Warning(" Checking if_group_is_active.")); + if ( !AllGoalsInState(Goal->if_group_is_active, TFGS_ACTIVE) ) + return false; + MDEBUG(Warning("passed.\n")); + } + + if (Goal->if_group_is_inactive) + { + MDEBUG(Warning(" Checking if_group_is_inactive.")); + if ( !AllGoalsInState(Goal->if_group_is_inactive, TFGS_INACTIVE) ) + return false; + MDEBUG(Warning("passed.\n")); + } + + if (Goal->if_group_is_removed) + { + MDEBUG(Warning(" Checking if_group_is_removed.")); + if ( !AllGoalsInState(Goal->if_group_is_removed, TFGS_REMOVED) ) + return false; + MDEBUG(Warning("passed.\n")); + } + + // Check Item States + if (Goal->if_item_has_moved) + { + MDEBUG(Warning(" Checking if_item_has_moved.")); + // Find the item + pItem = Finditem(Goal->if_item_has_moved); + if (!pItem) + return false; + if (pItem->goal_state != TFGS_ACTIVE && pItem->GetAbsOrigin() == pItem->oldorigin) + return false; + MDEBUG(Warning("passed.\n")); + } + + if (Goal->if_item_hasnt_moved) + { + MDEBUG(Warning(" Checking if_item_hasnt_moved.")); + // Find the item + pItem = Finditem(Goal->if_item_hasnt_moved); + if (!pItem) + return false; + if (pItem->goal_state == TFGS_ACTIVE || pItem->GetAbsOrigin() != pItem->oldorigin ) + return false; + MDEBUG(Warning("passed.\n")); + } + + // Check Items being carried + if (AP != NULL && AP->Classify() == CLASS_PLAYER) + { + if (Goal->has_item_from_group) + { + MDEBUG(Warning(" Checking has_item_from_group.")); + if ( !HasItemFromGroup(AP, Goal->has_item_from_group) ) + return false; + MDEBUG(Warning("passed.\n")); + } + + if (Goal->hasnt_item_from_group) + { + MDEBUG(Warning(" Checking hasnt_item_from_group.")); + if ( HasItemFromGroup(AP, Goal->hasnt_item_from_group) ) + return false; + MDEBUG(Warning("passed.\n")); + } + } + + MDEBUG(Warning("Criteria passed.\n")); + return true; +} + + +//========================================================================= +// Return true if the Entity should activate +bool ShouldActivate(CTFGoal *Goal, CTFPlayer *AP) +{ +#ifdef MAP_DEBUG + Warning(UTIL_VarArgs("\nDoIActivate: ", Goal->edict()->netname ? STRING(Goal->edict()->netname) : STRING(Goal->edict()->classname))); + if (AP) + Warning(UTIL_VarArgs(", AP: %s\n", AP->GetPlayerName())); +#endif + + // Abort if it's already active + if (Goal->goal_state == TFGS_ACTIVE) + { + MDEBUG(Warning("-- Goal already active --\n")); + return false; + } + // Abort if it's been removed + if (Goal->goal_state == TFGS_REMOVED) + { + MDEBUG(Warning("-- Goal is in Removed state --\n")); + return false; + } + // Abort if it's been activated already and its activation's being delayed + if (Goal->goal_state == TFGS_DELAYED) + { + MDEBUG(Warning("-- Goal is being Delayed --\n")); + return false; + } + + // See if the AP matches the criteria + bool bAPMet = APMeetsCriteria(Goal, AP); + bool bAct = false; + bool bRevAct; + if ( FClassnameIs(Goal,"item_tfgoal") ) + bRevAct = (Goal->goal_activation & TFGI_REVERSE_AP) != 0; + else + bRevAct = (Goal->goal_activation & TFGA_REVERSE_AP) != 0; + + // Does the AP match the AP Criteria? + if (bAPMet) + { + MDEBUG(Warning("-- Criteria met --\n")); + if (!bRevAct) + bAct = true; + } + else + { + MDEBUG(Warning("-- Criteria not met --\n")); + if (bRevAct) + { + MDEBUG(Warning("Reverse Activation\n")); + bAct = true; + } + } + +#ifdef MAP_DEBUG + if (bAct) + Warning("Activation.\n"); + else + Warning("NO Activation.\n"); +#endif + + return bAct; +}; + + +//========================================================================= +// Return TRUE if the player is affected by the goal +BOOL IsAffectedBy(CTFGoal *Goal, CTFPlayer *Player, CTFPlayer *AP) +{ + // Don't affect anyone who isn't alive or is in Observer mode + CTFPlayerClass *pPlayerClass = AP->GetPlayerClass(); + if ( pPlayerClass && pPlayerClass->IsClass( TF_CLASS_UNDEFINED ) ) + return FALSE; + + // Same Environment Check + if (Goal->goal_effects & TFGE_SAME_ENVIRONMENT) + { + int iEnviron = UTIL_PointContents( Goal->GetAbsOrigin() ); + if ( UTIL_PointContents( Player->GetAbsOrigin() ) != iEnviron ) + return FALSE; + } + + if (Goal->t_length != 0) + { + // Within radius? + if ((Goal->GetAbsOrigin() - Player->GetAbsOrigin()).Length() <= Goal->t_length) + { + // Obstructed by walls? + if (Goal->goal_effects & TFGE_WALL) + { + trace_t tr; + UTIL_TraceLine ( Goal->GetAbsOrigin(), Player->WorldSpaceCenter(), MASK_SOLID, Goal, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction == 1.0 ) + return TRUE; + } + else + { + return TRUE; + } + } + } + + if ( Goal->Classify() != CLASS_TFGOAL_TIMER && AP != NULL ) + { + // Spawnpoints always affect the player who spawns on them + if ((Goal->Classify() == CLASS_TFSPAWN) && (Player == AP)) + return TRUE; + + if ((Goal->goal_effects & TFGE_AP) && (Player == AP)) + return TRUE; + + if ((Goal->goal_effects & TFGE_AP_TEAM) && (AP->GetTeamNumber() == Player->GetTeamNumber())) + return TRUE; + } + + if (Goal->goal_effects & TFGE_NOT_AP_TEAM) + { + if (AP == NULL || AP->GetTeamNumber() != Player->GetTeamNumber()) + return TRUE; + } + + if ((Goal->goal_effects & TFGE_NOT_AP) && (Player != AP)) + return TRUE; + + if ((Goal->maxammo_shells != 0) && (Player->GetTeamNumber() == Goal->maxammo_shells)) + return TRUE; + + if ((Goal->maxammo_nails != 0) && (Player->GetTeamNumber() != Goal->maxammo_nails)) + return TRUE; + + return FALSE; +} + + +//========================================================================= +// Do all the checking of Item Groups +void DoItemGroupWork(CTFGoalItem *Item, CTFPlayer *AP) +{ + if (Item->distance != 0) + { + if (Item->pain_finished == 0) + { + // No goal specified in .pain_finished. Print error. + Warning( "GoalItem %d has .distance specified, but no .pain_finished\n", Item->goal_no ); + } + + BOOL bAllCarried = TRUE; + // Find all items + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" ); + while ( pEnt && bAllCarried ) + { + CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt ); + if ( pItem ) + { + if (pItem->group_no == Item->distance && pItem->goal_state != TFGS_ACTIVE) + bAllCarried = FALSE; + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" ); + } + + if (bAllCarried) + { + CTFGoal *pGoal = Findgoal(Item->pain_finished); + if (pGoal) + DoResults(pGoal, AP, (Item->goal_result & TFGR_ADD_BONUSES)); + } + } + + if (Item->speed != 0) + { + if (Item->attack_finished == 0) + { + // No goal specified in .attack_finished. Print error. + Warning( "GoalItem %d has .speed specified, but no .attack_finished\n", Item->goal_no ); + } + + BOOL bAllCarried = TRUE; + CBaseEntity *pCarrier = NULL; + // Find all goals + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" ); + while ( pEnt && bAllCarried ) + { + CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt ); + if ( pItem ) + { + if (pItem->group_no == Item->speed) + { + if (pItem->goal_state != TFGS_ACTIVE) + bAllCarried = FALSE; + else if (!pCarrier) // Store Player + pCarrier = pItem->GetOwnerEntity(); + else if (pCarrier != pItem->GetOwnerEntity()) // Need to all be carried by the same player + bAllCarried = FALSE; + } + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" ); + } + + if (bAllCarried) + { + CTFGoal *pGoal = Findgoal(Item->attack_finished); + if (pGoal) + DoResults(pGoal, AP, (Item->goal_result & TFGR_ADD_BONUSES)); + } + } +} + + +//========================================================================= +// Remove any results applied to this player by the Goal +// Used when a GoalItem is dropped/removed +void RemoveResults(CTFGoal *Goal, CTFPlayer *pPlayer) +{ + // Only remove the stats if the player has been affected + // by this item. This is needed because the player may have + // died since being affected + if ( FClassnameIs( Goal, "item_tfgoal" ) ) + { + if (!(pPlayer->item_list & Goal->item_list)) + return; + + if (Goal->goal_activation & TFGI_DONTREMOVERES) + return; + + // Remove the affected flag + pPlayer->item_list &= ~(Goal->item_list); + } + + if (Goal->GetHealth() > 0) + pPlayer->TakeDamage( CTakeDamageInfo( Goal, Goal, Goal->GetHealth(), DMG_IGNOREARMOR ) ); + if (Goal->GetHealth() < 0) + pPlayer->TakeHealth( (0 - Goal->GetHealth()), 0 ); + pPlayer->lives -= Goal->lives; +// pPlayer->armortype -= Goal->armortype; + pPlayer->SetArmorValue( pPlayer->ArmorValue() - Goal->armorvalue ); +// pPlayer->armorclass &= ~(Goal->armorclass); + + if (Goal->frags) + { + pPlayer->TF_AddFrags(Goal->frags); + } + + pPlayer->RemoveAmmo( Goal->ammo_shells, TF_AMMO_SHELLS ); + pPlayer->RemoveAmmo( Goal->ammo_nails, TF_AMMO_NAILS ); + pPlayer->RemoveAmmo( Goal->ammo_rockets, TF_AMMO_ROCKETS ); + pPlayer->RemoveAmmo( Goal->ammo_cells, TF_AMMO_CELLS ); + pPlayer->RemoveAmmo( Goal->ammo_medikit, TF_AMMO_MEDIKIT ); + pPlayer->RemoveAmmo( Goal->ammo_detpack, TF_AMMO_DETPACK ); + + // Detpacks +//TFTODO: this should be handled in the GiveAmmo functions.. +// if (pPlayer->ammo_detpack > pPlayer->maxammo_detpack) +// pPlayer->ammo_detpack = pPlayer->maxammo_detpack; + + // Grenades + pPlayer->RemoveAmmo( Goal->no_grenades_1, TF_AMMO_GRENADES1 ); + pPlayer->RemoveAmmo( Goal->no_grenades_2, TF_AMMO_GRENADES2 ); + + // If they had a primed grenade, and they don't have any more of + // that type of grenade, unprime it and remove it. + if (pPlayer->m_Shared.GetStateFlags() & TFSTATE_GRENPRIMED) + { + if (pPlayer->GetAmmoCount( TF_AMMO_GRENADES2 ) <= 0 || pPlayer->GetAmmoCount( TF_AMMO_GRENADES1 ) <= 0) + { + pPlayer->m_Shared.RemoveStateFlags( TFSTATE_GRENPRIMED ); + pPlayer->m_Shared.RemoveStateFlags( TFSTATE_GRENTHROWING ); + pPlayer->bRemoveGrenade = TRUE; + } + } + + BOOL puinvin = FALSE; + BOOL puinvis = FALSE; + BOOL puquad = FALSE; + BOOL purad = FALSE; + // Make sure we don't remove an effect another Goal is also supplying + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" ); + while ( pEnt ) + { + CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt ); + if ( pItem ) + { + if ( (pItem->GetOwnerEntity() == pPlayer) && (pEnt != Goal) ) + { + if (pItem->invincible_finished > 0) + puinvin = TRUE; + if (pItem->invisible_finished > 0) + puinvis = TRUE; + if (pItem->super_damage_finished > 0) + puquad = TRUE; + if (pItem->radsuit_finished > 0) + purad = TRUE; + } + } + pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" ); + } + + // Remove all powerups + if ((Goal->invincible_finished > 0) && (!puinvin)) + { + // if its a GoalItem, powerup was permanent, so we remove TFSTATE flag + pPlayer->m_Shared.RemoveStateFlags( TFSTATE_INVINCIBLE ); + pPlayer->m_Shared.AddItemFlags( IT_INVULNERABILITY ); + pPlayer->invincible_finished = gpGlobals->curtime + Goal->invincible_finished; + } + if ((Goal->invisible_finished > 0) && (!puinvis)) + { + // if its a GoalItem, powerup was permanent, so we remove TFSTATE flag + pPlayer->m_Shared.RemoveStateFlags( TFSTATE_INVISIBLE ); + pPlayer->m_Shared.AddItemFlags( IT_INVISIBILITY ); + pPlayer->invisible_finished = gpGlobals->curtime + Goal->invisible_finished; + } + if ((Goal->super_damage_finished > 0) && (!puquad)) + { + // if its a GoalItem, powerup was permanent, so we remove TFSTATE flag + pPlayer->m_Shared.RemoveStateFlags( TFSTATE_QUAD ); + pPlayer->m_Shared.AddItemFlags( IT_QUAD ); + pPlayer->super_damage_finished = gpGlobals->curtime + Goal->super_damage_finished; + } + if ((Goal->radsuit_finished > 0) && (!purad)) + { + // if its a GoalItem, powerup was permanent, so we remove TFSTATE flag + pPlayer->m_Shared.RemoveStateFlags( TFSTATE_RADSUIT ); + pPlayer->m_Shared.AddItemFlags( IT_SUIT ); + pPlayer->radsuit_finished = gpGlobals->curtime + Goal->radsuit_finished; + } + + // Now apply the pev->playerclass limitations & Redisplay Ammo counts + //W_SetCurrentAmmo (); +} + + +//========================================================================= +// Give the GoalItem to a Player. +void tfgoalitem_GiveToPlayer(CTFGoalItem *Item, CTFPlayer *AP, CTFGoal *Goal) +{ + MDEBUG(Warning( "Giving %s to %s\n", Item->GetEntityName().ToCStr(), AP->GetPlayerName())); + + // Don't let it re-drop + if (Item->redrop_count) + Item->SetThink( NULL ); + + Item->SetOwnerEntity( AP ); + // Remove it from the map + Item->FollowEntity( AP ); + // Play carry animations + if (Item->GetModelName() != NULL_STRING) + { + Item->RemoveEffects( EF_NODRAW ); + + Item->SetSequence( Item->LookupSequence( "carried" ) ); + if (Item->GetSequence() != -1) + { + Item->ResetSequenceInfo(); + Item->SetCycle( 0.0f ); + } + } + + Item->AddSolidFlags( FSOLID_NOT_SOLID ); + + // Do the deeds on the player + if (Item->goal_activation & TFGI_GLOW) + AP->AddEffects( EF_BRIGHTLIGHT ); //TFTODO: this used to be EF_BRIGHTFIELD.. make sure it's the same + if (Item->goal_activation & TFGI_SLOW) + AP->TeamFortress_SetSpeed(); + if (Item->speed_reduction) + AP->TeamFortress_SetSpeed(); + + if (Item->goal_activation & TFGI_ITEMGLOWS) + { + Item->m_nRenderFX = kRenderFxNone; + Item->SetRenderColor( 0, 0, 0, 0 ); + } + + // Light up console icons + if (Item->items & IT_KEY1) + AP->m_Shared.AddItemFlags( IT_KEY1 ); + if (Item->items & IT_KEY2) + AP->m_Shared.AddItemFlags( IT_KEY2 ); + if (Item->items & IT_KEY3) + AP->m_Shared.AddItemFlags( IT_KEY3 ); + if (Item->items & IT_KEY4) + AP->m_Shared.AddItemFlags( IT_KEY4 ); + + // Only do the results if we're allowed to + if (Goal != Item) + { + if (Goal->goal_result & TFGR_NO_ITEM_RESULTS) + { + Item->goal_state = TFGS_ACTIVE; + return; + } + } + + MDEBUG(Warning("Doing item results...\n")); + + // Prevent the Player from disguising themself if applicable + if (Item->goal_result & TFGR_REMOVE_DISGUISE) + AP->is_unableto_spy_or_teleport = 1; + + // Do the Results, adding the bonuses + DoResults(Item, AP, TRUE); + + // Check the Item Group Stuff + DoItemGroupWork(Item, AP); +} + + +//========================================================================= +// Drop the item +void tfgoalitem_drop(CTFGoalItem *Item, BOOL PAlive, CTFPlayer *P) +{ + CBaseEntity *pOwner = Item->GetOwnerEntity(); + + // Backup origin for retry at the drop + if ( FBitSet( pOwner->GetFlags(), FL_DUCKING ) ) + Item->redrop_origin = pOwner->GetAbsOrigin() + Vector(0, 0, 26); + else + Item->redrop_origin = pOwner->GetAbsOrigin() + Vector(0, 0, 8); + Item->redrop_count = 0; + + Item->SetTouch( &CTFGoalItem::item_tfgoal_touch ); + Item->DoDrop( Item->redrop_origin ); + + Item->SetOwnerEntity( P ); + if (PAlive) + { + Vector vForward, vUp; + AngleVectors( P->EyeAngles(), &vForward, NULL, &vUp ); + Item->SetAbsVelocity( (vForward * 400) + (vUp * 200) ); + + Item->SetTouch( NULL ); + Item->SetThink( &CTFGoalItem::tfgoalitem_droptouch ); // give it 0.75 seconds + Item->SetNextThink( gpGlobals->curtime + 0.75 ); // and then set it's touch func + + // Prevent the dropping player from picking it up for longer + Item->enemy = P; + Item->m_flDroppedAt = gpGlobals->curtime; + } +} + + +//========================================================================= +// Remove the GoalItem from a Player. +void tfgoalitem_RemoveFromPlayer(CTFGoalItem *Item, CTFPlayer *AP, int iMethod) +{ + MDEBUG(Warning("Removing %s from %s\n", STRING(Item->pev->netname), STRING(AP->pev->netname))); + + // If we have a teamcheck entity, use it instead + if ( Item->owned_by_teamcheck != NULL_STRING ) + Item->owned_by = GetTeamCheckTeam( STRING(Item->owned_by_teamcheck) ); + + BOOL lighton = FALSE; + BOOL slowon = FALSE; + BOOL key1on = FALSE; + BOOL key2on = FALSE; + BOOL key3on = FALSE; + BOOL key4on = FALSE; + BOOL spyoff = FALSE; + // Remove the effects from the player + // Make sure we don't remove an effect another Goal is also supplying + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" ); + while ( pEnt ) + { + CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt ); + if ( pItem ) + { + if ( (pItem->GetOwnerEntity() == AP) && (pEnt != Item) ) + { + if (pItem->goal_activation & TFGI_GLOW) + lighton = TRUE; + if (pItem->goal_activation & TFGI_SLOW) + slowon = TRUE; + + if (pItem->items & IT_KEY1) + key1on = TRUE; + if (pItem->items & IT_KEY2) + key2on = TRUE; + if (pItem->items & IT_KEY3) + key3on = TRUE; + if (pItem->items & IT_KEY4) + key4on = TRUE; + + if (pItem->goal_result & TFGR_REMOVE_DISGUISE) + spyoff = TRUE; + } + } + pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" ); + } + + // Check Powerups too + if (!lighton) + { + if (AP->invincible_finished > gpGlobals->curtime + 3) + lighton = TRUE; + else if (AP->super_damage_finished > gpGlobals->curtime + 3) + lighton = TRUE; + } + if (!lighton) + { + //TFTODO: Add support for EF_BRIGHTFIELD if necessary. + //AP->RemoveEffects( EF_BRIGHTFIELD ); + AP->RemoveEffects( EF_BRIGHTLIGHT ); + } + if (Item->goal_activation & TFGI_ITEMGLOWS) + { + Item->m_nRenderFX = kRenderFxGlowShell; + +// if (Item->owned_by > 0 && Item->owned_by <= 4) +// Item->m_clrRender = Vector255ToRGBColor( rgbcolors[Item->owned_by] ); +// else +// Item->m_clrRender = Vector255ToRGBColor( rgbcolors[0] ); + + if ( TFTeamMgr()->IsValidTeam( Item->owned_by ) ) + { + CTFTeam *pTeam = TFTeamMgr()->GetTeam( Item->owned_by ); + Item->m_clrRender = pTeam->GetColor(); + } + else + { + Item->m_clrRender = TFTeamMgr()->GetUndefinedTeamColor(); + } + + Item->SetRenderColorA( 100 ); // Shell size + } + + // Remove the Spy prevention + if (!spyoff) + AP->is_unableto_spy_or_teleport = FALSE; + // Remove the lit console key icons + if (!key1on) + AP->m_Shared.RemoveItemFlags( IT_KEY1 ); + if (!key2on) + AP->m_Shared.RemoveItemFlags( IT_KEY2 ); + if (!key3on) + AP->m_Shared.RemoveItemFlags( IT_KEY3 ); + if (!key4on) + AP->m_Shared.RemoveItemFlags( IT_KEY4 ); + + // Remove AP Modifications + // Go through all the players and do any results + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); + if ( pPlayer && IsAffectedBy(Item, pPlayer, AP) ) + RemoveResults(Item, pPlayer); + } + + // Setup animations + if (Item->GetModelName() != NULL_STRING) + { + Item->SetSequence( Item->LookupSequence( "not_carried" ) ); + if (Item->GetSequence() != -1) + { + Item->ResetSequenceInfo(); + Item->SetCycle( 0.0f ); + } + } + + // Return it to the starting point if the flag is set + if (iMethod == GI_DROP_PLAYERDEATH || iMethod == GI_DROP_PLAYERDROP) + { + // Do messages + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( !pPlayer ) + continue; + + if (pPlayer->GetTeamNumber() == Item->owned_by) + { + if (Item->team_drop != NULL_STRING) + UTIL_ShowMessage( STRING(Item->team_drop), pPlayer ); + if (Item->netname_team_drop != NULL_STRING) + ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Item->netname_team_drop), AP->GetPlayerName() ); + // Old printing + if (Item->org_team_drop != NULL_STRING) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Item->org_team_drop) ); + } + else // (pPlayer->GetTeamNumber() != Item->owned_by) + { + if (Item->non_team_drop != NULL_STRING) + UTIL_ShowMessage( STRING(Item->non_team_drop), pPlayer ); + if (Item->netname_non_team_drop != NULL_STRING) + ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Item->netname_non_team_drop), AP->GetPlayerName() ); + // Old printing + if (Item->org_non_team_drop != NULL_STRING) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Item->org_non_team_drop) ); + } + } + + // Drop it if the flag is set + if (Item->goal_activation & TFGI_RETURN_DROP) + { + CTimer *pTimer = Timer_CreateTimer( Item, TF_TIMER_RETURNITEM ); + pTimer->m_flNextThink = gpGlobals->curtime + 0.5f; + if (iMethod == GI_DROP_PLAYERDEATH) + pTimer->weapon = GI_RET_DROP_DEAD; + else + pTimer->weapon = GI_RET_DROP_LIVING; + } + else if (Item->goal_activation & TFGI_DROP) + { + if ( (iMethod == GI_DROP_PLAYERDROP) && (Item->goal_activation & TFGI_CANBEDROPPED) ) + tfgoalitem_drop(Item, TRUE, AP); + else + tfgoalitem_drop(Item, FALSE, AP); + } + else + { + // Remove the Item + Item->SetOwnerEntity( NULL ); + Item->SetNextThink( gpGlobals->curtime ); + Item->SetThink( &CBaseEntity::SUB_Remove ); + AP->TeamFortress_SetSpeed(); + return; + } + + Item->SetOwnerEntity( NULL ); + Item->RemoveFlag( FL_ONGROUND ); + UTIL_SetSize(Item, Item->goal_min, Item->goal_max); + + AP->TeamFortress_SetSpeed(); + } + else if (iMethod == GI_DROP_REMOVEGOAL) + { + Item->SetOwnerEntity( NULL ); + + if (Item->goal_activation & TFGI_RETURN_GOAL) + { + CTimer *pTimer = Timer_CreateTimer( Item, TF_TIMER_RETURNITEM ); + pTimer->m_flNextThink = gpGlobals->curtime + 0.5; + pTimer->weapon = GI_RET_GOAL; + AP->TeamFortress_SetSpeed(); + return; + } + + // Don't remove it, since it may be given away again later + Item->AddSolidFlags( FSOLID_NOT_SOLID ); + Item->AddEffects( EF_NODRAW ); + Item->StopFollowingEntity(); + AP->TeamFortress_SetSpeed(); + } +} + + +//========================================================================= +// Apply modifications to the Player passed in +void Apply_Results(CTFGoal *Goal, CTFPlayer *Player, CTFPlayer *AP, BOOL bAddBonuses) +{ + MDEBUG( Warning("Applying Results from %s to %s\n", STRING(Goal->pev->netname), STRING(Player->pev->netname)) ); + +#if 0 + // If this is a goalitem, record the fact that this player + // has been affected by it. + if ( FClassnameIs(Goal, "item_tfgoal") ) + Player->item_list |= Goal->item_list; + + if (Player == AP) + { + // Alter the team score + if (Goal->count != 0 && Player->GetTeamNumber() > 0) + { + TeamFortress_TeamIncreaseScore(Player->GetTeamNumber(), Goal->count); + // Display short team scores + //TeamFortress_TeamShowScores(FALSE, NULL); + } + } + + // Apply Stats, only if told to + if (bAddBonuses) + { + MDEBUG( Warning("Adding bonuses.\n") ); + // Some results are not applied to dead players + if ( Player->IsAlive() ) + { + if (Goal->GetHealth() > 0) + Player->TakeHealth(Goal->GetHealth(), 0); + if (Goal->GetHealth() < 0) + { + // Make sure we don't gib them, because it creates too many entities if + // a lot of players are affected by this Goal. + Player->TakeDamage( CTakeDamageInfo( Goal, Goal, (0 - Goal->GetHealth()), DMG_IGNOREARMOR | DMG_NEVERGIB ) ); + } + } + + // The player may be dead now, so check again + if ( Player->IsAlive() ) + { +// if (Goal->armortype > 0) +// Player->armortype = Goal->armortype; +// if (Goal->armorvalue > 0) +// Player->armortype = Player->armor_allowed; + Player->IncrementArmorValue( Goal->armorvalue ); +// if (Goal->armorclass > 0) +// Player->armorclass = Goal->armorclass; + + Player->GiveAmmo( Goal->ammo_shells, TF_AMMO_SHELLS ); + Player->GiveAmmo( Goal->ammo_nails, TF_AMMO_NAILS ); + Player->GiveAmmo( Goal->ammo_rockets, TF_AMMO_ROCKETS ); + Player->GiveAmmo( Goal->ammo_cells, TF_AMMO_CELLS ); + Player->GiveAmmo( Goal->ammo_medikit, TF_AMMO_MEDIKIT ); + Player->GiveAmmo( Goal->ammo_detpack, TF_AMMO_DETPACK ); + +#ifdef TFTODO // do this when grenades are implemented. + // Grenades + if ( Player->tp_grenades_1 != GR_TYPE_NONE ) + Player->no_grenades_1 += Goal->no_grenades_1; + if ( Player->tp_grenades_2 != GR_TYPE_NONE ) + Player->no_grenades_2 += Goal->no_grenades_2; + + // If they had a primed grenade, and they don't have any more of + // that type of grenade, unprime it and remove it. + if (Player->tfstate & TFSTATE_GRENPRIMED) + { + if ( (Player->m_iPrimedGrenType == 1 && Player->no_grenades_1 <= 0 && Goal->no_grenades_1 < 0) || + (Player->m_iPrimedGrenType == 2 && Player->no_grenades_2 <= 0 && Goal->no_grenades_2 < 0) ) + { + Player->tfstate &= ~TFSTATE_GRENPRIMED; + Player->tfstate &= ~TFSTATE_GRENTHROWING; + Player->bRemoveGrenade = TRUE; + } + } +#endif + + // Apply any powerups + if (Goal->invincible_finished > 0) + { + Player->m_Shared.AddItemFlags( IT_INVULNERABILITY ); + Player->invincible_finished = gpGlobals->curtime + Goal->invincible_finished; + // if its a GoalItem, powerup is permanent, so we use TFSTATE flags + if ( FClassnameIs(Goal, "item_tfgoal") ) + { + Player->m_Shared.AddStateFlags( TFSTATE_INVINCIBLE ); + Player->invincible_finished = gpGlobals->curtime + 666; + } + + // Force it to recalculate shell color + Player->m_nRenderFX = kRenderFxNone; + } + if (Goal->invisible_finished > 0) + { + Player->m_Shared.AddItemFlags( IT_INVISIBILITY ); + Player->invisible_finished = gpGlobals->curtime + Goal->invisible_finished; + // if its a GoalItem, powerup is permanent, so we use TFSTATE flags + if ( FClassnameIs(Goal, "item_tfgoal") ) + { + Player->m_Shared.AddStateFlags( TFSTATE_INVISIBLE ); + Player->invisible_finished = gpGlobals->curtime + 666; + } + + // Force it to recalculate shell color + Player->m_nRenderFX = kRenderFxNone; + } + if (Goal->super_damage_finished > 0) + { + Player->m_Shared.AddItemFlags( IT_QUAD ); + Player->super_damage_finished = gpGlobals->curtime + Goal->super_damage_finished; + // if its a GoalItem, powerup is permanent, so we use TFSTATE flags + if ( FClassnameIs(Goal, "item_tfgoal") ) + { + Player->m_Shared.AddStateFlags( TFSTATE_QUAD ); + Player->super_damage_finished = gpGlobals->curtime + 666; + } + + // Force it to recalculate shell color + Player->m_nRenderFX = kRenderFxNone; + } + if (Goal->radsuit_finished > 0) + { + Player->m_Shared.AddItemFlags( IT_SUIT ); + Player->radsuit_finished = gpGlobals->curtime + Goal->radsuit_finished; + // if its a GoalItem, powerup is permanent, so we use TFSTATE flags + if ( FClassnameIs(Goal, "item_tfgoal") ) + { + Player->m_Shared.AddStateFlags( TFSTATE_RADSUIT ); + Player->radsuit_finished = gpGlobals->curtime + 666; + } + } + } + + // These results are applied to dead and living players + Player->lives += Goal->lives; + + if ( Goal->frags != 0 ) + Player->TF_AddFrags(Goal->frags); + } +#ifdef MAP_DEBUG + else + ALERT( at_console, "NOT Adding bonuses.\n" ); +#endif + + // If the Goal resets Spy skin/color then do it + CTFPlayerClass *pPlayerClass = Player->GetPlayerClass(); + if ( pPlayerClass && pPlayerClass->IsClass( TF_CLASS_SPY ) && Goal->goal_result & TFGR_REMOVE_DISGUISE ) + { + pPlayerClass->Spy_RemoveDisguise(); + } + + // If there's a GoalItem for this goal, give it to the player + // GoalItems use "items" for the console lights... so don't do it for items. + if ( Goal->items != 0 && !FClassnameIs(Goal,"item_tfgoal") ) + { + // Find the item + CTFGoalItem *pItem = Finditem(Goal->items); + // Don't give them the item if it's the item that just affected them + if (pItem != NULL && pItem != Goal) + tfgoalitem_GiveToPlayer(pItem, Player, Goal); + } + + // If this goal removes an item from the player, remove it + if (Goal->axhitme != 0) + { + CTFGoalItem *pItem = Finditem(Goal->axhitme); + if (pItem->GetOwnerEntity() == Player) + tfgoalitem_RemoveFromPlayer(pItem, Player, GI_DROP_REMOVEGOAL); + } + + // if this goal removes a group of items from the player, remove them + if (Goal->remove_item_group != 0) + { + // Find all items in the group + CTFGoalItem *pItemToRemove = NULL; + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" ); + while ( pEnt ) + { + CTFGoalItem *pItem = dynamic_cast<CTFGoalItem*>( pEnt ); + if ( pItem ) + { + if ( (pItem->group_no == Goal->remove_item_group) && (pItem->GetOwnerEntity() == Player) ) + pItemToRemove = pItem; + + // need to cycle before removing it from the player, because it may be destroyed + pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" ); + if (pItemToRemove) + { + tfgoalitem_RemoveFromPlayer(pItemToRemove, Player, GI_DROP_REMOVEGOAL); + pItemToRemove = NULL; + } + } + } + } + + // Display all the item statuses + Player->DisplayLocalItemStatus(Goal); + + // Destroy buildings + if (Goal->goal_result & TFGR_DESTROY_BUILDINGS) + { + Player->no_sentry_message = TRUE; + Player->no_dispenser_message = TRUE; + Player->no_entry_teleporter_message = TRUE; + Player->no_exit_teleporter_message = TRUE; + Player->Engineer_RemoveBuildings(); + Player->TeamFortress_RemoveLiveGrenades(); + Player->TeamFortress_RemoveRockets(); + Player->RemovePipebombs(); + + // is the player setting a detpack? + if ( Player->is_detpacking ) + { + Player->TeamFortress_DetpackStop(); + } + else + { + // does the player have a detpack in the world? + if ( Player->TeamFortress_RemoveDetpacks() ) + { + Player->GiveAmmo( 1, TF_AMMO_DETPACK ); + } + } + } + + // Force respawns + if (Goal->goal_result & TFGR_FORCE_RESPAWN) + { + // Only if they're alive + if ( Player->IsAlive() ) + Player->ForceRespawn(); + } + +#endif +} + + +//========================================================================= +// Use (Triggered) function for Goals +void EndRound( CTFGoal *pGoal ) +{ + // fade everyones screen + color32 clr; + memset( &clr, 0, sizeof( clr ) ); + UTIL_ScreenFadeAll( clr, 0.3, pGoal->m_flEndRoundTime, FFADE_MODULATE | FFADE_OUT ); + + // Display Long TeamScores to everyone +// TeamFortress_TeamShowScores(TRUE, NULL); + TFTeamMgr()->ShowScores(); + + int highestScore = -99990; + int winningTeam = 1; + const char *winnerMsg = ""; + // Only do team score check if the win one is set + if ( pGoal->m_iszEndRoundMsg_Team1_Win != NULL_STRING ) + { + // work out which team won + int nTeamCount = TFTeamMgr()->GetTeamCount(); + for ( int iTeam = 1; iTeam < nTeamCount; ++iTeam ) + { + // int teamScore = TeamFortress_TeamGetScoreFrags( i ); + CTFTeam *pTeam = TFTeamMgr()->GetTeam( iTeam ); + if ( pTeam ) + { + int teamScore = pTeam->GetScore(); + if ( teamScore > highestScore ) + { + winningTeam = iTeam; + highestScore = teamScore; + } + } + } + + // work out the winning msg + switch ( winningTeam ) + { + case 1: winnerMsg = STRING(pGoal->m_iszEndRoundMsg_Team1_Win); break; + case 2: winnerMsg = STRING(pGoal->m_iszEndRoundMsg_Team2_Win); break; + case 3: winnerMsg = STRING(pGoal->m_iszEndRoundMsg_Team3_Win); break; + case 4: winnerMsg = STRING(pGoal->m_iszEndRoundMsg_Team4_Win); break; + }; + } + + // Prevent players from moving and shooting + no_cease_fire_text = TRUE; + cease_fire = TRUE; + + // Send out the messages + CTFPlayer *client = NULL; + while ( ((client = (CTFPlayer*)gEntList.FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) ) + { + if ( !client ) + continue; + + // Freeze all the players + if ( client->IsObserver() == FALSE ) + { + //TFTODO implement something for this? + // iuser4 stops firing on the clients + //client->pev->iuser4 = TRUE; + + //TFTODO: implement HIDEHUD_WEAPONS, or is it the same as HIDEHUD_WEAPONSELECTION? + //client->m_Local.m_iHideHUD |= (HIDEHUD_HEALTH | HIDEHUD_WEAPONS); + client->m_Local.m_iHideHUD |= (HIDEHUD_HEALTH | HIDEHUD_WEAPONSELECTION); + + client->m_Shared.AddStateFlags( TFSTATE_CANT_MOVE ); + } + client->TeamFortress_SetSpeed(); + + // Owned by and Non owned by take precedence + if ( pGoal->m_iszEndRoundMsg_OwnedBy != NULL_STRING && ( client->GetTeamNumber() == pGoal->owned_by ) ) + { + UTIL_ShowMessage( STRING( pGoal->m_iszEndRoundMsg_OwnedBy ), client ); + } + else if ( pGoal->m_iszEndRoundMsg_NonOwnedBy != NULL_STRING && ( client->GetTeamNumber() != pGoal->owned_by ) ) + { + UTIL_ShowMessage( STRING( pGoal->m_iszEndRoundMsg_NonOwnedBy ), client ); + } + else if ( pGoal->m_iszEndRoundMsg_Team1_Win != NULL_STRING && client->GetTeamNumber() == winningTeam ) + { + UTIL_ShowMessage( winnerMsg, client ); + } + else + { + const char *loserMsg = ""; + // work out the loser message and send it to them + if ( pGoal->m_iszEndRoundMsg_Team1_Win != NULL_STRING ) + { + switch ( client->GetTeamNumber() ) + { + case 1: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team1_Lose); break; + case 2: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team2_Lose); break; + case 3: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team3_Lose); break; + case 4: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team4_Lose); break; + }; + } + else + { + switch ( client->GetTeamNumber() ) + { + case 1: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team1); break; + case 2: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team2); break; + case 3: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team3); break; + case 4: loserMsg = STRING(pGoal->m_iszEndRoundMsg_Team4); break; + }; + } + + UTIL_ShowMessage( loserMsg, client ); + } + } + + // Create a timer to remove the EndRound in the specified time + CTimer *pTimer = Timer_CreateTimer( pGoal, TF_TIMER_ENDROUND ); + //pTimer->SetThink( &CBaseEntity::EndRoundEnd ); + pTimer->m_flNextThink = gpGlobals->curtime + pGoal->m_flEndRoundTime; +} + + +//========================================================================= +// Inactivate a Timer/Goal +void InactivateGoal(CTFGoal *Goal) +{ + MDEBUG( Warning("Inactivating %s", STRING(Goal->pev->netname)) ); + + if (Goal->goal_state == TFGS_ACTIVE) + { + MDEBUG( Warning("... succeeded.\n") ); + // Not a timer goal + if (Goal->Classify() != CLASS_TFGOAL_TIMER) + { + if ( Goal->goal_activation & TFGI_SOLID && (Goal->Classify() == CLASS_TFGOAL || Goal->Classify() == CLASS_TFGOAL_ITEM) ) + Goal->SetSolid( SOLID_BBOX ); + else + Goal->AddSolidFlags( FSOLID_TRIGGER ); + } + + Goal->goal_state = TFGS_INACTIVE; + const char *pModel = STRING( Goal->GetModelName() ); + if (pModel && pModel[0] != '*') + Goal->RemoveEffects( EF_NODRAW ); + } +#ifdef MAP_DEBUG + else + Warning("... failed. Goal is %s\n", g_szStates[Goal->goal_state]); +#endif +} + + +//========================================================================= +// Restores a Timer/Goal +void RestoreGoal(CTFGoal *Goal) +{ + MDEBUG( Warning("Attempting to Restore %s", STRING(Goal->pev->netname)) ); + + if (Goal->goal_state == TFGS_REMOVED) + { + MDEBUG( Warning("... succeeded.\n") ); + + // Not a timer goal + if (Goal->search_time == 0) + { + if (Goal->goal_activation & TFGI_SOLID && FClassnameIs(Goal, "item_tfgoal") ) + Goal->SetSolid( SOLID_BBOX ); + else + Goal->AddSolidFlags( FSOLID_TRIGGER ); + } + else + Goal->SetNextThink( gpGlobals->curtime + Goal->search_time ); + + Goal->goal_state = TFGS_INACTIVE; + const char *pModel = STRING(Goal->GetModelName()); + if (pModel[0] != '*') + Goal->RemoveEffects( EF_NODRAW ); + } +#ifdef MAP_DEBUG + else + Warning("... failed. Goal is %s\n", g_szStates[Goal->goal_state]); +#endif +} + + +//========================================================================= +// Do all the activation/inactivation/etc of Goal Groups +void DoGroupWork(CTFGoal *Goal, CTFPlayer *AP) +{ +#ifdef MAP_DEBUG + if (Goal->all_active || Goal->activate_group_no || Goal->inactivate_group_no || Goal->restore_group_no || Goal->remove_group_no) + Warning("Doing Groupwork...\n"); +#endif + + // Check all goals activated flag + if (Goal->all_active != 0) + { + if (Goal->last_impulse == 0) + { + // No goal specified in .last_impulse. Print error. + Warning("Goal %d has .all_active specified, but no .last_impulse\n", Goal->goal_no); + } + else + { + MDEBUG( Warning("All Active Group Check.\n") ); + + BOOL bAllSet = TRUE; + // Find all goals + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" ); + while ( pEnt && bAllSet) + { + CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt ); + if ( pGoal ) + { + if (pGoal->group_no == Goal->all_active && pGoal->goal_state != TFGS_ACTIVE) + bAllSet = FALSE; + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" ); + } + + // If all goals in this group are activated, do it + if (bAllSet) + { + MDEBUG( Warning("All Active, Activating last_impulse.\n") ); + + CTFGoal *pGoal = Findgoal(Goal->last_impulse); + if (pGoal) + DoResults(pGoal, AP, (Goal->goal_result & TFGR_ADD_BONUSES)); + } + #ifdef MAP_DEBUG + else + { + Warning("Not all Active.\n"); + } + #endif + } + } + + // Check Activate all in the group flag + if (Goal->activate_group_no != 0) + { + // Find all goals + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" ); + while ( pEnt ) + { + CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt ); + if ( pGoal ) + { + if (pGoal->group_no == Goal->activate_group_no) + ActivateDoResults(pGoal, AP, Goal); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" ); + } + } + + // Check Inactivate all in the group flag + if (Goal->inactivate_group_no != 0) + { + // Find all goals + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" ); + while ( pEnt ) + { + CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt ); + if ( pGoal ) + { + if (pGoal->group_no == Goal->inactivate_group_no) + InactivateGoal(pGoal); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" ); + } + } + + // Check Remove all in the group flag + if (Goal->remove_group_no != 0) + { + // Find all goals + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" ); + while ( pEnt ) + { + CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt ); + if ( pGoal ) + { + if (pGoal->group_no == Goal->remove_group_no) + RemoveGoal(pGoal); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" ); + } + } + + // Check Restore all in the group flag + if (Goal->restore_group_no != 0) + { + // Find all goals + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_tfgoal" ); + while ( pEnt ) + { + CTFGoal *pGoal = dynamic_cast< CTFGoal* >( pEnt ); + if ( pGoal ) + { + if (pGoal->group_no == Goal->restore_group_no) + RestoreGoal(pGoal); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_tfgoal" ); + } + } + +#ifdef MAP_DEBUG + if (Goal->remove_spawngroup || Goal->restore_spawngroup) + Warning("Doing SpawnGroupwork...\n"); +#endif +} + + + +//========================================================================= +// Do all the activation/inactivation/etc of individual Goals +void DoGoalWork(CTFGoal *Goal, CTFPlayer *AP) +{ +#ifdef MAP_DEBUG + if (Goal->activate_goal_no || Goal->inactivate_goal_no || Goal->restore_goal_no || Goal->remove_goal_no || Goal->return_item_no) + Warning("Doing Goalwork...\n"); +#endif + + // If another goal should be activated, activate it + if (Goal->activate_goal_no != 0) + { + CTFGoal *pFoundGoal = Findgoal(Goal->activate_goal_no); + if (pFoundGoal) + ActivateDoResults(pFoundGoal, AP, Goal); + } + + // If another goal should be inactivated, inactivate it + if (Goal->inactivate_goal_no != 0) + { + CTFGoal *pFoundGoal = Findgoal(Goal->inactivate_goal_no); + if (pFoundGoal) + InactivateGoal(pFoundGoal); + } + + // If another goal should be restored, restore it + if (Goal->restore_goal_no != 0) + { + CTFGoal *pFoundGoal = Findgoal(Goal->restore_goal_no); + if (pFoundGoal) + RestoreGoal(pFoundGoal); + } + + // If another goal should be removed, remove it + if (Goal->remove_goal_no != 0) + { + CTFGoal *pFoundGoal = Findgoal(Goal->remove_goal_no); + if (pFoundGoal) + RemoveGoal(pFoundGoal); + } + + // If a GoalItem should be returned, return it + if (Goal->return_item_no != 0) + { + CTFGoalItem *pFoundGoal = Finditem(Goal->return_item_no); + if (pFoundGoal) + { + CBaseEntity *pOwner = pFoundGoal->GetOwnerEntity(); + Assert( dynamic_cast<CTFPlayer*>( pOwner ) ); + if (pFoundGoal->goal_state == TFGS_ACTIVE) + tfgoalitem_RemoveFromPlayer(pFoundGoal, (CTFPlayer*)pOwner, GI_DROP_REMOVEGOAL); + + // Setup a ReturnItem timer + CTimer *pTimer = Timer_CreateTimer( pFoundGoal, TF_TIMER_RETURNITEM ); + pTimer->weapon = GI_RET_TIME; + pTimer->m_flNextThink = gpGlobals->curtime + 0.1; + + pFoundGoal->AddSolidFlags( FSOLID_NOT_SOLID ); + } + } + +#ifdef MAP_DEBUG + if (Goal->remove_spawnpoint || Goal->restore_spawnpoint) + Warning("Doing Spawnwork...\n"); +#endif + + // Spawnpoint behaviour + if (Goal->remove_spawnpoint != 0) + { + CTFSpawn *pFoundGoal = Findteamspawn(Goal->remove_spawnpoint); + if (pFoundGoal) + InactivateSpawn(pFoundGoal); + } + + if (Goal->restore_spawnpoint != 0) + { + CTFSpawn *pFoundGoal = Findteamspawn(Goal->restore_spawnpoint); + if (pFoundGoal) + { + if (pFoundGoal->goal_state == TFGS_REMOVED) + ActivateSpawn(pFoundGoal); + } + } +} + + +//========================================================================= +// Do all the activation/removal of Quake Triggers +void DoTriggerWork(CTFGoal *Goal, CTFPlayer *AP) +{ + // remove killtargets + if (Goal->killtarget != NULL_STRING) + { + MDEBUG( Warning("Doing Triggerwork...\n") ); + MDEBUG( Warning("Killing Target(s): %s\n", STRING(Goal->killtarget)) ); + + CBaseEntity *pentKillTarget = gEntList.FindEntityByName( NULL, STRING(Goal->killtarget) ); + while ( pentKillTarget ) + { + UTIL_Remove( pentKillTarget ); + pentKillTarget = gEntList.FindEntityByName( pentKillTarget, STRING(Goal->killtarget) ); + } + } + + // fire targets + if (Goal->target != NULL_STRING) + { + MDEBUG( Warning("Doing Triggerwork...\n") ); + MDEBUG( ALERT( at_console, "Activating Target(s): %s\n", STRING(Goal->pev->target) ) ); + + CBaseEntity *pentTarget = gEntList.FindEntityByName( NULL, STRING(Goal->target) ); + while ( pentTarget ) + { + CBaseEntity *pTarget = pentTarget; + if ( !(pTarget->GetFlags() & FL_KILLME) ) + pTarget->Use( AP, Goal, USE_TOGGLE, 0 ); + pentTarget = gEntList.FindEntityByName( pentTarget, STRING(Goal->target) ); + } + } +} + + +//========================================================================= +// Setup the way this Timer/Goal/Item will respawn +void SetupRespawn(CTFGoal *pGoal) +{ + MDEBUG( Warning("Setting up Respawn...\n") ); + + pGoal->m_bAddBonuses = FALSE; + + // Check status of respawn for this goal + // Single Activation, do nothing + if (pGoal->goal_result & TFGR_SINGLE) + { + RemoveGoal(pGoal); + return; + } + + // Timer Goal? + if (pGoal->Classify() == CLASS_TFGOAL_TIMER) + { + InactivateGoal(pGoal); + pGoal->SetThink( &CTFGoal::tfgoal_timer_tick ); + pGoal->SetNextThink( gpGlobals->curtime + pGoal->search_time ); + return; + } + + // Respawn Activation, set up respawn + if (pGoal->wait > 0) + { + pGoal->SetThink(&CTFGoal::DoRespawn); + pGoal->SetNextThink( gpGlobals->curtime + pGoal->wait ); + return; + } + // Permanently active goal? + else if (pGoal->wait == -1) + return; + + // Otherwise, it's a Multiple Goal + InactivateGoal(pGoal); +} + + +//========================================================================= +// Do the results for the Timer/Goal/Item +void DoResults(CTFGoal *Goal, CTFPlayer *AP, BOOL bAddBonuses) +{ + // Can't activate during PreMatch time + if ( (TFGameRules()->IsInPreMatch()) && (Goal->Classify() != CLASS_TFGOAL_TIMER) ) + return; + + // Is the goal already activated? + // This check is needed for goals which are being activated by other goals + if (Goal->goal_state == TFGS_ACTIVE) + return; + + // Delayed Activation? + if (Goal->delay_time > 0 && Goal->goal_state != TFGS_DELAYED) + { + MDEBUG( Warning("Delaying Results of %s\n", STRING(Goal->edict()->netname)) ); + + Goal->goal_state = TFGS_DELAYED; + Timer_CreateTimer( Goal, TF_TIMER_DELAYEDGOAL ); + Goal->enemy = AP; + Goal->SetThink( &CTFGoal::DelayedResult ); + Goal->SetNextThink( gpGlobals->curtime + Goal->delay_time ); + Goal->weapon = bAddBonuses; + return; + } + + // If we have a teamcheck entity, use it instead + if ( Goal->owned_by_teamcheck != NULL_STRING ) + Goal->owned_by = GetTeamCheckTeam( STRING(Goal->owned_by_teamcheck) ); + + Goal->goal_state = TFGS_INACTIVE; + + + // if it's a TF goal, removes it's model + if ( Goal->Classify() == CLASS_TFGOAL || Goal->Classify() == CLASS_TFGOAL_TIMER ) + Goal->AddEffects( EF_NODRAW ); + +#ifdef MAP_DEBUG + Warning("---= Activation =---\n"); + if (AP) + Warning("Goal: %s\nAP : %s\n", STRING(Goal->edict()->netname), AP->GetPlayerName()); + else + Warning("Goal: %s\nAP : NONE\n", STRING(Goal->edict()->netname)); + if (bAddBonuses) + Warning(" adding bonuses\n-=================-\n"); + else + Warning("NOT adding bonuses\n-=================-\n"); +#endif + + // Make the sound + if (Goal->noise != NULL_STRING) + { + Goal->EmitSound( STRING( Goal->noise ) ); + } + + // Increase scores + BOOL bDumpScores = FALSE; + int i; + for ( i = 0; i <= 3; i++) + { + if (Goal->increase_team[i] != 0) + { + TeamFortress_TeamIncreaseScore(i + 1, Goal->increase_team[i]); + bDumpScores = TRUE; + } + } + + // Increase the score of the team that owns this entity + if ( ( Goal->increase_team_owned_by != 0 ) && ( Goal->owned_by != 0 ) ) + { + TeamFortress_TeamIncreaseScore( Goal->owned_by, Goal->increase_team_owned_by ); + bDumpScores = TRUE; + } + + // CTF Map support + if (TFGameRules()->CTF_Map == TRUE && AP != NULL) + { + if (Goal->goal_no == CTF_FLAG1 || Goal->goal_no == CTF_FLAG1 || Goal->goal_no == CTF_DROPOFF1 || Goal->goal_no == CTF_DROPOFF1) + { + // Do Messaging + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); + if ( !pPlayer ) + continue; + + if ( (pPlayer->GetTeamNumber() == 2 && Goal->goal_no == CTF_FLAG1) || (pPlayer->GetTeamNumber() == 1 && Goal->goal_no == CTF_FLAG2) ) + { + if (pPlayer == AP) + ClientPrint( pPlayer, HUD_PRINTCENTER, "You got the enemy flag!\n\nReturn to base!"); + else + ClientPrint( pPlayer, HUD_PRINTCENTER, "Your team GOT the ENEMY flag!!"); + } + else if (Goal->goal_no == CTF_FLAG1 || Goal->goal_no == CTF_FLAG2) + { + ClientPrint( pPlayer, HUD_PRINTCENTER, "Your flag has been TAKEN!!"); + } + else if ( (pPlayer->GetTeamNumber() == 2 && Goal->goal_no == CTF_DROPOFF1) || (pPlayer->GetTeamNumber() == 1 && Goal->goal_no == CTF_DROPOFF2) ) + { + if (pPlayer == AP) + ClientPrint( pPlayer, HUD_PRINTCENTER, "You CAPTURED the FLAG!!"); + else + ClientPrint( pPlayer, HUD_PRINTCENTER, "Your flag was CAPTURED!!"); + } + else if (Goal->goal_no == CTF_DROPOFF1 || Goal->goal_no == CTF_DROPOFF2) + { + ClientPrint( pPlayer, HUD_PRINTCENTER, "Your team CAPTURED the flag!!"); + } + } + + const char *pTeamName = "Spectator"; + if ( AP->GetTeamNumber() != 0 ) + { + CTeam *pTeam = GetGlobalTeam( AP->GetTeamNumber() ); + if ( pTeam ) + pTeamName = pTeam->GetName(); + } + + // Console Prints + switch(Goal->goal_no) + { + case CTF_FLAG1: + UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s GOT the BLUE flag!", AP->GetPlayerName()) ); + + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Stole_Blue_Flag\"\n", + AP->GetPlayerName(), + AP->GetUserID(), + pTeamName ); + + AP->m_Shared.AddItemFlags( IT_KEY1 ); + break; + case CTF_FLAG2: + UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s GOT the RED flag!", AP->GetPlayerName()) ); + + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Stole_Red_Flag\"\n", + AP->GetPlayerName(), + AP->GetUserID(), + pTeamName ); + + AP->m_Shared.AddItemFlags( IT_KEY2 ); + break; + case CTF_DROPOFF1: + UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s CAPTURED the RED flag!", AP->GetPlayerName()) ); + + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Captured_Red_Flag\"\n", + AP->GetPlayerName(), + AP->GetUserID(), + pTeamName ); + + AP->m_Shared.RemoveItemFlags( IT_KEY2 ); + break; + case CTF_DROPOFF2: + UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s CAPTURED the BLUE flag!", AP->GetPlayerName()) ); + + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Captured_Blue_Flag\"\n", + AP->GetPlayerName(), + AP->GetUserID(), + pTeamName ); + + AP->m_Shared.RemoveItemFlags( IT_KEY1 ); + break; + default: + break; + } + } + } + + // Do Spawnpoint work before cycling players, so Forced respawn players work correctly. + if (Goal->remove_spawngroup != 0) + { + // Find all goals + //TFTODO: I think FindEntityByClassname will do the same thing. + //CBaseEntity *pEnt = UTIL_FindEntityByString( NULL, "netname", "info_player_teamspawn" ); + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_player_teamspawn" ); + while ( pEnt ) + { + CTFSpawn *pSpawn = dynamic_cast<CTFSpawn*>( pEnt ); + if ( pSpawn ) + { + if ( pSpawn->group_no == Goal->remove_spawngroup) + InactivateSpawn(pSpawn); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_player_teamspawn" ); + } + } + + if (Goal->restore_spawngroup != 0) + { + // Find all goals + //TFTODO: I think FindEntityByClassname will do the same thing. + //CBaseEntity *pEnt = UTIL_FindEntityByString( NULL, "netname", "info_player_teamspawn" ); + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "info_player_teamspawn" ); + while ( pEnt ) + { + CTFSpawn *pSpawn = dynamic_cast<CTFSpawn*>( pEnt ); + if ( pSpawn ) + { + if (pSpawn->group_no == Goal->restore_spawngroup) + ActivateSpawn(pSpawn); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_player_teamspawn" ); + } + } + + // Go through all the players and do any results + if ( Goal->broadcast != NULL_STRING && TFGameRules()->CTF_Map == FALSE ) + { + UTIL_LogPrintf("World triggered \"%s\"\n", STRING(Goal->broadcast) ); + } + if ( Goal->netname_broadcast != NULL_STRING && TFGameRules()->CTF_Map == FALSE && AP != NULL ) + { + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"%s\"\n", + AP->GetPlayerName(), + AP->GetUserID(), + ( AP->GetTeamNumber() != 0 ) ? GetTeamName( AP->GetTeamNumber() ) : "Spectator", + STRING(Goal->netname_broadcast) ); + } + + BOOL bGotOne = FALSE; + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); + if ( !pPlayer ) + continue; + + // Centerprinting + if (Goal->broadcast != NULL_STRING && TFGameRules()->CTF_Map == FALSE) + UTIL_ShowMessage( STRING(Goal->broadcast), pPlayer ); + if (Goal->netname_broadcast != NULL_STRING && TFGameRules()->CTF_Map == FALSE && AP != NULL) + ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_broadcast), AP->GetPlayerName() ); + // Old printing + if (Goal->org_broadcast != NULL_STRING && TFGameRules()->CTF_Map == FALSE) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_broadcast) ); + + // VOX + if (Goal->speak != NULL_STRING) + pPlayer->ClientHearVox( STRING(Goal->speak) ); + + if (AP == pPlayer) + { + // Spawnpoints handle their own printing elsewhere + if (Goal->message != NULL_STRING && Goal->Classify() != CLASS_TFSPAWN) + UTIL_ShowMessage( STRING(Goal->message), pPlayer ); + if (Goal->org_message != NULL_STRING && Goal->Classify() != CLASS_TFSPAWN) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_message) ); + + // VOX + if (Goal->AP_speak != NULL_STRING) + pPlayer->ClientHearVox( STRING(Goal->AP_speak) ); + } + else if ( (AP != NULL) && (AP->GetTeamNumber() == pPlayer->GetTeamNumber()) ) + { + // Text Printing + if (Goal->owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by) + UTIL_ShowMessage( STRING(Goal->owners_team_broadcast), pPlayer ); + else if (Goal->non_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by) + UTIL_ShowMessage( STRING(Goal->non_owners_team_broadcast), pPlayer ); + else if (Goal->team_broadcast!= NULL_STRING ) + UTIL_ShowMessage( STRING(Goal->team_broadcast), pPlayer ); + // Old Text Printing + if (Goal->org_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_owners_team_broadcast) ); + else if (Goal->org_non_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_non_owners_team_broadcast) ); + else if (Goal->org_team_broadcast!= NULL_STRING ) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_team_broadcast) ); + + + // VOX + if (Goal->owners_team_speak != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by) + pPlayer->ClientHearVox( STRING(Goal->owners_team_speak) ); + else if (Goal->non_owners_team_speak != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by) + pPlayer->ClientHearVox( STRING(Goal->non_owners_team_speak) ); + else if (Goal->team_speak!= NULL_STRING ) + pPlayer->ClientHearVox( STRING(Goal->team_speak) ); + + if (Goal->netname_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by) + ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_owners_team_broadcast), AP->GetPlayerName() ); + else if (Goal->netname_team_broadcast!= NULL_STRING ) + ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_team_broadcast), AP->GetPlayerName() ); + } + else + { + // Text Printing + if (Goal->owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by) + UTIL_ShowMessage( STRING(Goal->owners_team_broadcast), pPlayer ); + else if (Goal->non_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by) + UTIL_ShowMessage( STRING(Goal->non_owners_team_broadcast), pPlayer ); + else if (Goal->non_team_broadcast != NULL_STRING ) + UTIL_ShowMessage( STRING(Goal->non_team_broadcast), pPlayer ); + // Old Text Printing + if (Goal->org_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_owners_team_broadcast) ); + else if (Goal->org_non_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_non_owners_team_broadcast) ); + else if (Goal->org_non_team_broadcast!= NULL_STRING ) + ClientPrint( pPlayer, HUD_PRINTCENTER, STRING(Goal->org_non_team_broadcast) ); + + // VOX + if (Goal->owners_team_speak != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by) + pPlayer->ClientHearVox( STRING(Goal->owners_team_speak) ); + else if (Goal->non_owners_team_speak != NULL_STRING && pPlayer->GetTeamNumber() != Goal->owned_by) + pPlayer->ClientHearVox( STRING(Goal->non_owners_team_speak) ); + else if (Goal->non_team_speak != NULL_STRING ) + pPlayer->ClientHearVox( STRING(Goal->non_team_speak) ); + + if (Goal->netname_owners_team_broadcast != NULL_STRING && pPlayer->GetTeamNumber() == Goal->owned_by && AP != NULL) + ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_owners_team_broadcast), AP->GetPlayerName() ); + else if (Goal->netname_non_team_broadcast != NULL_STRING && AP != NULL) + ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_non_team_broadcast), AP->GetPlayerName() ); + } + + if (IsAffectedBy(Goal, pPlayer, AP)) + { + // If its a Timer Goal, see if it needs to check Criteria again + if (Goal->search_time != 0 && Goal->goal_effects & TFGE_TIMER_CHECK_AP) + { + if (APMeetsCriteria(Goal, pPlayer)) + { + Apply_Results(Goal, (CTFPlayer*)pPlayer, AP, bAddBonuses); + bGotOne = TRUE; + } + } + else + { + Apply_Results(Goal, (CTFPlayer*)pPlayer, AP, bAddBonuses); + bGotOne = TRUE; + } + } + } + +#ifdef MAP_DEBUG + if (bGotOne == FALSE) + Warning("NO PLAYERS AFFECTED\n"); +#endif + + // Goal is now active + // Items are not set to active. They handle their modes. + if ( Goal->Classify() == CLASS_TFGOAL_TIMER || Goal->Classify() == CLASS_TFGOAL ) + Goal->goal_state = TFGS_ACTIVE; + + // EndGame checking + if (Goal->goal_result & TFGR_ENDGAME) + { + // Display Long TeamScores to everyone +// TeamFortress_TeamShowScores(TRUE, NULL); + TFTeamMgr()->ShowScores(); + + if ( g_pGameRules->IsMultiplayer() ) + TFGameRules()->TFGoToIntermission(); + return; + } + + // EndRound checking + if (Goal->m_flEndRoundTime) + EndRound( Goal ); + + // Do Goal Group checking + DoGroupWork(Goal, AP); + + // Do Goal checking + DoGoalWork(Goal, AP); + + // Do Quake Trigger actions (Standard entities use SUB_UseTargets()) + if ( Goal->Classify() == CLASS_TFGOAL_TIMER || Goal->Classify() == CLASS_TFGOAL_ITEM || Goal->Classify() == CLASS_TFGOAL || Goal->Classify() == CLASS_TFSPAWN || Goal->do_triggerwork ) + DoTriggerWork(Goal, AP); + + // Setup for Respawn + // Items, Triggers, and Spawnpoints do their own respawn work + if ( Goal->Classify() == CLASS_TFGOAL || Goal->Classify() == CLASS_TFGOAL_TIMER ) + SetupRespawn(Goal); +} + + +//========================================================================= +// Check to see if the Goal should Activate. Handle Else Goals if not. +// If it does activate, Do the Results. Return true if the Goal activated. +bool ActivateDoResults(CTFGoal *Goal, CTFPlayer *AP, CTFGoal *ActivatingGoal) +{ + // Check Goal activation. This func handles Else Goals. + if ( !ActivationSucceeded(Goal, AP, ActivatingGoal) ) + return false; + + // Do the Results. + if (ActivatingGoal == Goal || Goal->m_bAddBonuses == true) + DoResults(Goal, AP, true); + else if (ActivatingGoal != NULL) + DoResults(Goal, AP, (ActivatingGoal->goal_result & TFGR_ADD_BONUSES)); + else + DoResults(Goal, AP, 0); + + return true; +} + + +//========================================================================= +// Return true if the Goal should activate, and handle Else Goals +bool ActivationSucceeded(CTFGoal *Goal, CTFPlayer *AP, CTFGoal *ActivatingGoal) +{ + // Can't activate during PreMatch time, except for timers + if ( (TFGameRules()->IsInPreMatch()) && (Goal->Classify() != CLASS_TFGOAL_TIMER) ) + return false; + + // If activation fails, try and activate the Else Goal + if ( !ShouldActivate(Goal, AP) ) + { + // If an else goal should be activated, activate it + if (Goal->else_goal != 0) + { + MDEBUG( Warning(" Else Goal.\n") ); + + CTFGoal *pElseGoal = Findgoal(Goal->else_goal); + if (pElseGoal) + ActivateDoResults(pElseGoal, AP, Goal); + } + + return false; + } + + return true; +} + + +// ---------------------------------------------------------------------------------------- // +// CTFBaseItem existence. +// ---------------------------------------------------------------------------------------- // + +//=========================================== +// Check whether this entity should exist at this skill +bool CTFBaseItem::CheckExistence() +{ + if (ex_skill_min == -1 && g_iSkillLevel < 0) + return FALSE; + else if (ex_skill_max == -1 && g_iSkillLevel > 0) + return FALSE; + + if ( (ex_skill_min != 0) && (ex_skill_min != -1) && (g_iSkillLevel < ex_skill_min) ) + return FALSE; + else if ( (ex_skill_max != 0) && (ex_skill_max != -1) && (g_iSkillLevel > ex_skill_max) ) + return FALSE; + + return TRUE; +} + + +// ---------------------------------------------------------------------------------------- // +// CTFGoal implementation. +// ---------------------------------------------------------------------------------------- // + +LINK_ENTITY_TO_CLASS(info_tfgoal, CTFGoal); + +BEGIN_DATADESC( CTFGoal ) + DEFINE_FUNCTION( PlaceGoal ), + DEFINE_FUNCTION( DelayedResult ) +END_DATADESC() + + + +//=========================================== +// TF Goal spawn +void CTFGoal::Spawn( void ) +{ + if (CheckExistence() == false) + { + UTIL_Remove(this); + return; + } + + // Graphic + string_t modelName = GetModelName(); + if ( modelName != NULL_STRING ) + { + // Brush Models need to be invisible + const char *pModel = STRING( modelName ); + if (pModel[0] == '*') + AddEffects( EF_NODRAW ); + } + +#ifdef TFTODO + // Activation sound + if (pev->noise) + PRECACHE_SOUND( (char*)STRING(pev->noise) ); + + // For the powerups + PRECACHE_SOUND("items/protect.wav"); + PRECACHE_SOUND("items/protect2.wav"); + PRECACHE_SOUND("items/protect3.wav"); + PRECACHE_SOUND("FVox/HEV_logon.wav"); + PRECACHE_SOUND("FVox/hev_shutdown.wav"); + PRECACHE_SOUND("items/inv1.wav"); + PRECACHE_SOUND("items/inv2.wav"); + PRECACHE_SOUND("items/inv3.wav"); + PRECACHE_SOUND("items/damage.wav"); + PRECACHE_SOUND("items/damage2.wav"); + PRECACHE_SOUND("items/damage3.wav"); +#endif + + // Set initial states + AddSolidFlags( FSOLID_TRIGGER ); + if (goal_state == 0) + goal_state = TFGS_INACTIVE; + + // Set Size + if (goal_min != vec3_origin && goal_max != vec3_origin) + UTIL_SetSize( this, goal_min, goal_max ); + + StartGoal(); +} + + +//========================================================================= +// Respawn the goal +void CTFGoal::DoRespawn() +{ + RestoreGoal(this); + InactivateGoal(this); +} + + +//========================================================================= +// Timer goal tick +void CTFGoal::tfgoal_timer_tick() +{ + // Check criteria + if (goal_state != TFGS_REMOVED) + { + #ifdef MAP_DEBUG + Warning("==========================\n"); + Warning("Timer Tick for: %s\nChecking Criteria...", GetEntityName().ToCStr()); + #endif + + // Timers don't fire during prematch. + // Instead, they setup to fire the correct amount of time past the prematch + if ( TFGameRules()->IsInPreMatch() ) + { + MDEBUG( Warning("\n PREMATCH IS ON. DELAYING UNTIL AFTER PREMATCH.\n") ); + + SetThink( &CTFGoal::tfgoal_timer_tick ); + SetNextThink( TFGameRules()->GetPreMatchEndTime() + search_time ); + return; + } + + if (APMeetsCriteria(this, NULL)) + { + DoResults(this, NULL, TRUE); + } + else + { + MDEBUG( Warning("\n") ); + SetThink( &CTFGoal::tfgoal_timer_tick ); + SetNextThink( gpGlobals->curtime + search_time ); + } + } +} + + +//========================================================================= +// Use (Triggered) function for Goals +void CTFGoal::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + // If the Activator isn't a player, pretend there isn't an activator + if (pActivator && !pActivator->IsPlayer()) + { + pActivator = NULL; + } + + CTFGoal *pCallerGoal = dynamic_cast<CTFGoal*>( pCaller ); + if ( !pCallerGoal ) + { + // Need to rethink some stuff if this can be called by entities that aren't CTFGoals. + Assert( false ); + return; + } + + // Goals are only activatable by players + if (!pActivator || pActivator->IsPlayer()) + { + // Force it to add bonuses + m_bAddBonuses = true; + ActivateDoResults(this, (CTFPlayer*)pActivator, pCallerGoal); + } +} + + +//=========================================== +// Make Goal's more easy to touch +void CTFGoal::SetObjectCollisionBox( void ) +{ + const char *pModel = STRING( GetModelName() ); + if (pModel[0] != '*') + { + Vector vMins = WorldAlignMins() + Vector(-24, -24, 0); + Vector vMaxs = WorldAlignMaxs() + Vector(24, 24, 16); + SetCollisionBounds( vMins, vMaxs ); + } + else + { +// Do we even need to do this? The bmodel should be setup correctly at this point anyway. +#ifdef TFTODO + // Ripped from ::SetObjectCollisionBox + float max, v; + int i; + + max = 0; + for (i=0 ; i<3 ; i++) + { + v = fabs( (( float * )pev->mins )[i]); + if (v > max) + max = v; + v = fabs( (( float * )pev->maxs )[i]); + if (v > max) + max = v; + } + for (i=0 ; i<3 ; i++) + { + ((float *)pev->absmin)[i] = (( float * )GetAbsOrigin())[i] - max; + ((float *)pev->absmax)[i] = (( float * )GetAbsOrigin())[i] + max; + } + + pev->absmin.x -= 1; + pev->absmin.y -= 1; + pev->absmin.z -= 1; + pev->absmax.x += 1; + pev->absmax.y += 1; + pev->absmax.z += 1; +#endif + } +} + +//=========================================== +// Start the Goal +void CTFGoal::StartGoal( void ) +{ + m_bAddBonuses = false; + SetThink( &CTFGoal::PlaceGoal ); + SetNextThink( gpGlobals->curtime + 0.2 ); // goals start after other solids + + if (goal_state == TFGS_REMOVED) + RemoveGoal(this); +}; + +//=========================================== +// Sets up the Goal's first thoughts +void CTFGoal::PlaceGoal( void ) +{ + if ( FClassnameIs(this, "info_tfgoal_timer") ) + { + // Set up the next Timer Tick + SetThink( &CTFGoal::tfgoal_timer_tick ); + SetNextThink( gpGlobals->curtime + search_time ); + } + else + { + // Only give touch functions to goals that can be activated by touch + if (goal_activation & TFGA_TOUCH) + SetTouch( &CTFGoal::tfgoal_touch ); + } + + // So searches for this goal work later on + Assert( stricmp( GetClassname(), "info_tfgoal" ) == 0 ); + + // Drop to ground + if (goal_activation & TFGA_DROPTOGROUND) + { +// Is this right? +#ifdef TFTODO + SetMoveType( MOVETYPE_TOSS ); +#else + SetMoveType( MOVETYPE_FLYGRAVITY ); +#endif + SetAbsOrigin( GetAbsOrigin() + Vector( 0, 0, 6 ) ); + + if ( UTIL_DropToFloor(this, MASK_SOLID) == 0) + { + Error("TF Goal %s fell out of level at %f,%f,%f", GetEntityName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z); + UTIL_Remove( this ); + return; + } + } + + SetMoveType( MOVETYPE_NONE ); + SetAbsVelocity( Vector( 0, 0, 0 ) ); + oldorigin = GetAbsOrigin(); // So we can return it later +} + + +// Touch function for Goals +void CTFGoal::tfgoal_touch( CBaseEntity *pOther ) +{ + // Can't touch during PreMatch time + if ( TFGameRules()->IsInPreMatch() ) + return; + // If it is not activated in by the player's touch, return + if (!(goal_activation & TFGA_TOUCH)) + return; + // Only activatable by a player + if (!pOther->IsPlayer()) + return; + if ( pOther->IsAlive() == FALSE ) + return; + + // If it's already active, don't bother + if (goal_state == TFGS_ACTIVE) + { + MDEBUG( Warning("Goal already active. aborting touch.\n") ); + return; + } + + // CTF Hack to make sure the key is in place. Like the rest of the CTF_Map stuff, + // it's not needed... the base scripting could handle it all. + if (TFGameRules()->CTF_Map) + { + if ((goal_no == CTF_DROPOFF1) && (pOther->GetTeamNumber() == 1)) + { + CTFGoalItem *pFlag = Finditem(CTF_FLAG1); + if ((pFlag->goal_state == TFGS_ACTIVE) || (pFlag->GetAbsOrigin() != pFlag->oldorigin)) + return; + } + if ((goal_no == CTF_DROPOFF2) && (pOther->GetTeamNumber() == 2)) + { + CTFGoalItem *pFlag = Finditem(CTF_FLAG2); + if ((pFlag->goal_state == TFGS_ACTIVE) || (pFlag->GetAbsOrigin() != pFlag->oldorigin)) + return; + } + } + + ActivateDoResults(this, ToTFPlayer( pOther ), this); +} + +//========================================================================= +// Handles Delayed Activation of Goals +void CTFGoal::DelayedResult() +{ + CBaseEntity *pEnemy = enemy; + if (goal_state == TFGS_DELAYED) + DoResults(this, ToTFPlayer( pEnemy ), weapon); +} + + +// ---------------------------------------------------------------------------------------- // +// CTFGoalItem implementation. +// ---------------------------------------------------------------------------------------- // + +LINK_ENTITY_TO_CLASS(item_tfgoal, CTFGoalItem); + + +//========================================================================= +// Spawn a goalitem entity +void CTFGoalItem::Spawn( void ) +{ + if (CheckExistence() == false) + { + UTIL_Remove(this); + return; + } + + // Set this in case they used abbreviations + Assert( stricmp( GetClassname(), "item_tfgoal" ) == 0 ); + + // Graphic + if ( GetModelName() != NULL_STRING ) + { + // Setup animations + SetSequence( LookupSequence( "not_carried" ) ); + if ( GetSequence() != -1 ) + { + ResetSequenceInfo(); + SetCycle( 0.0f ); + } + } + +#ifdef TFTODO + // Respawn sound + PRECACHE_SOUND("items/itembk2.wav"); + + // Activation sound + if (pev->noise) + PRECACHE_SOUND( (char*)STRING(pev->noise) ); + + if (!(pev->netname)) + pev->netname = MAKE_STRING("goalitem"); +#endif + + if (goal_state == 0) + goal_state = TFGS_INACTIVE; + + // Set initial solidity + if (goal_activation & TFGI_SOLID) + { + SetSolid( SOLID_BBOX ); + // Solid goalitems need a bbox + if (goal_min == vec3_origin) + goal_min = Vector(-16, -16, -24); + if (goal_max == vec3_origin) + goal_max = Vector(16, 16, 32); + } + else + { + SetSolidFlags( FSOLID_TRIGGER ); + } + + if (drop_time <= 0) + drop_time = 60; + + // Set Size + UTIL_SetSize(this, goal_min, goal_max); + + SetTouch( &CTFGoalItem::item_tfgoal_touch ); + StartItem(); +}; + +//========================================================================= +// Start the Goal Item +void CTFGoalItem::StartItem( void ) +{ + SetThink( &CTFGoalItem::PlaceItem ); + SetNextThink( gpGlobals->curtime + 0.2 ); // items start after other solids + + if (goal_state == TFGS_REMOVED) + RemoveGoal(this); +} + +//=========================================== +// Place the Goal Item +void CTFGoalItem::PlaceItem( void ) +{ + static int item_list_bit = 1; // used to determine what the bit of each new GoalItem will be. + + SetAbsVelocity( vec3_origin ); + + // Drop to ground + if (goal_activation & TFGA_DROPTOGROUND) + { +#ifdef TFTODO + pev->movetype = MOVETYPE_TOSS; +#else + SetMoveType( MOVETYPE_FLYGRAVITY ); +#endif + SetAbsOrigin( GetAbsOrigin() + Vector( 0, 0, 6 ) ); + + if ( UTIL_DropToFloor( this, MASK_SOLID ) == 0) + { + Error("TF GoalItem %s fell out of level at %f,%f,%f", STRING( GetEntityName() ), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z); + UTIL_Remove( this ); + return; + } + } + + SetMoveType( MOVETYPE_NONE ); + oldorigin = GetAbsOrigin(); // So we can return it later + + if (goal_activation & TFGI_ITEMGLOWS) + { + m_nRenderFX = kRenderFxGlowShell; + + // If we have a teamcheck entity, use it instead + if ( owned_by_teamcheck != NULL_STRING ) + owned_by = GetTeamCheckTeam( STRING(owned_by_teamcheck) ); + +// if (owned_by > 0 && owned_by <= 4) +// m_clrRender = Vector255ToRGBColor( rgbcolors[owned_by] ); +// else +// m_clrRender = Vector255ToRGBColor( rgbcolors[0] ); + + if ( TFTeamMgr()->IsValidTeam( owned_by ) ) + { + CTFTeam *pTeam = TFTeamMgr()->GetTeam( owned_by ); + m_clrRender = pTeam->GetColor(); + } + else + { + m_clrRender = TFTeamMgr()->GetUndefinedTeamColor(); + } + + SetRenderColorA( 100 ); // Shell size + } + + // Set the item bit + item_list = item_list_bit; + item_list_bit *= 2; +} + + +// Touch function for the goalitem entity +void CTFGoalItem::item_tfgoal_touch( CBaseEntity *pOther ) +{ + if (!pOther->IsPlayer()) + return; + if ( pOther->IsAlive() == FALSE ) + return; + // Can't touch during PreMatch time + if ( TFGameRules()->IsInPreMatch() ) + return; + + // Hack to prevent feigning spies from repicking up flags + CTFPlayer *pPlayer = dynamic_cast<CTFPlayer*>( pOther ); + CTFPlayerClass *pPlayerClass = pPlayer->GetPlayerClass(); + if ( pPlayerClass && pPlayerClass->IsClass( TF_CLASS_SPY ) && pPlayerClass->Spy_IsFeigning() ) + return; + + // Prevent the dropping player from picking it up for longer + if (enemy.Get() && m_flDroppedAt != 0) + { + if ( (enemy == pOther) && m_flDroppedAt + 5 > gpGlobals->curtime ) + return; + } + m_flDroppedAt = 0; + + ASSERT( pOther != GetOwnerEntity() ); // There is no way in hell this should ever happen, and yet it still does. + + // Prevent picking up flags through thin walls + trace_t tr; + UTIL_TraceLine ( WorldSpaceCenter(), pOther->WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction != 1.0 && tr.m_pEnt != pOther ) + return; + + // CTF Hack to return your key. + // Should use the builtin support now. + if (TFGameRules()->CTF_Map == TRUE) + { + // Flag not at home? + if (GetAbsOrigin() != oldorigin) + { + if (GetTeamNumber() == 1) + { + if (goal_no == CTF_FLAG1) + { + UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s RETURNED the BLUE flag!", pPlayer->GetPlayerName()) ); + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Returned_Blue_Flag\"\n", + pPlayer->GetPlayerName(), + pPlayer->GetUserID(), + GetTeamName( pOther->GetTeamNumber() ) ); + } + else + { + UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs("%s RETURNED the RED flag!", pPlayer->GetPlayerName()) ); + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Returned_Red_Flag\"\n", + pPlayer->GetPlayerName(), + pPlayer->GetUserID(), + GetTeamName( pOther->GetTeamNumber() ) ); + } + + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( !pPlayer ) + continue; + + if (pPlayer->GetTeamNumber() == 1 && goal_no == CTF_FLAG1) + ClientPrint( pPlayer, HUD_PRINTCENTER, "Your flag was RETURNED!!\n"); + else if (pPlayer->GetTeamNumber() != 1 && goal_no == CTF_FLAG1) + ClientPrint( pPlayer, HUD_PRINTCENTER, "The ENEMY flag was RETURNED!!\n"); + else if (pPlayer->GetTeamNumber() == 2 && goal_no == CTF_FLAG2) + ClientPrint( pPlayer, HUD_PRINTCENTER, "Your flag was RETURNED!!\n"); + else if (pPlayer->GetTeamNumber() == 2 && goal_no == CTF_FLAG1) + ClientPrint( pPlayer, HUD_PRINTCENTER, "The ENEMY flag was RETURNED!!\n"); + } + + goal_state = TFGS_INACTIVE; + SetSolidFlags( FSOLID_TRIGGER ); + SetTouch( &CTFGoalItem::item_tfgoal_touch ); + SetAbsOrigin( oldorigin ); + + EmitSound( "GoalItem.Touch" ); + //EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/itembk2.wav", 1, ATTN_NORM, 0, 150 ); + return; + } + } + else + { + // Ignore touches to our own flag when it's at home + if (pOther->GetTeamNumber() == 1 && goal_no == CTF_FLAG1) + return; + if (pOther->GetTeamNumber() == 2 && goal_no == CTF_FLAG2) + return; + } + } + + // Activate. Handles Else Goals if it fails. + if ( ActivationSucceeded( this, ToTFPlayer( pOther ), NULL) ) + { + // Give it to the player + tfgoalitem_GiveToPlayer(this, ToTFPlayer( pOther ), this); + // It may have killed the player, so check: + if (pOther->GetHealth() > 0) + goal_state = TFGS_ACTIVE; + } +} + + +//========================================================================= +// Throw the item into the air at the specified position +void CTFGoalItem::DoDrop( Vector vecOrigin ) +{ + SetAbsOrigin( vecOrigin ); + + StopFollowingEntity(); + SetMoveType( MOVETYPE_FLYGRAVITY ); + + // Just drop it vertically first time to prevent it falling through walls too often + Vector vVel = GetAbsVelocity(); + vVel.z = 400; + if (redrop_count > 1) + { + // Second and third drops try pushing it in other directions + vVel.x = RandomFloat(-50, 50); + vVel.y = RandomFloat(-50, 50); + } + SetAbsVelocity( vVel ); + + goal_state = TFGS_INACTIVE; + SetAbsAngles( QAngle( 0, 0, 0 ) ); + if (goal_activation & TFGI_SOLID) + { + SetSolid( SOLID_BBOX ); + } + else + { + SetSolid( SOLID_BBOX ); + SetSolidFlags( FSOLID_TRIGGER ); + } + + RemoveEffects( EF_NODRAW ); + + UTIL_SetSize(this, goal_min, goal_max); + + redrop_count++; + + SetThink( &CTFGoalItem::tfgoalitem_dropthink ); // give it five seconds + SetNextThink( gpGlobals->curtime + 5.0 ); // and then find where it ended up +} + + +//========================================================================= +// Set the GoalItems touch func +void CTFGoalItem::tfgoalitem_droptouch() +{ + SetTouch( &CTFGoalItem::item_tfgoal_touch ); + SetThink( &CTFGoalItem::tfgoalitem_dropthink ); // give it five seconds since it was dropped + SetNextThink( gpGlobals->curtime + 4.25 ); // and then find where it ended up +} + + +//========================================================================= +// A quick check to make sure the items is not in a wall +void CTFGoalItem::tfgoalitem_dropthink() +{ + MDEBUG( Msg( "DropThink for %s\n", STRING(pev->netname)) ); + + StopFollowingEntity(); + SetMoveType( MOVETYPE_FLYGRAVITY ); + + if (drop_time != 0) + { + int iEnviron = UTIL_PointContents( GetAbsOrigin() ); + + if (iEnviron == CONTENTS_SLIME) + { + SetNextThink( gpGlobals->curtime + (drop_time / 4) ); + } +#ifdef TFTODO // CONTENTS_LAVA and CONTENTS_SKY don't exist in src. + else if (iEnviron == CONTENTS_LAVA) + { + SetNextThink( gpGlobals->curtime + 5 ); + } + else if (iEnviron == CONTENTS_SOLID || iEnviron == CONTENTS_SKY) +#else + else if (iEnviron == CONTENTS_SOLID) +#endif + { + // Its out of the world + // Retry a drop from the original position 3 times + if (redrop_count < 3) + { + // Retry the Drop + DoDrop( redrop_origin ); + return; + } + else + { + // Fourth time round, just return it + SetNextThink( gpGlobals->curtime + 2 ); + } + } + else + { + SetNextThink( gpGlobals->curtime + drop_time ); + } + + SetThink( &CTFGoalItem::tfgoalitem_remove ); + } +} + + +//========================================================================= +// Remove the item, or Return it if needed +void CTFGoalItem::tfgoalitem_remove() +{ + MDEBUG( Msg( "RemoveItem for %s...", STRING(pev->netname)) ); + + // Has someone picked it up? + if (goal_state == TFGS_ACTIVE) + { + MDEBUG( Msg( "Item picked up, exiting.\n") ); + return; + } + + // Should it be returned? + if (goal_activation & TFGI_RETURN_REMOVE) + { + MDEBUG( Msg( "Returned.\n") ); + + CTimer *pTimer = Timer_CreateTimer( this, TF_TIMER_RETURNITEM ); + pTimer->weapon = GI_RET_TIME; + pTimer->m_flNextThink = gpGlobals->curtime + 0.1; + //pTimer->SetThink( &CBaseEntity::ReturnItem ); this is done by CreateTimer code now. + return; + } + + MDEBUG( Msg( "Removed.\n") ); + UTIL_Remove(this); +} + +#if 0 +// ---------------------------------------------------------------------------------------------------- // +// CTFSpawn implementation. +// ---------------------------------------------------------------------------------------------------- // + +LINK_ENTITY_TO_CLASS(info_player_teamspawn_old, CTFSpawn); + +//=========================================== +void CTFSpawn::Spawn( void ) +{ + if (CheckExistence() == FALSE) + { + UTIL_Remove(this); + return; + } + + // Team spawnpoints must have a team associated with them + if ( (GetTeamNumber() <= 0 || GetTeamNumber() >= 5) && teamcheck == NULL_STRING) + { + Warning("Teamspawnpoint with an invalid GetTeamNumber() of %d\n", GetTeamNumber()); + return; + } + + // Save out the info_player_teamspawn + Assert( stricmp( GetClassname(), "info_player_teamspawn" ) == 0 ); +#ifdef TFTODO + pev->netname = pev->classname = MAKE_STRING( ); +#endif +} + +void CTFSpawn::Activate( void ) +{ + m_pTeamCheck = NULL; + + // Find the team check entity + CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, STRING(teamcheck) ); + + if ( pTarget ) + { + if ( !strcmp( pTarget->GetClassname(), "info_tf_teamcheck" ) ) + m_pTeamCheck = pTarget; + } +} + +BOOL CTFSpawn::CheckTeam( int iTeamNo ) +{ + // First check team number + if ( GetTeamNumber() ) + { + return ( iTeamNo == GetTeamNumber() ); + } + + // Then check the teamcheck + if ( m_pTeamCheck ) + { + CTeamCheck *pTeamCheck = dynamic_cast<CTeamCheck*>( m_pTeamCheck.Get() ); + Assert( pTeamCheck ); + if ( pTeamCheck->TeamMatches( iTeamNo ) == FALSE ) + return FALSE; + + return TRUE; + } + + return FALSE; +} +#endif + +// ---------------------------------------------------------------------------------------------------- // +// CBaseDelay implementation. +// ---------------------------------------------------------------------------------------------------- // + +LINK_ENTITY_TO_CLASS( DelayedUse, CBaseDelay ); + +void CBaseDelay::SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ) +{ + // TeamFortress Goal Checking + if (!pActivator || pActivator->IsPlayer() ) + DoResults(this, ToTFPlayer( pActivator ), TRUE); + + // + // exit immediatly if we don't have a target or kill target + // + if (target == NULL_STRING && !m_iszKillTarget) + return; + + // + // check for a delay + // + if (m_flDelay != 0) + { + // create a temp object to fire at a later time + CBaseDelay *pTemp = (CBaseDelay*)CreateEntityByName( "DelayedUse" ); + Assert( stricmp( pTemp->GetClassname(), "DelayedUse" ) == 0 ); + + pTemp->SetNextThink( gpGlobals->curtime + m_flDelay ); + pTemp->SetThink( &CBaseDelay::DelayThink ); + + // Save the useType + pTemp->button = (int)useType; + pTemp->m_iszKillTarget = m_iszKillTarget; + pTemp->m_flDelay = 0; // prevent "recursion" + pTemp->target = target; + return; + } + + // + // kill the killtargets + // + + if ( m_iszKillTarget != NULL_STRING ) + { + CBaseEntity *pentKillTarget = NULL; + + Msg( "KillTarget: %s\n", STRING(m_iszKillTarget) ); + pentKillTarget = gEntList.FindEntityByName( NULL, STRING(m_iszKillTarget) ); + while ( pentKillTarget ) + { + Msg( "killing %s\n", pentKillTarget->GetClassname() ); + + CBaseEntity *pNext = gEntList.FindEntityByName( pentKillTarget, STRING(m_iszKillTarget) ); + UTIL_Remove( pentKillTarget ); + pentKillTarget = pNext; + } + } + + // + // fire targets + // + if ( target != NULL_STRING ) + { + FireTargets( STRING(target), pActivator, this, useType, value ); + } +} + + +bool CBaseDelay::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (FStrEq(szKeyName, "delay")) + { + m_flDelay = atof( szValue ); + return true; + } + else if (FStrEq(szKeyName, "killtarget")) + { + m_iszKillTarget = MAKE_STRING(szValue); + return true; + } + else + { + return BaseClass::KeyValue( szKeyName, szValue ); + } +} + +void CBaseDelay::DelayThink( void ) +{ + // The use type is cached (and stashed) in pev->button + SUB_UseTargets( NULL, (USE_TYPE)button, 0 ); + UTIL_Remove( this ); +} + + +// ---------------------------------------------------------------------------------------------------- // +// CTeamCheck implementation. +// ---------------------------------------------------------------------------------------------------- // + +LINK_ENTITY_TO_CLASS(info_tf_teamcheck, CTeamCheck ); + + +void CTeamCheck::Spawn( void ) +{ +} + +void CTeamCheck::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + // Either Toggle or get set to a specific team number + if ( useType == USE_TOGGLE ) + { + if ( GetTeamNumber() == 1 ) + ChangeTeam( 2 ); + else + ChangeTeam( 1 ); + } + else if ( useType == USE_SET ) + { + if ( value >= 1 && value <= 4 ) + ChangeTeam( value ); + } +} + +BOOL CTeamCheck::TeamMatches( int iTeam ) +{ + return ( iTeam == GetTeamNumber() ); +} + +#endif |