//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_BaseObject::Simulate( void ) { if ( IsPlacing() && !MustBeBuiltOnAttachmentPoint() ) { int iValidPlacement = ( IsPlacementPosValid() && ServerValidPlacement() ) ? 1 : 0; if ( m_iLastPlacementPosValid != iValidPlacement ) { m_iLastPlacementPosValid = iValidPlacement; OnPlacementStateChanged( m_iLastPlacementPosValid > 0 ); } // We figure out our own placement pos, but we still leave it to the server to // do collision with other entities and nobuild triggers, so that sets the // placement animation SetLocalOrigin( m_vecBuildOrigin ); InvalidateBoneCache(); // Clear out our origin and rotation interpolation history // so we don't pop when we teleport in the actual position from the server CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator(); interpolator.ClearHistory(); CInterpolatedVar<QAngle> &rotInterpolator = GetRotationInterpolator(); rotInterpolator.ClearHistory(); } BaseClass::Simulate(); }
//----------------------------------------------------------------------------- // Purpose: Return true if this object should be active //----------------------------------------------------------------------------- bool CBaseObject::ShouldBeActive( void ) { // Placing and/or constructing objects shouldn't be active if ( IsPlacing() || IsBuilding() ) return false; return true; }
//----------------------------------------------------------------------------- // Purpose: Find nearby objects and buff them. //----------------------------------------------------------------------------- void CObjectBuffStation::BuffNearbyObjects( CBaseObject *pObjectToTarget, bool bPlacing ) { // ROBIN: Disabled object buffing for now return; // Check for a team. if ( !GetTFTeam() ) return; // Am I ready to power anything? if ( IsBuilding() || ( !bPlacing && IsPlacing() ) ) return; // Am I already full? if ( m_nObjectCount >= BUFF_STATION_MAX_OBJECTS ) return; // Do we have a specific target? if ( pObjectToTarget ) { if( !pObjectToTarget->CanBeHookedToBuffStation() || pObjectToTarget->GetBuffStation() ) return; if ( IsWithinBuffRange( pObjectToTarget ) ) { AttachObject( pObjectToTarget, bPlacing ); } } else { // Find nearby objects for ( int iObject = 0; iObject < GetTFTeam()->GetNumObjects(); iObject++ ) { CBaseObject *pObject = GetTFTeam()->GetObject( iObject ); assert(pObject); if ( pObject == this || !pObject->CanBeHookedToBuffStation() || pObject->GetBuffStation() ) continue; // Make sure it's within range if ( IsWithinBuffRange( pObject ) ) { AttachObject( pObject, bPlacing ); // Am I now full? if ( m_nObjectCount >= BUFF_STATION_MAX_OBJECTS ) break; } } } }
//----------------------------------------------------------------------------- // Purpose: Change our model based on the object we are attaching to //----------------------------------------------------------------------------- void CObjectSapper::SetupAttachedVersion( void ) { CBaseObject *pObject = dynamic_cast<CBaseObject *>( m_hBuiltOnEntity.Get() ); Assert( pObject ); if ( !pObject ) { DestroyObject(); return; } if ( IsPlacing() ) { SetModel( GetSapperModelName( SAPPER_MODEL_PLACEMENT ) ); } BaseClass::SetupAttachedVersion(); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CObjectBuffStation::BoostObjectThink( void ) { // Set next boost object think time. SetNextThink( gpGlobals->curtime + BUFF_STATION_BOOST_OBJECT_THINK_INTERVAL, BUFF_STATION_BOOST_OBJECT_THINK_CONTEXT ); // If we're emped, placing, or building, we're not ready to powerup if ( IsPlacing() || IsBuilding() || HasPowerup( POWERUP_EMP ) ) return; float flMaxRangeSq = obj_buff_station_obj_range.GetFloat(); flMaxRangeSq *= flMaxRangeSq; // Boost objects. for ( int iObject = m_nObjectCount; --iObject >= 0; ) { CBaseObject *pObject = m_hObjects[iObject].Get(); if ( !pObject || !InSameTeam( pObject ) ) { DetachObjectByIndex( iObject ); continue; } // Check for out of range. float flDistSq = GetAbsOrigin().DistToSqr( pObject->GetAbsOrigin() ); if ( flDistSq > flMaxRangeSq ) { DetachObjectByIndex( iObject ); continue; } // Don't powerup it until it's finished building if ( pObject->IsPlacing() || pObject->IsBuilding() ) continue; // Boost it if ( !pObject->AttemptToPowerup( POWERUP_BOOST, BUFF_STATION_BOOST_OBJECT_THINK_INTERVAL, 0, this, &m_aObjectAttachInfo[iObject].m_DamageModifier ) ) { m_aObjectAttachInfo[iObject].m_DamageModifier.RemoveModifier(); } } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseTFVehicle::VehiclePassengerThink( void ) { SetNextThink( gpGlobals->curtime + 10.0, PASSENGER_THINK_CONTEXT ); if ( IsPlacing() ) { ResetDeteriorationTime(); return; } // If there are any passengers in the vehicle, push off deterioration time if ( GetPassengerCount() ) { ResetDeteriorationTime(); return; } // See if there are any team members nearby if ( GetTeam() ) { int iNumPlayers = GetTFTeam()->GetNumPlayers(); for ( int i = 0; i < iNumPlayers; i++ ) { if ( GetTFTeam()->GetPlayer(i) ) { Vector vecOrigin = GetTFTeam()->GetPlayer(i)->GetAbsOrigin(); if ( (vecOrigin - GetAbsOrigin()).LengthSqr() < DETERIORATION_DISTANCE ) { // Found one nearby, reset our deterioration time ResetDeteriorationTime(); return; } } } } }
void CObjectTeleporter::DeterminePlaybackRate( void ) { float flPlaybackRate = GetPlaybackRate(); bool bWasBelowFullSpeed = ( flPlaybackRate < 1.0f ); if ( IsBuilding() ) { // Default half rate, author build anim as if one player is building SetPlaybackRate( GetConstructionMultiplier() * 0.5 ); } else if ( IsPlacing() ) { SetPlaybackRate( 1.0f ); } else { float flFrameTime = 0.1; // BaseObjectThink delay switch( m_iState ) { case TELEPORTER_STATE_READY: { // spin up to 1.0 from whatever we're at, at some high rate flPlaybackRate = Approach( 1.0f, flPlaybackRate, 0.5f * flFrameTime ); } break; case TELEPORTER_STATE_RECHARGING: { // Recharge - spin down to low and back up to full speed over 10 seconds // 0 -> 4, spin to low // 4 -> 6, stay at low // 6 -> 10, spin to 1.0 float flScale = g_flTeleporterRechargeTimes[GetUpgradeLevel() - 1] / g_flTeleporterRechargeTimes[0]; float flToLow = 4.0f * flScale; float flToHigh = 6.0f * flScale; float flRechargeTime = g_flTeleporterRechargeTimes[GetUpgradeLevel() - 1]; float flTimeSinceChange = gpGlobals->curtime - m_flLastStateChangeTime; float flLowSpinSpeed = 0.15f; if ( flTimeSinceChange <= flToLow ) { flPlaybackRate = RemapVal( gpGlobals->curtime, m_flLastStateChangeTime, m_flLastStateChangeTime + flToLow, 1.0f, flLowSpinSpeed ); } else if ( flTimeSinceChange > flToLow && flTimeSinceChange <= flToHigh ) { flPlaybackRate = flLowSpinSpeed; } else { flPlaybackRate = RemapVal( gpGlobals->curtime, m_flLastStateChangeTime + flToHigh, m_flLastStateChangeTime + flRechargeTime, flLowSpinSpeed, 1.0f ); } } break; default: { if ( m_flLastStateChangeTime <= 0.0f ) { flPlaybackRate = 0.0f; } else { // lost connect - spin down to 0.0 from whatever we're at, slowish rate flPlaybackRate = Approach( 0.0f, flPlaybackRate, 0.25f * flFrameTime ); } } break; } SetPlaybackRate( flPlaybackRate ); } bool bBelowFullSpeed = ( GetPlaybackRate() < 1.0f ); if ( m_iBlurBodygroup >= 0 && bBelowFullSpeed != bWasBelowFullSpeed ) { if ( bBelowFullSpeed ) { SetBodygroup( m_iBlurBodygroup, 0 ); // turn off blur bodygroup } else { SetBodygroup( m_iBlurBodygroup, 1 ); // turn on blur bodygroup } } StudioFrameAdvance(); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_BaseObject::OnDataChanged( DataUpdateType_t updateType ) { if (updateType == DATA_UPDATE_CREATED) { CreateBuildPoints(); } BaseClass::OnDataChanged( updateType ); // Did we just finish building? if ( m_bWasBuilding && !m_bBuilding ) { FinishedBuilding(); } // Did we just go active? bool bShouldBeActive = ShouldBeActive(); if ( !m_bWasActive && bShouldBeActive ) { OnGoActive(); } else if ( m_bWasActive && !bShouldBeActive ) { OnGoInactive(); } if ( m_bDisabled != m_bOldDisabled ) { if ( m_bDisabled ) { OnStartDisabled(); } else { OnEndDisabled(); } } if ( ( !IsBuilding() && m_iHealth != m_iOldHealth ) ) { // recalc our damage particle state BuildingDamageLevel_t damageLevel = CalculateDamageLevel(); if ( damageLevel != m_damageLevel ) { UpdateDamageEffects( damageLevel ); m_damageLevel = damageLevel; } } if ( m_bWasBuilding && !m_bBuilding ) { // Force update damage effect when finishing construction. BuildingDamageLevel_t damageLevel = CalculateDamageLevel(); UpdateDamageEffects( damageLevel ); m_damageLevel = damageLevel; } // Kill all particles when getting picked up. if ( !m_bWasCarried && m_bCarried ) { ParticleProp()->StopParticlesInvolving( this ); } if ( m_iHealth > m_iOldHealth && m_iHealth == m_iMaxHealth ) { // If we were just fully healed, remove all decals RemoveAllDecals(); } if ( GetOwner() == C_TFPlayer::GetLocalTFPlayer() ) { IGameEvent *event = gameeventmanager->CreateEvent( "building_info_changed" ); if ( event ) { event->SetInt( "building_type", GetType() ); event->SetInt( "object_mode", GetObjectMode() ); gameeventmanager->FireEventClientSide( event ); } } if ( IsPlacing() && GetSequence() != m_nObjectOldSequence ) { // Ignore server sequences while placing OnPlacementStateChanged( m_iLastPlacementPosValid > 0 ); } }
void C_ObjectSentrygun::ClientThink( void ) { // Turtling sentryguns don't think if ( IsTurtled() ) return; if ( IsPlacing() || IsBuilding() ) return; if ( m_hEnemy != NULL ) { // Figure out where we're firing at Vector vecMid = EyePosition(); Vector vecFireTarget = m_hEnemy->WorldSpaceCenter(); // + vecMid; // BodyTarget( vecMid ); Vector vecDirToEnemy = vecFireTarget - vecMid; QAngle angToTarget; VectorAngles(vecDirToEnemy, angToTarget); angToTarget.y = UTIL_AngleMod( angToTarget.y ); if (angToTarget.x < -180) angToTarget.x += 360; if (angToTarget.x > 180) angToTarget.x -= 360; // now all numbers should be in [1...360] // pin to turret limitations to [-50...50] if (angToTarget.x > 50) angToTarget.x = 50; else if (angToTarget.x < -50) angToTarget.x = -50; m_vecGoalAngles.y = angToTarget.y; m_vecGoalAngles.x = angToTarget.x; MoveTurret(); return; } // Rotate if ( !MoveTurret() ) { // Play a sound occasionally if ( random->RandomFloat(0, 1) < 0.02 ) { EmitSound( "ObjectSentrygun.Idle" ); } // Switch rotation direction if (m_bTurningRight) { m_bTurningRight = false; m_vecGoalAngles.y = m_iLeftBound; } else { m_bTurningRight = true; m_vecGoalAngles.y = m_iRightBound; } // Randomly look up and down a bit if ( random->RandomFloat(0, 1) < 0.3 ) { m_vecGoalAngles.x = (int)random->RandomFloat(-10,10); } } }
//----------------------------------------------------------------------------- // Purpose: Change our model based on the object we are attaching to //----------------------------------------------------------------------------- void CObjectSapper::SetupAttachedVersion( void ) { CBaseObject *pObject = dynamic_cast<CBaseObject *>( m_hBuiltOnEntity.Get() ); Assert( pObject ); if ( !pObject ) { DestroyObject(); return; } if ( IsPlacing() ) { switch( pObject->GetType() ) { case OBJ_SENTRYGUN: { // what level? CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun *>( pObject ); Assert( pSentry ); if ( !pSentry ) { DestroyObject(); return; } int iLevel = pSentry->GetUpgradeLevel(); switch( iLevel ) { case 1: SetModel( SAPPER_MODEL_SENTRY_1_PLACEMENT ); break; case 2: SetModel( SAPPER_MODEL_SENTRY_2_PLACEMENT ); break; case 3: SetModel( SAPPER_MODEL_SENTRY_3_PLACEMENT ); break; default: Assert(0); break; } } break; case OBJ_TELEPORTER: SetModel( SAPPER_MODEL_TELEPORTER_PLACEMENT ); break; case OBJ_DISPENSER: SetModel( SAPPER_MODEL_DISPENSER_PLACEMENT ); break; default: Assert( !"what kind of object are we trying to place a sapper on?" ); break; } } BaseClass::SetupAttachedVersion(); }