You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3131 lines
88 KiB
3131 lines
88 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "tfc_mapitems.h" |
|
#include "tfc_shareddefs.h" |
|
#include "tfc_player.h" |
|
#include "tfc_gamerules.h" |
|
#include "tfc_timer.h" |
|
#include "tfc_team.h" |
|
|
|
|
|
bool ActivateDoResults(CTFGoal *Goal, CTFCPlayer *AP, CTFGoal *ActivatingGoal); |
|
bool ActivationSucceeded(CTFGoal *Goal, CTFCPlayer *AP, CTFGoal *ActivatingGoal); |
|
void DoResults(CTFGoal *Goal, CTFCPlayer *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, CTFCPlayer *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) ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//========================================================================= |
|
// 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; |
|
} |
|
|
|
|
|
//========================================================================= |
|
// 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 |
|
//TFCTODO: 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, CTFCPlayer *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.")); |
|
if (Goal->playerclass != AP->m_Shared.GetPlayerClass()) |
|
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, CTFCPlayer *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, CTFCPlayer *Player, CTFCPlayer *AP) |
|
{ |
|
// Don't affect anyone who isn't alive or is in Observer mode |
|
if (Player->m_Shared.GetPlayerClass() == PC_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, CTFCPlayer *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, CTFCPlayer *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, TFC_AMMO_SHELLS ); |
|
pPlayer->RemoveAmmo( Goal->ammo_nails, TFC_AMMO_NAILS ); |
|
pPlayer->RemoveAmmo( Goal->ammo_rockets, TFC_AMMO_ROCKETS ); |
|
pPlayer->RemoveAmmo( Goal->ammo_cells, TFC_AMMO_CELLS ); |
|
pPlayer->RemoveAmmo( Goal->ammo_medikit, TFC_AMMO_MEDIKIT ); |
|
pPlayer->RemoveAmmo( Goal->ammo_detpack, TFC_AMMO_DETPACK ); |
|
|
|
// Detpacks |
|
//TFCTODO: 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, TFC_AMMO_GRENADES1 ); |
|
pPlayer->RemoveAmmo( Goal->no_grenades_2, TFC_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( TFC_AMMO_GRENADES2 ) <= 0 || pPlayer->GetAmmoCount( TFC_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 |
|
pPlayer->TeamFortress_CheckClassStats(); |
|
//W_SetCurrentAmmo (); |
|
|
|
if (Goal->replacement_model != NULL_STRING) |
|
{ |
|
// is it the same goal that gave us the replacment model? |
|
if (pPlayer->replacement_model == Goal->replacement_model) |
|
{ |
|
pPlayer->replacement_model = NULL_STRING; |
|
pPlayer->replacement_model_body = 0; |
|
pPlayer->replacement_model_skin = 0; |
|
pPlayer->replacement_model_flags = 0; |
|
|
|
pPlayer->TeamFortress_SetSkin(); |
|
} |
|
} |
|
} |
|
|
|
|
|
//========================================================================= |
|
// Give the GoalItem to a Player. |
|
void tfgoalitem_GiveToPlayer(CTFGoalItem *Item, CTFCPlayer *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->m_flCycle = 0; |
|
} |
|
} |
|
|
|
Item->AddSolidFlags( FSOLID_NOT_SOLID ); |
|
|
|
// Do the deeds on the player |
|
if (Item->goal_activation & TFGI_GLOW) |
|
AP->AddEffects( EF_BRIGHTLIGHT ); //TFCTODO: 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, CTFCPlayer *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, CTFCPlayer *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) |
|
{ |
|
//TFCTODO: 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] ); |
|
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++ ) |
|
{ |
|
CTFCPlayer *pPlayer = ToTFCPlayer( 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->m_flCycle = 0; |
|
} |
|
} |
|
|
|
// 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, CTFCPlayer *Player, CTFCPlayer *AP, BOOL bAddBonuses) |
|
{ |
|
MDEBUG( Warning("Applying Results from %s to %s\n", STRING(Goal->pev->netname), STRING(Player->pev->netname)) ); |
|
|
|
// 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; |
|
else 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, TFC_AMMO_SHELLS ); |
|
Player->GiveAmmo( Goal->ammo_nails, TFC_AMMO_NAILS ); |
|
Player->GiveAmmo( Goal->ammo_rockets, TFC_AMMO_ROCKETS ); |
|
Player->GiveAmmo( Goal->ammo_cells, TFC_AMMO_CELLS ); |
|
Player->GiveAmmo( Goal->ammo_medikit, TFC_AMMO_MEDIKIT ); |
|
Player->GiveAmmo( Goal->ammo_detpack, TFC_AMMO_DETPACK ); |
|
|
|
#ifdef TFCTODO // 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); |
|
|
|
// Now apply the m_Shared.GetPlayerClass() limitations & Redisplay Ammo counts |
|
Player->TeamFortress_CheckClassStats(); |
|
} |
|
#ifdef MAP_DEBUG |
|
else |
|
ALERT( at_console, "NOT Adding bonuses.\n" ); |
|
#endif |
|
|
|
// If the Goal resets Spy skin/color then do it |
|
if (Player->m_Shared.GetPlayerClass() == PC_SPY && Goal->goal_result & TFGR_REMOVE_DISGUISE) |
|
{ |
|
//TFCTODO: looks like this isn't actually used anywhere. |
|
//Player->immune_to_check = gpGlobals->curtime + 10; |
|
Player->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, TFC_AMMO_DETPACK ); |
|
} |
|
} |
|
} |
|
|
|
// Force respawns |
|
if (Goal->goal_result & TFGR_FORCE_RESPAWN) |
|
{ |
|
// Only if they're alive |
|
if ( Player->IsAlive() ) |
|
Player->ForceRespawn(); |
|
} |
|
|
|
if (Goal->replacement_model != NULL_STRING) |
|
{ |
|
// if we don't already have a replacement_model |
|
if ( !Player->replacement_model ) |
|
{ |
|
Player->replacement_model = Goal->replacement_model; |
|
Player->replacement_model_body = Goal->replacement_model_body; |
|
Player->replacement_model_skin = Goal->replacement_model_skin; |
|
Player->replacement_model_flags = Goal->replacement_model_flags; |
|
|
|
Player->TeamFortress_SetSkin(); |
|
} |
|
} |
|
} |
|
|
|
|
|
//========================================================================= |
|
// 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); |
|
|
|
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 |
|
for ( int i = 1; i <= 4; i++ ) |
|
{ |
|
int teamScore = TeamFortress_TeamGetScoreFrags( i ); |
|
|
|
if ( teamScore > highestScore ) |
|
{ |
|
winningTeam = i; |
|
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 |
|
CTFCPlayer *client = NULL; |
|
while ( ((client = (CTFCPlayer*)gEntList.FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) ) |
|
{ |
|
if ( !client ) |
|
continue; |
|
|
|
// Freeze all the players |
|
if ( client->IsObserver() == FALSE ) |
|
{ |
|
//TFCTODO implement something for this? |
|
// iuser4 stops firing on the clients |
|
//client->pev->iuser4 = TRUE; |
|
|
|
//TFCTODO: 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, CTFCPlayer *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, CTFCPlayer *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<CTFCPlayer*>( pOwner ) ); |
|
if (pFoundGoal->goal_state == TFGS_ACTIVE) |
|
tfgoalitem_RemoveFromPlayer(pFoundGoal, (CTFCPlayer*)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, CTFCPlayer *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, CTFCPlayer *AP, BOOL bAddBonuses) |
|
{ |
|
// Can't activate during PreMatch time |
|
if ( (TFCGameRules()->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 (TFCGameRules()->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++ ) |
|
{ |
|
CTFCPlayer *pPlayer = ToTFCPlayer( 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 |
|
//TFCTODO: 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 |
|
//TFCTODO: 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 && TFCGameRules()->CTF_Map == FALSE ) |
|
{ |
|
UTIL_LogPrintf("World triggered \"%s\"\n", STRING(Goal->broadcast) ); |
|
} |
|
if ( Goal->netname_broadcast != NULL_STRING && TFCGameRules()->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++ ) |
|
{ |
|
CTFCPlayer *pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) ); |
|
if ( !pPlayer ) |
|
continue; |
|
|
|
// Centerprinting |
|
if (Goal->broadcast != NULL_STRING && TFCGameRules()->CTF_Map == FALSE) |
|
UTIL_ShowMessage( STRING(Goal->broadcast), pPlayer ); |
|
if (Goal->netname_broadcast != NULL_STRING && TFCGameRules()->CTF_Map == FALSE && AP != NULL) |
|
ClientPrint( pPlayer, HUD_PRINTNOTIFY, STRING(Goal->netname_broadcast), AP->GetPlayerName() ); |
|
// Old printing |
|
if (Goal->org_broadcast != NULL_STRING && TFCGameRules()->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, (CTFCPlayer*)pPlayer, AP, bAddBonuses); |
|
bGotOne = TRUE; |
|
} |
|
} |
|
else |
|
{ |
|
Apply_Results(Goal, (CTFCPlayer*)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); |
|
|
|
if ( g_pGameRules->IsMultiplayer() ) |
|
TFCGameRules()->TFCGoToIntermission(); |
|
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, CTFCPlayer *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, CTFCPlayer *AP, CTFGoal *ActivatingGoal) |
|
{ |
|
// Can't activate during PreMatch time, except for timers |
|
if ( (TFCGameRules()->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 TFCTODO |
|
// 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 ( TFCGameRules()->IsInPreMatch() ) |
|
{ |
|
MDEBUG( Warning("\n PREMATCH IS ON. DELAYING UNTIL AFTER PREMATCH.\n") ); |
|
|
|
SetThink( &CTFGoal::tfgoal_timer_tick ); |
|
SetNextThink( TFCGameRules()->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, (CTFCPlayer*)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 TFCTODO |
|
// 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 TFCTODO |
|
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 ( TFCGameRules()->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 (TFCGameRules()->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, ToTFCPlayer( pOther ), this); |
|
} |
|
|
|
//========================================================================= |
|
// Handles Delayed Activation of Goals |
|
void CTFGoal::DelayedResult() |
|
{ |
|
CBaseEntity *pEnemy = enemy; |
|
if (goal_state == TFGS_DELAYED) |
|
DoResults(this, ToTFCPlayer( 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(); |
|
m_flCycle = 0; |
|
} |
|
} |
|
|
|
#ifdef TFCTODO |
|
// 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 TFCTODO |
|
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] ); |
|
|
|
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 ( TFCGameRules()->IsInPreMatch() ) |
|
return; |
|
|
|
// Hack to prevent feigning spies from repicking up flags |
|
CTFCPlayer *pPlayer = dynamic_cast<CTFCPlayer*>( pOther ); |
|
if (pPlayer->is_feigning) |
|
return; |
|
|
|
// only let players have one replacement_model goal item at a time |
|
if ( replacement_model != NULL_STRING ) |
|
{ |
|
if ( pPlayer->replacement_model != NULL_STRING ) |
|
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 (TFCGameRules()->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, ToTFCPlayer( pOther ), NULL) ) |
|
{ |
|
// Give it to the player |
|
tfgoalitem_GiveToPlayer(this, ToTFCPlayer( 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 TFCTODO // 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); |
|
} |
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------- // |
|
// CTFSpawn implementation. |
|
// ---------------------------------------------------------------------------------------------------- // |
|
|
|
LINK_ENTITY_TO_CLASS(info_player_teamspawn, 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; |
|
} |
|
|
|
|
|
// find the highest team number |
|
|
|
if (number_of_teams < GetTeamNumber()) |
|
number_of_teams = GetTeamNumber(); |
|
|
|
// Save out the info_player_teamspawn |
|
Assert( stricmp( GetClassname(), "info_player_teamspawn" ) == 0 ); |
|
#ifdef TFCTODO |
|
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; |
|
} |
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------- // |
|
// 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, ToTFCPlayer( 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() ); |
|
}
|
|
|