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.
416 lines
13 KiB
416 lines
13 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//============================================================================= |
|
#include "cbase.h" |
|
|
|
#include "c_tf_player.h" |
|
#include "collisionutils.h" |
|
#include "econ_item_inventory.h" |
|
#include "iclientmode.h" |
|
#include "tf_gcmessages.h" |
|
#include "tf_gamerules.h" |
|
#include "econ_notifications.h" |
|
#include "rtime.h" |
|
#include "achievementmgr.h" |
|
#include "baseachievement.h" |
|
#include "achievements_tf.h" |
|
#include "gc_clientsystem.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
EHalloweenMap GetHalloweenMap() |
|
{ |
|
if ( FStrEq( engine->GetLevelName(), "maps/cp_manor_event.bsp" ) ) |
|
return kHalloweenMap_MannManor; |
|
|
|
if ( FStrEq( engine->GetLevelName(), "maps/koth_viaduct_event.bsp" ) ) |
|
return kHalloweenMap_Viaduct; |
|
|
|
if ( FStrEq( engine->GetLevelName(), "maps/koth_lakeside_event.bsp" ) ) |
|
return kHalloweenMap_Lakeside; |
|
|
|
if ( FStrEq( engine->GetLevelName(), "maps/plr_hightower_event.bsp" ) ) |
|
return kHalloweenMap_Hightower; |
|
|
|
return kHalloweenMapCount; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Created when the GC decides to give out an item. |
|
// A player must intersect the item to claim it. |
|
//----------------------------------------------------------------------------- |
|
#define HALLOWEEN_ITEM_TIME_TO_READY 10.0f |
|
class C_HalloweenItemPickup : public CBaseAnimating |
|
{ |
|
DECLARE_CLASS( C_HalloweenItemPickup, CBaseAnimating ); |
|
public: |
|
C_HalloweenItemPickup() |
|
: m_bReadyForPickup( false ) |
|
, m_bClaimed( false ) |
|
, m_flTimeToReady( 0.0f ) |
|
{ |
|
AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); |
|
} |
|
|
|
virtual ~C_HalloweenItemPickup() |
|
{ |
|
} |
|
|
|
bool Initialize() |
|
{ |
|
const char *pszModelName = "models/props_halloween/halloween_gift.mdl"; |
|
SetModelName( AllocPooledString( pszModelName ) ); |
|
|
|
if ( InitializeAsClientEntity( STRING(GetModelName()), RENDER_GROUP_OPAQUE_ENTITY ) == false ) |
|
return false; |
|
|
|
const model_t *mod = GetModel(); |
|
if ( mod ) |
|
{ |
|
Vector mins, maxs; |
|
modelinfo->GetModelBounds( mod, mins, maxs ); |
|
SetCollisionBounds( mins, maxs ); |
|
} |
|
|
|
Spawn(); |
|
|
|
// initialize as translucent |
|
float alpha = 0.0f; |
|
SetRenderMode( kRenderTransTexture ); |
|
SetRenderColorA( alpha * 256 ); |
|
m_flTimeToReady = gpGlobals->realtime + HALLOWEEN_ITEM_TIME_TO_READY; |
|
|
|
UpdatePartitionListEntry(); |
|
|
|
SetBlocksLOS( false ); // this should be a small object |
|
|
|
// Set up shadows; do it here so that objects can change shadowcasting state |
|
CreateShadow(); |
|
|
|
UpdateVisibility(); |
|
|
|
SetNextClientThink( CLIENT_THINK_ALWAYS ); |
|
|
|
return true; |
|
} |
|
|
|
virtual void Spawn() |
|
{ |
|
Precache(); |
|
BaseClass::Spawn(); |
|
SetSolid( SOLID_NONE ); |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
SetMoveType( MOVETYPE_NONE ); |
|
} |
|
|
|
virtual void ClientThink() |
|
{ |
|
if ( m_bReadyForPickup ) |
|
{ |
|
ClientThink_Active(); |
|
return; |
|
} |
|
|
|
float flTimeDelta = m_flTimeToReady - gpGlobals->realtime; |
|
if ( flTimeDelta < 0 ) |
|
{ |
|
m_bReadyForPickup = true; |
|
ParticleProp()->Create( "halloween_pickup_active", PATTACH_ABSORIGIN_FOLLOW ); |
|
SetRenderMode( kRenderNormal ); |
|
} |
|
else |
|
{ |
|
float alpha = 0.75f * ( ( HALLOWEEN_ITEM_TIME_TO_READY - flTimeDelta ) / HALLOWEEN_ITEM_TIME_TO_READY ); |
|
SetRenderColorA( alpha * 256 ); |
|
} |
|
} |
|
|
|
void ClientThink_Active( void ) |
|
{ |
|
Vector vWorldMins = WorldAlignMins(); |
|
Vector vWorldMaxs = WorldAlignMaxs(); |
|
Vector vBoxMin1 = GetAbsOrigin() + vWorldMins; |
|
Vector vBoxMax1 = GetAbsOrigin() + vWorldMaxs; |
|
|
|
float flBestDistance2 = 0.0f; |
|
CSteamID bestSteamID; |
|
bool bBestHasNoclip = false; |
|
|
|
#define CLIENT_HALLOWEEN_LOGIC_ENABLE_LOCAL_PLAYER_ONLY 1 |
|
|
|
#if !CLIENT_HALLOWEEN_LOGIC_ENABLE_LOCAL_PLAYER_ONLY |
|
for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ ) |
|
{ |
|
C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerIndex ) ); |
|
#else |
|
do |
|
{ |
|
C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
#endif |
|
CSteamID steamID; |
|
if ( pPlayer == NULL || pPlayer->IsBot() == true || pPlayer->GetSteamID( &steamID ) == false || |
|
( pPlayer->GetTeamNumber() != TF_TEAM_RED && pPlayer->GetTeamNumber() != TF_TEAM_BLUE ) || |
|
pPlayer->IsAlive() == false || |
|
pPlayer->GetObserverMode() != OBS_MODE_NONE ) |
|
{ |
|
continue; |
|
} |
|
|
|
Vector vPlayerMins = pPlayer->GetAbsOrigin() + pPlayer->WorldAlignMins(); |
|
Vector vPlayerMaxs = pPlayer->GetAbsOrigin() + pPlayer->WorldAlignMaxs(); |
|
bool bIntersecting = IsBoxIntersectingBox( vBoxMin1, vBoxMax1, vPlayerMins, vPlayerMaxs ); |
|
float flDistance2 = ( pPlayer->GetAbsOrigin(), GetAbsOrigin() ).LengthSqr(); |
|
if ( bIntersecting && ( bestSteamID.GetAccountID() == 0 || flDistance2 < flBestDistance2 ) ) |
|
{ |
|
bestSteamID = steamID; |
|
bBestHasNoclip = pPlayer->GetMoveType() == MOVETYPE_NOCLIP; |
|
flBestDistance2 = flDistance2; |
|
} |
|
} |
|
#if CLIENT_HALLOWEEN_LOGIC_ENABLE_LOCAL_PLAYER_ONLY |
|
while ( false ); |
|
#endif |
|
|
|
if ( bestSteamID.GetAccountID() != 0 ) |
|
{ |
|
GCSDK::CProtoBufMsg<CMsgGC_Halloween_GrantItem> msg( k_EMsgGC_Halloween_GrantItem ); |
|
msg.Body().set_recipient_account_id( bestSteamID.GetAccountID() ); |
|
msg.Body().set_level_id( GetHalloweenMap() ); |
|
msg.Body().set_flagged( bBestHasNoclip ); |
|
GCClientSystem()->BSendMessage( msg ); |
|
OnClaimed( true ); |
|
return; |
|
} |
|
|
|
SetNextClientThink( gpGlobals->curtime + 0.33f ); |
|
} |
|
|
|
void OnClaimed( bool bPlayAudio ) |
|
{ |
|
if ( m_bClaimed ) |
|
return; |
|
|
|
m_bClaimed = true; |
|
// stop thinking and remove sparkle... |
|
ParticleProp()->StopParticlesNamed( "halloween_pickup_active", true ); |
|
SetNextClientThink( CLIENT_THINK_NEVER ); |
|
// throw up bday confetti |
|
DispatchParticleEffect( "halloween_gift_pickup", GetAbsOrigin(), vec3_angle ); |
|
|
|
if ( bPlayAudio ) |
|
{ |
|
C_BaseEntity::EmitSound( "Game.HappyBirthday" ); |
|
} |
|
SetRenderMode( kRenderNone ); |
|
UpdateVisibility(); |
|
} |
|
|
|
bool m_bReadyForPickup; |
|
bool m_bClaimed; |
|
float m_flTimeToReady; |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( tf_halloween_item_pickup, C_HalloweenItemPickup ); |
|
PRECACHE_REGISTER( tf_halloween_item_pickup ); |
|
|
|
static EHANDLE gHalloweenPickup; |
|
|
|
#ifdef _DEBUG |
|
CON_COMMAND( cl_halloween_test_cheating, "Test cheating the halloween pickup" ) |
|
{ |
|
GCSDK::CProtoBufMsg< CMsgGC_Halloween_GrantItem > msg( k_EMsgGC_Halloween_GrantItem ); |
|
msg.Body().set_recipient_account_id( steamapicontext->SteamUser()->GetSteamID().GetAccountID() ); |
|
GCClientSystem()->BSendMessage( msg ); |
|
} |
|
|
|
CON_COMMAND( cl_halloween_test_spawn_pickup, "Test spawning the pickup item" ) |
|
{ |
|
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
if ( pLocalPlayer == NULL ) |
|
return; |
|
|
|
// Now create the pickup item |
|
C_HalloweenItemPickup *pEntity = new C_HalloweenItemPickup(); |
|
if ( !pEntity ) |
|
return; |
|
|
|
Vector vecTargetPoint; |
|
trace_t tr; |
|
Vector forward; |
|
pLocalPlayer->EyeVectors( &forward ); |
|
UTIL_TraceLine( pLocalPlayer->EyePosition(), |
|
pLocalPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_NPCSOLID, |
|
pLocalPlayer, COLLISION_GROUP_NONE, &tr ); |
|
|
|
if ( tr.fraction != 1.0 ) |
|
{ |
|
vecTargetPoint = tr.endpos; |
|
} |
|
|
|
pEntity->SetAbsOrigin( vecTargetPoint ); |
|
if ( !pEntity->Initialize() ) |
|
{ |
|
pEntity->Release(); |
|
} |
|
else |
|
{ |
|
if ( gHalloweenPickup.Get() ) |
|
gHalloweenPickup->Release(); |
|
gHalloweenPickup = pEntity; |
|
} |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// GC has decided to drop a Halloween item |
|
//----------------------------------------------------------------------------- |
|
//class CGCHalloween_ReservedItem : public GCSDK::CGCClientJob |
|
//{ |
|
//public: |
|
// CGCHalloween_ReservedItem( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} |
|
// |
|
// virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) |
|
// { |
|
// GCSDK::CProtoBufMsg<CMsgGC_Halloween_ReservedItem> msg( pNetPacket ); |
|
// |
|
// // Figure out which level we're on so we know how to handle notifications, etc. |
|
// EHalloweenMap eMap = GetHalloweenMap(); |
|
// if ( eMap == kHalloweenMapCount ) |
|
// return true; |
|
// |
|
// // Sanity-check the message contents from the GC. |
|
// if ( msg.Body().x_size() != msg.Body().y_size() || msg.Body().y_size() != msg.Body().z_size() ) |
|
// return true; |
|
// |
|
// if ( msg.Body().x_size() <= eMap ) |
|
// return true; |
|
// |
|
// // Don't spawn gifts during startup. |
|
// if ( TFGameRules() == NULL |
|
// || TFGameRules()->State_Get() != GR_STATE_RND_RUNNING |
|
// || TFGameRules()->InSetup() |
|
// || TFGameRules()->IsInWaitingForPlayers() |
|
// || TFGameRules()->ArePlayersInHell() ) // Dont spawn gifts if players are in 2013 Hell |
|
// return true; |
|
// |
|
// C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
// if ( pLocalPlayer == NULL ) |
|
// return true; |
|
// |
|
// // If we don't already know about this gift pickup, create one. |
|
// if ( gHalloweenPickup.Get() == NULL ) |
|
// { |
|
// // Now create the pickup item |
|
// C_HalloweenItemPickup *pEntity = new C_HalloweenItemPickup(); |
|
// if ( !pEntity ) |
|
// return true; |
|
// |
|
// Vector position( msg.Body().x( eMap ), msg.Body().y( eMap ), msg.Body().z( eMap ) ); |
|
// pEntity->SetAbsOrigin( position ); |
|
// if ( !pEntity->Initialize() ) |
|
// { |
|
// pEntity->Release(); |
|
// } |
|
// else |
|
// { |
|
// gHalloweenPickup = pEntity; |
|
// } |
|
// } |
|
// |
|
// // Regardless of whether we created a new gift or whether this was a new notification about an old gift, |
|
// // display a UI notification for the user. |
|
// CEconNotification *pNotification = new CEconNotification(); |
|
// pNotification->SetText( "#TF_HalloweenItem_Reserved" ); |
|
// pNotification->SetLifetime( 15.0f ); |
|
// pNotification->SetSoundFilename( "ui/halloween_loot_spawn.wav" ); |
|
// NotificationQueue_Add( pNotification ); |
|
// |
|
// return true; |
|
// } |
|
//}; |
|
//GC_REG_JOB( GCSDK::CGCClient, CGCHalloween_ReservedItem, "CGCHalloween_ReservedItem", k_EMsgGC_Halloween_ReservedItem, GCSDK::k_EServerTypeGCClient ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// GC gave out the Halloween item to a player |
|
//----------------------------------------------------------------------------- |
|
//class CGCHalloween_GrantedItemResponse : public GCSDK::CGCClientJob |
|
//{ |
|
//public: |
|
// CGCHalloween_GrantedItemResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} |
|
// |
|
// virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) |
|
// { |
|
// GCSDK::CProtoBufMsg<CMsgGC_Halloween_GrantItemResponse> msg( pNetPacket ); |
|
// |
|
// // which Steam universe are we in? |
|
// EUniverse eUniverse = steamapicontext && steamapicontext->SteamUtils() |
|
// ? steamapicontext->SteamUtils()->GetConnectedUniverse() |
|
// : k_EUniverseInvalid; |
|
// |
|
// CSteamID steamIDRecipient( msg.Body().recipient_account_id(), eUniverse, k_EAccountTypeIndividual ); |
|
// bool bIsValidRecipient = steamIDRecipient.IsValid(); |
|
// |
|
// if ( gHalloweenPickup.Get() != NULL ) |
|
// { |
|
// assert_cast<C_HalloweenItemPickup *>( gHalloweenPickup.Get() )->OnClaimed( bIsValidRecipient ); |
|
// gHalloweenPickup->Release(); |
|
// gHalloweenPickup = NULL; |
|
// } |
|
// |
|
// // don't do any work if we're not on a Halloween map |
|
// EHalloweenMap eMap = GetHalloweenMap(); |
|
// if ( eMap == kHalloweenMapCount ) |
|
// return true; |
|
// |
|
// // add alert |
|
// const char* pPlayerName = InventoryManager()->PersonaName_Get( steamIDRecipient.GetAccountID() ); |
|
// wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH] = L""; |
|
// if ( pPlayerName != NULL && FStrEq( pPlayerName, "" ) == false ) |
|
// { |
|
// g_pVGuiLocalize->ConvertANSIToUnicode( pPlayerName, wszPlayerName, sizeof(wszPlayerName) ); |
|
// } |
|
// CEconNotification *pNotification = new CEconNotification(); |
|
// pNotification->SetLifetime( 15.0f ); |
|
// |
|
// if ( bIsValidRecipient ) |
|
// { |
|
// pNotification->SetText( "#TF_HalloweenItem_Granted" ); |
|
// pNotification->AddStringToken( "recipient", wszPlayerName ); |
|
// pNotification->SetSteamID( steamIDRecipient ); |
|
// pNotification->SetSoundFilename( "ui/halloween_loot_found.wav" ); |
|
// } |
|
// else |
|
// { |
|
// pNotification->SetText( "#TF_HalloweenItem_GrantPickupFail" ); |
|
// // pNotification->SetSoundFilename( "coach/coach_student_died.wav" ); |
|
// } |
|
// |
|
// NotificationQueue_Add( pNotification ); |
|
// |
|
// // is this the local player? award the achievement... |
|
// if ( steamapicontext && steamapicontext->SteamUser() ) |
|
// { |
|
// CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID(); |
|
// if ( steamIDRecipient == localSteamID ) |
|
// { |
|
// g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_HALLOWEEN_COLLECT_GOODY_BAG ); |
|
// } |
|
// } |
|
// |
|
// return true; |
|
// } |
|
//}; |
|
//GC_REG_JOB( GCSDK::CGCClient, CGCHalloween_GrantedItemResponse, "CGCHalloween_GrantedItemResponse", k_EMsgGC_Halloween_GrantItemResponse, GCSDK::k_EServerTypeGCClient ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void CL_Halloween_LevelShutdown() |
|
{ |
|
gHalloweenPickup = NULL; |
|
}
|
|
|