//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEP2GameStats::Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info ) { BaseClass::Event_PlayerKilled( pPlayer, info ); if ( info.GetDamageType() & DMG_FALL ) { ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_FALLINGDEATHS ]; } Ep2LevelStats_t::PlayerDeathsLump_t death; // set the location where the target died const Vector &org = pPlayer->GetAbsOrigin(); death.nPosition[ 0 ] = static_cast<short>( org.x ); death.nPosition[ 1 ] = static_cast<short>( org.y ); death.nPosition[ 2 ] = static_cast<short>( org.z ); StatsLog( "CEP2GameStats::Event_PlayerKilled at location [%d %d %d]\n", (int)death.nPosition[ 0 ], (int)death.nPosition[ 1 ], (int)death.nPosition[ 2 ] ); // set the class of the attacker CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pKiller = info.GetAttacker(); if ( pInflictor ) { StatsLog( "Inflictor: %s\n", pInflictor->GetClassname() ); } if ( pKiller ) { char const *pchKiller = pKiller->GetClassname(); Ep2LevelStats_t::EntityDeathsLump_t *lump = FindDeathsLump( pchKiller ); if ( lump ) { ++lump->m_nKilledPlayer; StatsLog( "Player has been killed %d times by %s's\n", lump->m_nKilledPlayer, pchKiller ); } else { StatsLog( "Player killed by %s (not tracked)\n", pchKiller ); } } // add it to the list of deaths Ep2LevelStats_t *map = FindOrAddMapStats( STRING( gpGlobals->mapname ) ); int slot = map->m_aPlayerDeaths.AddToTail( death ); Ep2LevelStats_t::SaveGameInfoRecord2_t *rec = map->m_SaveGameInfo.m_pCurrentRecord; if ( rec ) { if ( rec->m_nFirstDeathIndex == -1 ) { rec->m_nFirstDeathIndex = slot; } ++rec->m_nNumDeaths; StatsLog( "Player has died %d times since last save/load\n", rec->m_nNumDeaths ); } }
void CTFOClassnameFinder::FindTarget( inputdata_t &inputdata ) { bool bFound = false; CBaseEntity *pMyFoundEnt = NULL; // Try classname pMyFoundEnt = gEntList.FindEntityByClassname( NULL, szTarget.ToCStr() ); while ( pMyFoundEnt ) { if ( !strcmp( pMyFoundEnt->GetClassname(), szTarget.ToCStr() ) ) { pFoundTarget.FireOutput( this, this ); bFound = true; break; } pMyFoundEnt = gEntList.FindEntityByClassname( pMyFoundEnt, szTarget.ToCStr() ); } if ( bFound ) return; // Try the actual name pMyFoundEnt = gEntList.FindEntityByName( NULL, szTarget.ToCStr() ); while ( pMyFoundEnt ) { if ( !strcmp( pMyFoundEnt->GetEntityName().ToCStr(), szTarget.ToCStr() ) ) { pFoundTarget.FireOutput( this, this ); break; } pMyFoundEnt = gEntList.FindEntityByName( pMyFoundEnt, szTarget.ToCStr() ); } }
virtual const char *GetSceneFile( EntitySearchResult sr ) { CBaseEntity *ent = reinterpret_cast< CBaseEntity* >( sr ); if ( !sr ) return ""; if ( Q_stricmp( ent->GetClassname(), "logic_choreographed_scene" ) ) return ""; return GetSceneFilename( ent ); }
//----------------------------------------------------------------------------- // Returns the name for debugging purposes //----------------------------------------------------------------------------- char const* CMoveHelperServer::GetName( EntityHandle_t handle ) const { // This ain't pertickulerly fast, but it's for debugging anyways edict_t* pEdict = GetEdict(handle); CBaseEntity *ent = CBaseEntity::Instance( pEdict ); // Is it the world? if (ENTINDEX(pEdict) == 0) return STRING(gpGlobals->mapname); // Is it a model? if ( ent && ent->GetModelName() != NULL_STRING ) return STRING( ent->GetModelName() ); if ( ent->GetClassname() != NULL ) { return ent->GetClassname(); } return "?"; }
void cc_asw_inventory() { CASW_Player *pPlayer = ToASW_Player(UTIL_GetCommandClient()); if ( pPlayer->GetMarine() ) { for (int i=0;i<pPlayer->GetMarine()->WeaponCount();i++) { CBaseEntity *pWeapon = pPlayer->GetMarine()->GetWeapon(i); if ( pWeapon ) { Msg(" Inventory[%d] = %s (%d)\n", i, pWeapon->GetClassname(), pWeapon->entindex() ); } } } }
void CFuncTankControls::Think() { CBaseEntity* pTarget = nullptr; do { pTarget = UTIL_FindEntityByTargetname( pTarget, GetTarget() ); } while( !FNullEnt( pTarget ) && strncmp( pTarget->GetClassname(), "func_tank", 9 ) ); if( FNullEnt( pTarget ) ) { ALERT( at_console, "No tank %s\n", GetTarget() ); return; } m_pTank = static_cast<CFuncTank*>( pTarget ); }
//----------------------------------------------------------------------------- // Purpose: Push a physics object in our wash. Return false if it's now out of our wash //----------------------------------------------------------------------------- bool CBaseHelicopter::DoWashPush( washentity_t *pWash, Vector vecWashOrigin ) { if ( !pWash || !pWash->hEntity.Get() ) return false; // Make sure the entity is still within our wash's radius CBaseEntity *pEntity = pWash->hEntity; Vector vecSpot = pEntity->BodyTarget( vecWashOrigin ); Vector vecToSpot = ( vecSpot - vecWashOrigin ); vecToSpot.z = 0; float flDist = VectorNormalize( vecToSpot ); if ( flDist > BASECHOPPER_WASH_RADIUS ) return false; IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); if ( pPhysObject == NULL ) return false; // Push it away from the center of the wash float flMass = pPhysObject->GetMass(); float flPushTime = (gpGlobals->curtime - pWash->flWashStartTime); float flMinPush = BASECHOPPER_WASH_PUSH_MIN * flMass; float flMaxPush = BASECHOPPER_WASH_PUSH_MAX * flMass; float flWashAmount = min( flMaxPush, RemapVal( flPushTime, 0, BASECHOPPER_WASH_RAMP_TIME, flMinPush, flMaxPush ) ); Vector vecForce = flWashAmount * vecToSpot * phys_pushscale.GetFloat(); pEntity->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, vecWashOrigin, flWashAmount, DMG_BLAST ) ); // Debug if ( g_debug_basehelicopter.GetInt() == BASECHOPPER_DEBUG_WASH ) { NDebugOverlay::Cross3D( pEntity->GetAbsOrigin(), -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.1f ); NDebugOverlay::Line( pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin() + vecForce, 255, 255, 0, true, 0.1f ); IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); Msg("Pushed %s (mass %f) with force %f (min %.2f max %.2f) at time %.2f\n", pEntity->GetClassname(), pPhysObject->GetMass(), flWashAmount, flMinPush, flMaxPush, gpGlobals->curtime ); } // If we've pushed this thing for some time, remove it to give us a chance to find lighter things nearby if ( flPushTime > 2.0 ) return false; return true; }
void ReportEntityList() { const char *pLastClass = ""; int count = 0; int edicts = 0; for ( int i = 0; i < m_sortedList.Count(); i++ ) { CBaseEntity *pEntity = m_sortedList[i]; if ( !pEntity ) continue; if ( pEntity->edict() ) edicts++; const char *pClassname = pEntity->GetClassname(); if ( !FStrEq( pClassname, pLastClass ) ) { if ( count ) { Msg("Class: %s (%d)\n", pLastClass, count ); } pLastClass = pClassname; count = 1; } else count++; } if ( pLastClass[0] != 0 && count ) { Msg("Class: %s (%d)\n", pLastClass, count ); } if ( m_sortedList.Count() ) { Msg("Total %d entities (%d empty, %d edicts)\n", m_sortedList.Count(), m_emptyCount, edicts ); } }
entityIndex = trace.m_pEnt ? (short)trace.m_pEnt->entindex() : 0; if ( entityIndex ) { CBaseEntity *ent = trace.m_pEnt; if ( ent ) { modelIndex = ent->GetModelIndex(); VectorITransform( GetAbsOrigin(), ent->EntityToWorldTransform(), position ); canDraw = ( modelIndex != 0 ); if ( !canDraw ) { Warning( "Suppressed StaticDecal which would have hit entity %i (class:%s, name:%s) with modelindex = 0\n", ent->entindex(), ent->GetClassname(), STRING( ent->GetEntityName() ) ); } } } if ( canDraw ) { engine->StaticDecal( position, m_nTexture, entityIndex, modelIndex, m_bLowPriority ); } SUB_Remove(); } bool CDecal::KeyValue( const char *szKeyName, const char *szValue )
//----------------------------------------------------------------------------- // Purpose: Push a physics object in our wash. Return false if it's now out of our wash //----------------------------------------------------------------------------- bool CBaseHelicopter::DoWashPush( washentity_t *pWash, const Vector &vecWashOrigin ) { if ( !pWash || !pWash->hEntity.Get() ) return false; // Make sure the entity is still within our wash's radius CBaseEntity *pEntity = pWash->hEntity; // This can happen because we can dynamically turn this flag on and off if ( pEntity->IsEFlagSet( EFL_NO_ROTORWASH_PUSH )) return false; Vector vecSpot = pEntity->BodyTarget( vecWashOrigin ); Vector vecToSpot = ( vecSpot - vecWashOrigin ); vecToSpot.z = 0; float flDist = VectorNormalize( vecToSpot ); if ( flDist > BASECHOPPER_WASH_RADIUS ) return false; IRotorWashShooter *pShooter = GetRotorWashShooter( pEntity ); IPhysicsObject *pPhysObject; float flPushTime = (gpGlobals->curtime - pWash->flWashStartTime); flPushTime = clamp( flPushTime, 0, BASECHOPPER_WASH_RAMP_TIME ); float flWashAmount = RemapVal( flPushTime, 0, BASECHOPPER_WASH_RAMP_TIME, BASECHOPPER_WASH_PUSH_MIN, BASECHOPPER_WASH_PUSH_MAX ); if ( pShooter ) { Vector vecForce = (0.015f / 0.1f) * flWashAmount * vecToSpot * phys_pushscale.GetFloat(); pEntity = pShooter->DoWashPush( pWash->flWashStartTime, vecForce ); if ( !pEntity ) return true; washentity_t Wash; Wash.hEntity = pEntity; Wash.flWashStartTime = pWash->flWashStartTime; int i = m_hEntitiesPushedByWash.AddToTail( Wash ); pWash = &m_hEntitiesPushedByWash[i]; pPhysObject = pEntity->VPhysicsGetObject(); if ( !pPhysObject ) return true; } else { // Airboat gets special treatment if ( FClassnameIs( pEntity, "prop_vehicle_airboat" ) ) { DoWashPushOnAirboat( pEntity, vecToSpot, flWashAmount ); return true; } pPhysObject = pEntity->VPhysicsGetObject(); if ( !pPhysObject ) return false; } // Push it away from the center of the wash float flMass = pPhysObject->GetMass(); // This used to be mass independent, which is a bad idea because it blows 200kg engine blocks // as much as it blows cardboard and soda cans. Make this force mass-independent, but clamp at // 30kg. flMass = MIN( flMass, 30.0f ); Vector vecForce = (0.015f / 0.1f) * flWashAmount * flMass * vecToSpot * phys_pushscale.GetFloat(); pEntity->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, vecWashOrigin, flWashAmount, DMG_BLAST ) ); // Debug if ( g_debug_basehelicopter.GetInt() == BASECHOPPER_DEBUG_WASH ) { NDebugOverlay::Cross3D( pEntity->GetAbsOrigin(), -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.1f ); NDebugOverlay::Line( pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin() + vecForce, 255, 255, 0, true, 0.1f ); IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); Msg("Pushed %s (index %d) (mass %f) with force %f (min %.2f max %.2f) at time %.2f\n", pEntity->GetClassname(), pEntity->entindex(), pPhysObject->GetMass(), flWashAmount, BASECHOPPER_WASH_PUSH_MIN * flMass, BASECHOPPER_WASH_PUSH_MAX * flMass, gpGlobals->curtime ); } // If we've pushed this thing for some time, remove it to give us a chance to find lighter things nearby if ( flPushTime > 2.0 ) return false; return true; }
CBaseEntity * CASW_Rocket::FindPotentialTarget( void ) const { float bestdist = 0; CBaseEntity *bestent = NULL; Vector v_forward, v_right, v_up; AngleVectors( GetAbsAngles(), &v_forward, &v_right, &v_up ); // find the aimtarget nearest us int count = AimTarget_ListCount(); if ( count ) { CBaseEntity **pList = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * count ); AimTarget_ListCopy( pList, count ); CTraceFilterSkipTwoEntities filter(this, GetOwnerEntity(), COLLISION_GROUP_NONE); for ( int i = 0; i < count; i++ ) { CBaseEntity *pEntity = pList[i]; if (!pEntity || !pEntity->IsAlive() || !pEntity->edict() || !pEntity->IsNPC() ) { //Msg("not alive or not an edict, skipping\n"); continue; } if (!pEntity || !pEntity->IsAlive() || !pEntity->edict() || !pEntity->IsNPC() ) { //Msg("not alive or not an edict, skipping\n"); continue; } // don't autoaim onto marines if (pEntity->Classify() == CLASS_ASW_MARINE || pEntity->Classify() == CLASS_ASW_COLONIST) continue; if ( pEntity->Classify() == CLASS_ASW_PARASITE ) { CASW_Parasite *pParasite = static_cast< CASW_Parasite* >( pEntity ); if ( pParasite->m_bInfesting ) { continue; } } Vector center = pEntity->BodyTarget( GetAbsOrigin() ); Vector center_flat = center; center_flat.z = GetAbsOrigin().z; Vector dir = (center - GetAbsOrigin()); VectorNormalize( dir ); Vector dir_flat = (center_flat - GetAbsOrigin()); VectorNormalize( dir_flat ); // make sure it's in front of the rocket float dot = DotProduct (dir, v_forward ); //if (dot < 0) //{ //continue; //} float dist = (pEntity->GetAbsOrigin() - GetAbsOrigin()).LengthSqr(); if (dist > ASW_ROCKET_MAX_HOMING_RANGE) continue; // check another marine isn't between us and the target to reduce FF trace_t tr; UTIL_TraceLine(GetAbsOrigin(), pEntity->WorldSpaceCenter(), MASK_SHOT, &filter, &tr); if (tr.fraction < 1.0f && tr.m_pEnt != pEntity && tr.m_pEnt && tr.m_pEnt->Classify() == CLASS_ASW_MARINE) continue; // does this critter already have enough rockets to kill it? { CASW_DamageAllocationMgr::IndexType_t assignmentIndex = m_RocketAssigner.Find( pEntity ); if ( m_RocketAssigner.IsValid(assignmentIndex) ) { if ( m_RocketAssigner[assignmentIndex].m_flAccumulatedDamage > pEntity->GetHealth() ) { continue; } } } // check another marine isn't between us and the target to reduce FF UTIL_TraceLine(GetAbsOrigin(), pEntity->WorldSpaceCenter(), MASK_SHOT, &filter, &tr); if (tr.fraction < 1.0f && tr.m_pEnt != pEntity && tr.m_pEnt && tr.m_pEnt->Classify() == CLASS_ASW_MARINE) continue; // increase distance if dot isn't towards us dist += (1.0f - dot) * 150; // bias of x units when object is 90 degrees to the side if (bestdist == 0 || dist < bestdist) { bestdist = dist; bestent = pEntity; } } if ( bestent && asw_rocket_debug.GetBool() ) { Vector center = bestent->BodyTarget( GetAbsOrigin() ); Vector center_flat = center; center_flat.z = GetAbsOrigin().z; Vector dir = (center - GetAbsOrigin()); VectorNormalize( dir ); Msg( "Rocket[%d] starting homing in on %s(%d) dir = %f %f %f\n", entindex(), bestent->GetClassname(), bestent->entindex(), VectorExpand( dir ) ); } } return bestent; }
void CHL2MPRules::CleanUpMap() { // Recreate all the map entities from the map data (preserving their indices), // then remove everything else except the players. // Get rid of all entities except players. CBaseEntity *pCur = gEntList.FirstEnt(); while ( pCur ) { CBaseHL2MPCombatWeapon *pWeapon = dynamic_cast< CBaseHL2MPCombatWeapon* >( pCur ); // Weapons with owners don't want to be removed.. if ( pWeapon ) { if ( !pWeapon->GetPlayerOwner() ) { UTIL_Remove( pCur ); } } // remove entities that has to be restored on roundrestart (breakables etc) else if ( !FindInList( s_PreserveEnts, pCur->GetClassname() ) ) { UTIL_Remove( pCur ); } pCur = gEntList.NextEnt( pCur ); } // Really remove the entities so we can have access to their slots below. gEntList.CleanupDeleteList(); // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that // could kill respawning CTs g_EventQueue.Clear(); // Now reload the map entities. class CHL2MPMapEntityFilter : public IMapEntityFilter { public: virtual bool ShouldCreateEntity( const char *pClassname ) { // Don't recreate the preserved entities. if ( !FindInList( s_PreserveEnts, pClassname ) ) { return true; } else { // Increment our iterator since it's not going to call CreateNextEntity for this ent. if ( m_iIterator != g_MapEntityRefs.InvalidIndex() ) m_iIterator = g_MapEntityRefs.Next( m_iIterator ); return false; } } virtual CBaseEntity* CreateNextEntity( const char *pClassname ) { if ( m_iIterator == g_MapEntityRefs.InvalidIndex() ) { // This shouldn't be possible. When we loaded the map, it should have used // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list // with the same list of entities we're referring to here. Assert( false ); return NULL; } else { CMapEntityRef &ref = g_MapEntityRefs[m_iIterator]; m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity. if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) ) { // Doh! The entity was delete and its slot was reused. // Just use any old edict slot. This case sucks because we lose the baseline. return CreateEntityByName( pClassname ); } else { // Cool, the slot where this entity was is free again (most likely, the entity was // freed above). Now create an entity with this specific index. return CreateEntityByName( pClassname, ref.m_iEdict ); } } } public: int m_iIterator; // Iterator into g_MapEntityRefs. }; CHL2MPMapEntityFilter filter; filter.m_iIterator = g_MapEntityRefs.Head(); // DO NOT CALL SPAWN ON info_node ENTITIES! MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true ); }
/* ============================== SUB_UseTargets If self.delay is set, a DelayedUse entity will be created that will actually do the SUB_UseTargets after that many seconds have passed. Removes all entities with a targetname that match self.killtarget, and removes them, so some events can remove other triggers. Search for (string)targetname in all entities that match (string)self.target and call their .use function (if they have one) ============================== */ void CBaseDelay::SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ) { // // exit immediatly if we don't have a target or kill target // if( !HasTarget() && !m_iszKillTarget ) return; // // check for a delay // if( m_flDelay != 0 ) { // create a temp object to fire at a later time auto pTemp = static_cast<CBaseDelay*>( UTIL_CreateNamedEntity( "DelayedUse" ) ); pTemp->SetNextThink( gpGlobals->time + m_flDelay ); pTemp->SetThink( &CBaseDelay::DelayThink ); // Save the useType pTemp->GetButtons().Set( ( int ) useType ); pTemp->m_iszKillTarget = m_iszKillTarget; pTemp->m_flDelay = 0; // prevent "recursion" pTemp->SetTarget( GetTarget() ); // HACKHACK // This wasn't in the release build of Half-Life. We should have moved m_hActivator into this class // but changing member variable hierarchy would break save/restore without some ugly code. // This code is not as ugly as that code if( pActivator && pActivator->IsPlayer() ) // If a player activates, then save it { pTemp->SetOwner( pActivator ); } else { pTemp->SetOwner( NULL ); } return; } // // kill the killtargets // if( m_iszKillTarget ) { CBaseEntity* pKillTarget = nullptr; ALERT( at_aiconsole, "KillTarget: %s\n", STRING( m_iszKillTarget ) ); while( ( pKillTarget = UTIL_FindEntityByTargetname( pKillTarget, STRING( m_iszKillTarget ) ) ) != nullptr ) { UTIL_Remove( pKillTarget ); ALERT( at_aiconsole, "killing %s\n", pKillTarget->GetClassname() ); } } // // fire targets // if( HasTarget() ) { FireTargets( GetTarget(), pActivator, this, useType, value ); } }
void DrawOverlays(CTFBotMissionSuicideBomber *action, CTFBot *actor) { constexpr float dt = 0.1f; constexpr float target_cross_size = 5.0f; constexpr int target_cross_r = 0x00; constexpr int target_cross_g = 0xff; constexpr int target_cross_b = 0x00; constexpr float det_cross_size = 5.0f; constexpr int det_cross_r = 0xff; constexpr int det_cross_g = 0x00; constexpr int det_cross_b = 0x00; int line; char buf[1024]; /* OVERLAYS: BOT */ line = 0; auto pos = actor->GetAbsOrigin(); snprintf(buf, sizeof(buf), "POS: (%4.0f, %4.0f, %4.0f)", pos.x, pos.y, pos.z); NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); snprintf(buf, sizeof(buf), "HEALTH: %d", actor->GetHealth()); NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); CBaseEntity *target = action->m_hTarget; if (target == nullptr) { snprintf(buf, sizeof(buf), "TARGET: null"); } else { const char *name; if (target->IsPlayer()) { name = static_cast<CBasePlayer *>(target)->GetPlayerName(); } else { name = STRING(target->GetEntityName()); } snprintf(buf, sizeof(buf), "TARGET: #%d '%s' '%s'", ENTINDEX(target), target->GetClassname(), name); } NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); snprintf(buf, sizeof(buf), "PATH FAILS: %d", action->m_nConsecutivePathFailures); NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); if (action->m_ctRecomputePath.HasStarted()) { snprintf(buf, sizeof(buf), "TIMER(path): %.1f / %.1f", action->m_ctRecomputePath.GetElapsedTime(), action->m_ctRecomputePath.GetCountdownDuration()); } else { snprintf(buf, sizeof(buf), "TIMER(path): NOT STARTED"); } NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); if (action->m_ctPlaySound.HasStarted()) { snprintf(buf, sizeof(buf), "TIMER(sound): %.1f / %.1f", action->m_ctPlaySound.GetElapsedTime(), action->m_ctPlaySound.GetCountdownDuration()); } else { snprintf(buf, sizeof(buf), "TIMER(sound): NOT STARTED"); } NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); if (action->m_ctDetonation.HasStarted()) { snprintf(buf, sizeof(buf), "TIMER(det): %.1f / %.1f", action->m_ctDetonation.GetElapsedTime(), action->m_ctDetonation.GetCountdownDuration()); } else { snprintf(buf, sizeof(buf), "TIMER(det): NOT STARTED"); } NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); snprintf(buf, sizeof(buf), "DETONATING: %s", (action->m_bDetonating ? "yes" : "no")); NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); snprintf(buf, sizeof(buf), "DET(reached_goal): %s", (action->m_bDetReachedGoal ? "yes" : "no")); NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); snprintf(buf, sizeof(buf), "DET(lost_all_health): %s", (action->m_bDetLostAllHealth ? "yes" : "no")); NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255); ConVarRef tf_bot_suicide_bomb_range("tf_bot_suicide_bomb_range"); float det_range = tf_bot_suicide_bomb_range.GetFloat() / 3.0f; int det_sphere_r = 0xff; int det_sphere_g = 0x00; int det_sphere_b = 0x00; int det_sphere_a = 0x00; if (action->m_vecTargetPos.DistToSqr(actor->GetAbsOrigin()) < det_range * det_range) { Vector vec_clear = action->m_vecTargetPos; vec_clear.z += 18.0f; if (actor->IsLineOfFireClear(vec_clear)) { det_sphere_r = 0x00; det_sphere_g = 0xff; det_sphere_b = 0x00; } else { det_sphere_r = 0xff; det_sphere_g = 0xff; det_sphere_b = 0x00; } } NDebugOverlay::Sphere(actor->GetAbsOrigin(), vec3_angle, det_range, det_sphere_r, det_sphere_g, det_sphere_b, det_sphere_a, true, dt); /* OVERLAYS: TARGET POS */ line = 0; snprintf(buf, sizeof(buf), "TARGET POS: (%4.0f, %4.0f, %4.0f)", action->m_vecTargetPos.x, action->m_vecTargetPos.y, action->m_vecTargetPos.z); NDebugOverlay::EntityTextAtPosition(action->m_vecTargetPos, line++, buf, dt, 255, 255, 255, 255); snprintf(buf, sizeof(buf), "DIST TO: %.1f", action->m_vecTargetPos.DistToSqr(actor->GetAbsOrigin())); NDebugOverlay::Cross3D(action->m_vecTargetPos, target_cross_size, target_cross_r, target_cross_g, target_cross_b, true, dt); /* OVERLAYS: DETONATE POS */ line = 0; snprintf(buf, sizeof(buf), "DET POS: (%4.0f, %4.0f, %4.0f)", action->m_vecDetonatePos.x, action->m_vecDetonatePos.y, action->m_vecDetonatePos.z); NDebugOverlay::EntityTextAtPosition(action->m_vecDetonatePos, line++, buf, dt, 255, 255, 255, 255); NDebugOverlay::Cross3D(action->m_vecDetonatePos, det_cross_size, det_cross_r, det_cross_g, det_cross_b, true, dt); }
void RadiusFlash( Vector vecSrc, CBaseEntity *pevInflictor, CBaseEntity *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType) { vecSrc.z += 1;// in case grenade is lying on the ground if (!pevAttacker) pevAttacker = pevInflictor; trace_t tr; float flAdjustedDamage; variant_t var; Vector vecEyePos; float fadeTime, fadeHold; Vector vForward; Vector vecLOS; float flDot; CBaseEntity *pEntity = NULL; static float flRadius = 1500; float falloff = flDamage / flRadius; bool bInWater = (UTIL_PointContents(vecSrc) == CONTENTS_WATER); // iterate on all entities in the vicinity. while ((pEntity = gEntList.FindEntityInSphere(pEntity, vecSrc, flRadius)) != NULL) { bool bPlayer = pEntity->IsPlayer(); bool bHostage = (Q_stricmp(pEntity->GetClassname(), "hostage_entity") == 0); if (!bPlayer && !bHostage) continue; vecEyePos = pEntity->EyePosition(); // blasts don't travel into or out of water if (bInWater && pEntity->GetWaterLevel() == 0) continue; if (!bInWater && pEntity->GetWaterLevel() == 3) continue; float percentageOfFlash = PercentageOfFlashForPlayer(pEntity, vecSrc, pevInflictor); if (percentageOfFlash > 0.0) { // decrease damage for an ent that's farther from the grenade flAdjustedDamage = flDamage - (vecSrc - pEntity->EyePosition()).Length() * falloff; if (flAdjustedDamage > 0) { // See if we were facing the flash AngleVectors(pEntity->EyeAngles(), &vForward); vecLOS = (vecSrc - vecEyePos); //float flDistance = vecLOS.Length(); // Normalize both vectors so the dotproduct is in the range -1.0 <= x <= 1.0 vecLOS.NormalizeInPlace(); flDot = DotProduct(vecLOS, vForward); float startingAlpha = 255; // if target is facing the bomb, the effect lasts longer if (flDot >= 0.5) { // looking at the flashbang fadeTime = flAdjustedDamage * 2.5f; fadeHold = flAdjustedDamage * 1.25f; } else if (flDot >= -0.5) { // looking to the side fadeTime = flAdjustedDamage * 1.75f; fadeHold = flAdjustedDamage * 0.8f; } else { // facing away fadeTime = flAdjustedDamage * 1.0f; fadeHold = flAdjustedDamage * 0.75f; startingAlpha = 200; } fadeTime *= percentageOfFlash; fadeHold *= percentageOfFlash; if (bPlayer) { //MOM_TODO: do we want this functionality? // blind players and bots //CMomentumPlayer *player = static_cast<CMomentumPlayer *>(pEntity); //player->Blind( fadeHold, fadeTime, startingAlpha ); // deafen players and bots //player->Deafen( flDistance ); } else if (bHostage) { variant_t val; val.SetFloat(fadeTime); pEntity->AcceptInput("flashbang", pevInflictor, pevAttacker, val, 0); } } } } CPVSFilter filter(vecSrc); te->DynamicLight(filter, 0.0, &vecSrc, 255, 255, 255, 2, 400, 0.1, 768); }
CBaseEntity *CBasePlayer::FindUseEntity() { Vector forward, up; EyeVectors( &forward, NULL, &up ); trace_t tr; // Search for objects in a sphere (tests for entities that are not solid, yet still useable) Vector searchCenter = EyePosition(); // NOTE: Some debris objects are useable too, so hit those as well // A button, etc. can be made out of clip brushes, make sure it's +useable via a traceline, too. int useableContents = MASK_SOLID | CONTENTS_DEBRIS | CONTENTS_PLAYERCLIP; #ifdef CSTRIKE_DLL useableContents = MASK_NPCSOLID_BRUSHONLY | MASK_OPAQUE_AND_NPCS; #endif #ifdef HL1_DLL useableContents = MASK_SOLID; #endif #ifndef CLIENT_DLL CBaseEntity *pFoundByTrace = NULL; #endif // UNDONE: Might be faster to just fold this range into the sphere query CBaseEntity *pObject = NULL; float nearestDist = FLT_MAX; // try the hit entity if there is one, or the ground entity if there isn't. CBaseEntity *pNearest = NULL; const int NUM_TANGENTS = 8; // trace a box at successive angles down // forward, 45 deg, 30 deg, 20 deg, 15 deg, 10 deg, -10, -15 const float tangents[NUM_TANGENTS] = { 0, 1, 0.57735026919f, 0.3639702342f, 0.267949192431f, 0.1763269807f, -0.1763269807f, -0.267949192431f }; for ( int i = 0; i < NUM_TANGENTS; i++ ) { if ( i == 0 ) { UTIL_TraceLine( searchCenter, searchCenter + forward * 1024, useableContents, this, COLLISION_GROUP_NONE, &tr ); } else { Vector down = forward - tangents[i]*up; VectorNormalize(down); UTIL_TraceHull( searchCenter, searchCenter + down * 72, -Vector(16,16,16), Vector(16,16,16), useableContents, this, COLLISION_GROUP_NONE, &tr ); } pObject = tr.m_pEnt; #ifndef CLIENT_DLL pFoundByTrace = pObject; #endif bool bUsable = IsUseableEntity(pObject, 0); while ( pObject && !bUsable && pObject->GetMoveParent() ) { pObject = pObject->GetMoveParent(); bUsable = IsUseableEntity(pObject, 0); } if ( bUsable ) { Vector delta = tr.endpos - tr.startpos; float centerZ = CollisionProp()->WorldSpaceCenter().z; delta.z = IntervalDistance( tr.endpos.z, centerZ + CollisionProp()->OBBMins().z, centerZ + CollisionProp()->OBBMaxs().z ); float dist = delta.Length(); if ( dist < PLAYER_USE_RADIUS ) { #ifndef CLIENT_DLL if ( sv_debug_player_use.GetBool() ) { NDebugOverlay::Line( searchCenter, tr.endpos, 0, 255, 0, true, 30 ); NDebugOverlay::Cross3D( tr.endpos, 16, 0, 255, 0, true, 30 ); } if ( pObject->MyNPCPointer() && pObject->MyNPCPointer()->IsPlayerAlly( this ) ) { // If about to select an NPC, do a more thorough check to ensure // that we're selecting the right one from a group. pObject = DoubleCheckUseNPC( pObject, searchCenter, forward ); } #endif if ( sv_debug_player_use.GetBool() ) { Msg( "Trace using: %s\n", pObject ? pObject->GetDebugName() : "no usable entity found" ); } pNearest = pObject; // if this is directly under the cursor just return it now if ( i == 0 ) return pObject; } } } // check ground entity first // if you've got a useable ground entity, then shrink the cone of this search to 45 degrees // otherwise, search out in a 90 degree cone (hemisphere) if ( GetGroundEntity() && IsUseableEntity(GetGroundEntity(), FCAP_USE_ONGROUND) ) { pNearest = GetGroundEntity(); } if ( pNearest ) { // estimate nearest object by distance from the view vector Vector point; pNearest->CollisionProp()->CalcNearestPoint( searchCenter, &point ); nearestDist = CalcDistanceToLine( point, searchCenter, forward ); if ( sv_debug_player_use.GetBool() ) { Msg("Trace found %s, dist %.2f\n", pNearest->GetClassname(), nearestDist ); } } for ( CEntitySphereQuery sphere( searchCenter, PLAYER_USE_RADIUS ); ( pObject = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) { if ( !pObject ) continue; if ( !IsUseableEntity( pObject, FCAP_USE_IN_RADIUS ) ) continue; // see if it's more roughly in front of the player than previous guess Vector point; pObject->CollisionProp()->CalcNearestPoint( searchCenter, &point ); Vector dir = point - searchCenter; VectorNormalize(dir); float dot = DotProduct( dir, forward ); // Need to be looking at the object more or less if ( dot < 0.8 ) continue; float dist = CalcDistanceToLine( point, searchCenter, forward ); if ( sv_debug_player_use.GetBool() ) { Msg("Radius found %s, dist %.2f\n", pObject->GetClassname(), dist ); } if ( dist < nearestDist ) { // Since this has purely been a radius search to this point, we now // make sure the object isn't behind glass or a grate. trace_t trCheckOccluded; UTIL_TraceLine( searchCenter, point, useableContents, this, COLLISION_GROUP_NONE, &trCheckOccluded ); if ( trCheckOccluded.fraction == 1.0 || trCheckOccluded.m_pEnt == pObject ) { pNearest = pObject; nearestDist = dist; } } } #ifndef CLIENT_DLL if ( !pNearest ) { // Haven't found anything near the player to use, nor any NPC's at distance. // Check to see if the player is trying to select an NPC through a rail, fence, or other 'see-though' volume. trace_t trAllies; UTIL_TraceLine( searchCenter, searchCenter + forward * PLAYER_USE_RADIUS, MASK_OPAQUE_AND_NPCS, this, COLLISION_GROUP_NONE, &trAllies ); if ( trAllies.m_pEnt && IsUseableEntity( trAllies.m_pEnt, 0 ) && trAllies.m_pEnt->MyNPCPointer() && trAllies.m_pEnt->MyNPCPointer()->IsPlayerAlly( this ) ) { // This is an NPC, take it! pNearest = trAllies.m_pEnt; } } if ( pNearest && pNearest->MyNPCPointer() && pNearest->MyNPCPointer()->IsPlayerAlly( this ) ) { pNearest = DoubleCheckUseNPC( pNearest, searchCenter, forward ); } if ( sv_debug_player_use.GetBool() ) { if ( !pNearest ) { NDebugOverlay::Line( searchCenter, tr.endpos, 255, 0, 0, true, 30 ); NDebugOverlay::Cross3D( tr.endpos, 16, 255, 0, 0, true, 30 ); } else if ( pNearest == pFoundByTrace ) { NDebugOverlay::Line( searchCenter, tr.endpos, 0, 255, 0, true, 30 ); NDebugOverlay::Cross3D( tr.endpos, 16, 0, 255, 0, true, 30 ); } else { NDebugOverlay::Box( pNearest->WorldSpaceCenter(), Vector(-8, -8, -8), Vector(8, 8, 8), 0, 255, 0, true, 30 ); } } #endif if ( sv_debug_player_use.GetBool() ) { Msg( "Radial using: %s\n", pNearest ? pNearest->GetDebugName() : "no usable entity found" ); } return pNearest; }
bool CSDKPlayer::PlayerUse() { #ifdef GAME_DLL // Was use pressed or released? if ( ((m_nButtons | m_afButtonPressed | m_afButtonReleased) & IN_USE) && !IsObserver() ) { Vector forward, up; EyeVectors( &forward, NULL, &up ); Vector vecSearchCenter = EyePosition(); CBaseEntity *pObject = nullptr; CBaseEntity *pNearest = nullptr; float flNearest = FLT_MAX; // Look for grenades so we can prioritize picking them up first. for ( CEntitySphereQuery sphere( vecSearchCenter, PLAYER_USE_RADIUS ); ( pObject = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) { if ( !pObject ) continue; if ( !IsUseableEntity( pObject, FCAP_USE_IN_RADIUS ) ) continue; CWeaponSDKBase* pWeapon = dynamic_cast<CWeaponSDKBase*>(pObject); if (!pWeapon) continue; if (pWeapon->GetWeaponID() != SDK_WEAPON_GRENADE) continue; // If we're full up on grenades, pass over to whatever other weapons are lying around. if (!g_pGameRules->CanHavePlayerItem(this, pWeapon)) continue; // see if it's more roughly in front of the player than previous guess Vector point; pObject->CollisionProp()->CalcNearestPoint( vecSearchCenter, &point ); Vector dir = point - vecSearchCenter; VectorNormalize(dir); float dot = DotProduct( dir, forward ); // Need to be looking at the object more or less if ( dot < 0.8 ) continue; float dist = CalcDistanceToLine( point, vecSearchCenter, forward ); ConVarRef sv_debug_player_use("sv_debug_player_use"); if ( sv_debug_player_use.GetBool() ) { Msg("Radius found %s, dist %.2f\n", pObject->GetClassname(), dist ); } // Not worried about shit being behind a wall at this point. // Just greedily gobble up all nearby grenades since there's // no penalty to the player for doing so. if ( dist < flNearest ) { pNearest = pObject; flNearest = dist; } } if (pNearest) { // This is a grenade. Use it to pick it up. variant_t emptyVariant; pNearest->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE ); return true; } } #endif bool bUsed = BaseClass::PlayerUse(); if (bUsed) return bUsed; if (!(m_afButtonPressed & IN_USE)) return false; if (!IsAlive()) return false; return false; }
//========================================================= // Deathnotice. //========================================================= void CHL2MPRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ) { #ifndef CLIENT_DLL // Work out what killed the player, and send a message to all clients about it const char *killer_weapon_name = "world"; // by default, the player is killed by the world int killer_ID = 0; // Find the killer & the scorer CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pKiller = info.GetAttacker(); CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor ); // Custom kill type? if ( info.GetDamageCustom() ) { killer_weapon_name = GetDamageCustomString( info ); if ( pScorer ) { killer_ID = pScorer->GetUserID(); } } else { // Is the killer a client? if ( pScorer ) { killer_ID = pScorer->GetUserID(); if ( pInflictor ) { if ( pInflictor == pScorer ) { // If the inflictor is the killer, then it must be their current weapon doing the damage if ( pScorer->GetActiveWeapon() ) { killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname(); } } else { killer_weapon_name = pInflictor->GetClassname(); // it's just that easy } } } else { killer_weapon_name = pInflictor->GetClassname(); } // strip the NPC_* or weapon_* from the inflictor's classname if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 ) { killer_weapon_name += 7; } else if ( strncmp( killer_weapon_name, "npc_", 4 ) == 0 ) { killer_weapon_name += 4; } else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 ) { killer_weapon_name += 5; } else if ( strstr( killer_weapon_name, "physics" ) ) { killer_weapon_name = "physics"; } if ( strcmp( killer_weapon_name, "prop_combine_ball" ) == 0 ) { killer_weapon_name = "combine_ball"; } else if ( strcmp( killer_weapon_name, "grenade_ar2" ) == 0 ) { killer_weapon_name = "smg1_grenade"; } else if ( strcmp( killer_weapon_name, "satchel" ) == 0 || strcmp( killer_weapon_name, "tripmine" ) == 0) { killer_weapon_name = "slam"; } } IGameEvent *event = gameeventmanager->CreateEvent( "player_death" ); if( event ) { event->SetInt("userid", pVictim->GetUserID() ); event->SetInt("attacker", killer_ID ); event->SetString("weapon", killer_weapon_name ); event->SetInt( "priority", 7 ); gameeventmanager->FireEvent( event ); } #endif }
//--------------------------------------------------------- // Purpose: See if it is time to poll and do so. // // SOON: We need to load-balance this. //--------------------------------------------------------- void CVisibilityMonitor::FrameUpdatePostEntityThink() { if( gpGlobals->curtime < m_flTimeNextPoll ) return; m_flTimeNextPoll = gpGlobals->curtime + vismon_poll_frequency.GetFloat(); int iDebugging = debug_visibility_monitor.GetInt(); if( m_Entities.Count() > m_iMaxEntitiesPerThink ) m_iMaxEntitiesPerThink = m_Entities.Count(); if( iDebugging > 1 ) { Msg("\nVisMon: Polling now. (Frequency: %f)\n", m_flPollFrequency ); Msg("VisMon: Time: %f - Tracking %d Entities. (Max:%d)\n", gpGlobals->curtime, m_Entities.Count(), m_iMaxEntitiesPerThink ); } // Cleanup, dump entities that have been removed since we last polled. for ( int i = 0 ; i < m_Entities.Count() ; i++ ) { if ( m_Entities[i].entity == NULL ) { m_Entities.FastRemove(i); if ( i >= m_Entities.Count() ) { break; } } } int numTraces = 0; bool bHitTraceLimit = false; if( m_iStartElement >= m_Entities.Count() ) { if( iDebugging > 1 ) { Msg("VisMon: RESET\n"); } m_iStartElement = 0; } if( iDebugging > 1 ) { Msg("VisMon: Starting at element: %d\n", m_iStartElement ); } for( int i = m_iStartElement ; i < m_Entities.Count() ; i++ ) { for( int j = 1 ; j <= gpGlobals->maxClients ; j++ ) { CBasePlayer *pPlayer =UTIL_PlayerByIndex( j ); if( pPlayer != NULL && pPlayer->IsAlive() && !pPlayer->IsBot() ) { int memoryBit = 1 << j; // The bit that is used to remember whether a given entity has been seen by a given player. CBaseEntity *pSeeEntity = m_Entities[ i ].entity.Get(); if( pSeeEntity == NULL ) { continue; } if( !(m_Entities[i].memory & memoryBit) ) { // If this player hasn't seen this entity yet, check it. if( EntityIsVisibleToPlayer( m_Entities[i], pPlayer, &numTraces ) ) { bool bIgnore = false; if( m_Entities[i].pfnEvaluator != NULL && !m_Entities[i].pfnEvaluator( m_Entities[i].entity, pPlayer ) ) { bIgnore = true; } // See it! Generate our event. if( iDebugging > 0 ) { if( bIgnore ) { Msg("VisMon: Player %s IGNORING VISIBILE Entity: %s\n", pPlayer->GetDebugName(), pSeeEntity->GetDebugName() ); NDebugOverlay::Cross3D( pSeeEntity->WorldSpaceCenter(), 16, 255, 0, 0, false, 10.0f ); } else { Msg("VisMon: Player %s sees Entity: %s\n", pPlayer->GetDebugName(), pSeeEntity->GetDebugName() ); NDebugOverlay::Cross3D( pSeeEntity->WorldSpaceCenter(), 16, 0, 255, 0, false, 10.0f ); } } if( !bIgnore ) { bool bGenerateEvent = true; if( m_Entities[i].pfnCallback != NULL ) { // Make the callback, and let it determine whether to generate the simple event. bGenerateEvent = m_Entities[i].pfnCallback( m_Entities[i].entity, pPlayer ); } if( bGenerateEvent ) { // No callback, generate the generic game event. IGameEvent * event = gameeventmanager->CreateEvent( "entity_visible" ); if ( event ) { event->SetInt( "userid", pPlayer->GetUserID() ); event->SetInt( "subject", pSeeEntity->entindex() ); event->SetString( "classname", pSeeEntity->GetClassname() ); event->SetString( "entityname", STRING( pSeeEntity->GetEntityName() ) ); gameeventmanager->FireEvent( event ); } } // Remember that this entity was visible to the player m_Entities[i].memory |= memoryBit; } } } } } if( numTraces >= vismon_trace_limit.GetInt() ) { if( iDebugging > 1 ) { Msg("VisMon: MAX Traces. Stopping after element %d\n", i ); } m_iStartElement = i + 1; // Pick up here next think. bHitTraceLimit = true; break; } } if( !bHitTraceLimit ) { m_iStartElement = 0; } if( numTraces > m_iMaxTracesPerThink ) m_iMaxTracesPerThink = numTraces; if( iDebugging > 1 ) { Msg("VisMon: %d traces performed during this polling cycle (Max: %d)\n\n", numTraces, m_iMaxTracesPerThink ); } }