int CMTalkMonster :: FOkToSpeak( void ) { // if in the grip of a barnacle, don't speak if ( m_MonsterState == MONSTERSTATE_PRONE || m_IdealMonsterState == MONSTERSTATE_PRONE ) { return FALSE; } // if not alive, certainly don't speak if ( pev->deadflag != DEAD_NO ) { return FALSE; } // if someone else is talking, don't speak if (gpGlobals->time <= CMTalkMonster::g_talkWaitTime) return FALSE; if ( pev->spawnflags & SF_MONSTER_GAG ) return FALSE; if ( m_MonsterState == MONSTERSTATE_PRONE ) return FALSE; // if player is not in pvs, don't speak if (!IsAlive() || FNullEnt(FIND_CLIENT_IN_PVS(edict()))) return FALSE; // don't talk if you're in combat if (m_hEnemy != NULL && UTIL_FVisible( m_hEnemy, this->edict() )) return FALSE; return TRUE; }
void CTriggerHurt :: Think( void ) { edict_t *pentPlayer; CBasePlayer *pPlayer = NULL; float flRange; entvars_t *pevTarget; Vector vecSpot1; Vector vecSpot2; Vector vecRange; Vector origin; Vector view_ofs; origin = pev->origin; view_ofs = pev->view_ofs; pev->origin = (pev->absmin + pev->absmax) * 0.5; pev->view_ofs = pev->view_ofs * 0.0; pentPlayer = FIND_CLIENT_IN_PVS(edict()); pev->origin = origin; pev->view_ofs = view_ofs; if (!FNullEnt(pentPlayer)) { pPlayer = GetClassPtr( (CBasePlayer *)VARS(pentPlayer)); pevTarget = VARS(pentPlayer); vecSpot1 = (pev->absmin + pev->absmax) * 0.5; vecSpot2 = (pevTarget->absmin + pevTarget->absmax) * 0.5; vecRange = vecSpot1 - vecSpot2; flRange = vecRange.Length(); if (pPlayer->m_flgeigerRange >= flRange) pPlayer->m_flgeigerRange = flRange; } SetNextThink( 0.25 ); }
void CGib::SpawnHeadGib(entvars_t *pevVictim) { CGib *pGib = GetClassPtr<CCSGib>((CGib *)NULL); if (g_Language == LANGUAGE_GERMAN) { // throw one head pGib->Spawn("models/germangibs.mdl"); pGib->pev->body = 0; } else { // throw one head pGib->Spawn("models/hgibs.mdl"); pGib->pev->body = 0; } if (pevVictim) { pGib->pev->origin = pevVictim->origin + pevVictim->view_ofs; edict_t *pentPlayer = FIND_CLIENT_IN_PVS(pGib->edict()); if (RANDOM_LONG(0, 100) <= 5 && pentPlayer != NULL) { // 5% chance head will be thrown at player's face. entvars_t *pevPlayer = VARS(pentPlayer); pGib->pev->velocity = ((pevPlayer->origin + pevPlayer->view_ofs) - pGib->pev->origin).Normalize() * 300; pGib->pev->velocity.z += 100; } else { // TODO: fix test demo pGib->pev->velocity.z = RANDOM_FLOAT(200, 300); pGib->pev->velocity.y = RANDOM_FLOAT(-100, 100); pGib->pev->velocity.x = RANDOM_FLOAT(-100, 100); } pGib->pev->avelocity.x = RANDOM_FLOAT(100, 200); pGib->pev->avelocity.y = RANDOM_FLOAT(100, 300); // copy owner's blood color pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor(); if (pevVictim->health > -50) { pGib->pev->velocity = pGib->pev->velocity * 0.7; } else if (pevVictim->health > -200) { pGib->pev->velocity = pGib->pev->velocity * 2; } else pGib->pev->velocity = pGib->pev->velocity * 4; } pGib->LimitVelocity(); }
//========================================================= //========================================================= void CFlockingFlyer :: IdleThink( void ) { pev->nextthink = gpGlobals->time + 0.2; // see if there's a client in the same pvs as the monster if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) { SetThink( &CFlockingFlyer::Start ); pev->nextthink = gpGlobals->time + 0.1; } }
//========================================================= // Look - overriden for the roach, which can virtually see // 360 degrees. //========================================================= void CRoach :: Look ( int iDistance ) { CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with CBaseEntity *pPreviousEnt;// the last entity added to the link list int iSighted = 0; // DON'T let visibility information from last frame sit around! ClearConditions( bits_COND_SEE_HATE |bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR ); // don't let monsters outside of the player's PVS act up, or most of the interesting // things will happen before the player gets there! if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) { return; } m_pLink = NULL; pPreviousEnt = this; // Does sphere also limit itself to PVS? // Examine all entities within a reasonable radius // !!!PERFORMANCE - let's trivially reject the ent list before radius searching! while ((pSightEnt = UTIL_FindEntityInSphere( pSightEnt, pev->origin, iDistance )) != NULL) { // only consider ents that can be damaged. !!!temporarily only considering other monsters and clients if ( pSightEnt->IsPlayer() || FBitSet ( pSightEnt->pev->flags, FL_MONSTER ) ) { if ( /*FVisible( pSightEnt ) &&*/ !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && pSightEnt->pev->health > 0 ) { // NULL the Link pointer for each ent added to the link list. If other ents follow, the will overwrite // this value. If this ent happens to be the last, the list will be properly terminated. pPreviousEnt->m_pLink = pSightEnt; pSightEnt->m_pLink = NULL; pPreviousEnt = pSightEnt; // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when // we see monsters other than the Enemy. switch ( IRelationship ( pSightEnt ) ) { case R_FR: iSighted |= bits_COND_SEE_FEAR; break; case R_NO: break; default: ALERT ( at_console, "%s can't asses %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) ); break; } } } } SetConditions( iSighted ); }
//========================================================= //========================================================= void CFlockingFlyer :: IdleThink( void ) { SetNextThink( 0.2 ); // see if there's a client in the same pvs as the monster // if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict()))) if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict())) || HaveCamerasInPVS( edict() )) { SetThink(&CFlockingFlyer :: Start ); SetNextThink( 0.1 ); } }
//========================================================= // RunAI //========================================================= void CBaseMonster :: RunAI ( void ) { // to test model's eye height //UTIL_ParticleEffect ( pev->origin + pev->view_ofs, g_vecZero, 255, 10 ); // IDLE sound permitted in ALERT state is because monsters were silent in ALERT state. Only play IDLE sound in IDLE state // once we have sounds for that state. if ( ( m_MonsterState == MONSTERSTATE_IDLE || m_MonsterState == MONSTERSTATE_ALERT ) && RANDOM_LONG(0,99) == 0 && !(pev->flags & SF_MONSTER_GAG) ) { IdleSound(); } if ( m_MonsterState != MONSTERSTATE_NONE && m_MonsterState != MONSTERSTATE_PRONE && m_MonsterState != MONSTERSTATE_DEAD )// don't bother with this crap if monster is prone. { // collect some sensory Condition information. // don't let monsters outside of the player's PVS act up, or most of the interesting // things will happen before the player gets there! // UPDATE: We now let COMBAT state monsters think and act fully outside of player PVS. This allows the player to leave // an area where monsters are fighting, and the fight will continue. if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) || ( m_MonsterState == MONSTERSTATE_COMBAT ) ) { Look( m_flDistLook ); Listen();// check for audible sounds. // now filter conditions. ClearConditions( IgnoreConditions() ); GetEnemy(); } // do these calculations if monster has an enemy. if ( m_hEnemy != NULL ) { CheckEnemy( m_hEnemy ); } CheckAmmo(); } FCheckAITrigger(); PrescheduleThink(); MaintainSchedule(); // if the monster didn't use these conditions during the above call to MaintainSchedule() or CheckAITrigger() // we throw them out cause we don't want them sitting around through the lifespan of a schedule // that doesn't use them. m_afConditions &= ~( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ); }
void CGib :: SpawnHeadGib( entvars_t *pevVictim, const char* szGibModel ) { CGib *pGib = GetClassPtr( (CGib *)NULL ); pGib->Spawn( szGibModel );// throw one head pGib->pev->body = 0; if ( pevVictim ) { pGib->pev->origin = pevVictim->origin + pevVictim->view_ofs; edict_t *pentPlayer = FIND_CLIENT_IN_PVS( pGib->edict() ); if ( RANDOM_LONG ( 0, 100 ) <= 5 && pentPlayer ) { // 5% chance head will be thrown at player's face. entvars_t *pevPlayer; pevPlayer = VARS( pentPlayer ); pGib->pev->velocity = ( ( pevPlayer->origin + pevPlayer->view_ofs ) - pGib->pev->origin ).Normalize() * 300; pGib->pev->velocity.z += 100; } else { pGib->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); } pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); // copy owner's blood color pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor(); if ( pevVictim->health > -50) { pGib->pev->velocity = pGib->pev->velocity * 0.7; } else if ( pevVictim->health > -200) { pGib->pev->velocity = pGib->pev->velocity * 2; } else { pGib->pev->velocity = pGib->pev->velocity * 4; } } pGib->LimitVelocity(); }
void CGib :: SpawnHeadGib( entvars_t *pevVictim ) { CGib *pGib = GetClassPtr( (CGib *)NULL ); if ( g_Language == LANGUAGE_GERMAN ) { pGib->Spawn( "models/germangibs.mdl" );// throw one head pGib->pev->body = 0; } else { pGib->Spawn( "models/head.mdl" );// throw one head } if ( pevVictim ) { pGib->pev->origin = pevVictim->origin + pevVictim->view_ofs; edict_t *pentPlayer = FIND_CLIENT_IN_PVS( pGib->edict() ); pGib->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(300,400)); pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); // copy owner's blood color pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor(); // Set its groupinfo to the player's pGib->pev->groupinfo = pevVictim->groupinfo; // Since this only ever happens when a player is hit by a frozen decapitator disc, make the gibs glow pGib->pev->renderfx = kRenderFxGlowShell; pGib->pev->rendercolor = Vector( 100,100, 250 ); pGib->pev->renderamt = 25; } pGib->LimitVelocity(); }
/* <8efe4> ../cstrike/dlls/func_tank.cpp:488 */ void CFuncTank::TrackTarget(void) { TraceResult tr; edict_t *pPlayer = FIND_CLIENT_IN_PVS(edict()); BOOL updateTime = FALSE, lineOfSight; Vector angles, direction, targetPosition, barrelEnd; edict_t *pTarget = NULL; // Get a position to aim for if (m_pController != NULL) { // Tanks attempt to mirror the player's angles angles = m_pController->pev->v_angle; angles.x = 0 - angles.x; pev->nextthink = pev->ltime + 0.05; } else { if (IsActive()) pev->nextthink = pev->ltime + 0.1; else return; if (FNullEnt(pPlayer)) { if (IsActive()) { // Wait 2 secs pev->nextthink = pev->ltime + 2; } return; } pTarget = FindTarget(pPlayer); if (!pTarget) { return; } // Calculate angle needed to aim at target barrelEnd = BarrelPosition(); targetPosition = pTarget->v.origin + pTarget->v.view_ofs; float range = (targetPosition - barrelEnd).Length(); if (!InRange(range)) return; UTIL_TraceLine(barrelEnd, targetPosition, dont_ignore_monsters, edict(), &tr); lineOfSight = FALSE; // No line of sight, don't track if (tr.flFraction == 1.0f || tr.pHit == pTarget) { lineOfSight = TRUE; CBaseEntity *pInstance = CBaseEntity::Instance(pTarget); if (InRange(range) && pInstance && pInstance->IsAlive()) { updateTime = TRUE; m_sightOrigin = UpdateTargetPosition(pInstance); } } // Track sight origin // !!! I'm not sure what i changed direction = m_sightOrigin - pev->origin; //direction = m_sightOrigin - barrelEnd; angles = UTIL_VecToAngles(direction); // Calculate the additional rotation to point the end of the barrel at the target (not the gun's center) AdjustAnglesForBarrel(angles, direction.Length()); } angles.x = -angles.x; // Force the angles to be relative to the center position angles.y = m_yawCenter + UTIL_AngleDistance(angles.y, m_yawCenter); angles.x = m_pitchCenter + UTIL_AngleDistance(angles.x, m_pitchCenter); // Limit against range in y if (angles.y > m_yawCenter + m_yawRange) { angles.y = m_yawCenter + m_yawRange; // Don't update if you saw the player, but out of range updateTime = FALSE; } else if (angles.y < (m_yawCenter - m_yawRange)) { angles.y = (m_yawCenter - m_yawRange); // Don't update if you saw the player, but out of range updateTime = FALSE; } if (updateTime) { m_lastSightTime = gpGlobals->time; } // Move toward target at rate or less float_precision distY = UTIL_AngleDistance(angles.y, pev->angles.y); pev->avelocity.y = distY * 10; if (pev->avelocity.y > m_yawRate) { pev->avelocity.y = m_yawRate; } else if (pev->avelocity.y < -m_yawRate) { pev->avelocity.y = -m_yawRate; } // Limit against range in x if (angles.x > m_pitchCenter + m_pitchRange) { angles.x = m_pitchCenter + m_pitchRange; } else if (angles.x < m_pitchCenter - m_pitchRange) { angles.x = m_pitchCenter - m_pitchRange; } // Move toward target at rate or less float_precision distX = UTIL_AngleDistance(angles.x, pev->angles.x); pev->avelocity.x = distX * 10; if (pev->avelocity.x > m_pitchRate) { pev->avelocity.x = m_pitchRate; } else if (pev->avelocity.x < -m_pitchRate) { pev->avelocity.x = -m_pitchRate; } if (m_pController != NULL) { return; } if (CanFire() && ((fabs(distX) < m_pitchTolerance && fabs(distY) < m_yawTolerance) || (pev->spawnflags & SF_TANK_LINEOFSIGHT))) { BOOL fire = FALSE; Vector forward; UTIL_MakeVectorsPrivate(pev->angles, forward, NULL, NULL); if (pev->spawnflags & SF_TANK_LINEOFSIGHT) { float length = direction.Length(); UTIL_TraceLine(barrelEnd, barrelEnd + forward * length, dont_ignore_monsters, edict(), &tr); if (tr.pHit == pTarget) { fire = TRUE; } } else fire = TRUE; if (fire) { Fire(BarrelPosition(), forward, pev); } else m_fireLast = 0; } else m_fireLast = 0; }
void CEnvSound::Think( void ) { // get pointer to client if visible; FIND_CLIENT_IN_PVS will // cycle through visible clients on consecutive calls. edict_t *pentPlayer = FIND_CLIENT_IN_PVS( edict() ); CBasePlayer *pPlayer = NULL; if( FNullEnt( pentPlayer ) ) goto env_sound_Think_slow; // no player in pvs of sound entity, slow it down pPlayer = GetClassPtr( ( CBasePlayer * ) VARS( pentPlayer ) ); float flRange; // check to see if this is the sound entity that is // currently affecting this player if( !FNullEnt( pPlayer->m_pentSndLast ) && ( pPlayer->m_pentSndLast == ENT( pev ) ) ) { // this is the entity currently affecting player, check // for validity if( pPlayer->m_flSndRoomtype != 0 && pPlayer->m_flSndRange != 0 ) { // we're looking at a valid sound entity affecting // player, make sure it's still valid, update range if( FEnvSoundInRange( pev, VARS( pentPlayer ), &flRange ) ) { pPlayer->m_flSndRange = flRange; goto env_sound_Think_fast; } else { // current sound entity affecting player is no longer valid, // flag this state by clearing room_type and range. // NOTE: we do not actually change the player's room_type // NOTE: until we have a new valid room_type to change it to. pPlayer->m_flSndRange = 0; pPlayer->m_flSndRoomtype = 0; goto env_sound_Think_slow; } } else { // entity is affecting player but is out of range, // wait passively for another entity to usurp it... goto env_sound_Think_slow; } } // if we got this far, we're looking at an entity that is contending // for current player sound. the closest entity to player wins. if( FEnvSoundInRange( pev, VARS( pentPlayer ), &flRange ) ) { if( flRange < pPlayer->m_flSndRange || pPlayer->m_flSndRange == 0 ) { // new entity is closer to player, so it wins. pPlayer->m_pentSndLast = ENT( pev ); pPlayer->m_flSndRoomtype = m_flRoomtype; pPlayer->m_flSndRange = flRange; // send room_type command to player's server. // this should be a rare event - once per change of room_type // only! //CLIENT_COMMAND(pentPlayer, "room_type %f", m_flRoomtype); MESSAGE_BEGIN( MSG_ONE, SVC_ROOMTYPE, NULL, pentPlayer ); // use the magic #1 for "one client" WRITE_SHORT( ( short ) m_flRoomtype ); // sequence number MESSAGE_END(); // crank up nextthink rate for new active sound entity // by falling through to think_fast... } // player is not closer to the contending sound entity, // just fall through to think_fast. this effectively // cranks up the think_rate of entities near the player. } // player is in pvs of sound entity, but either not visible or // not in range. do nothing, fall through to think_fast... env_sound_Think_fast: pev->nextthink = gpGlobals->time + 0.25; return; env_sound_Think_slow: pev->nextthink = gpGlobals->time + 0.75; return; }
//========================================================= // GetScheduleOfType //========================================================= Schedule_t* CHoundeye :: GetScheduleOfType ( int Type ) { if ( m_fAsleep ) { // if the hound is sleeping, must wake and stand! if ( HasConditions( bits_COND_HEAR_SOUND ) ) { CSound *pWakeSound; pWakeSound = PBestSound(); ASSERT( pWakeSound != NULL ); if ( pWakeSound ) { MakeIdealYaw ( pWakeSound->m_vecOrigin ); if ( FLSoundVolume ( pWakeSound ) >= HOUNDEYE_SOUND_STARTLE_VOLUME ) { // awakened by a loud sound return &slHoundWakeUrgent[ 0 ]; } } // sound was not loud enough to scare the bejesus out of houndeye return &slHoundWakeLazy[ 0 ]; } else if ( HasConditions( bits_COND_NEW_ENEMY ) ) { // get up fast, to fight. return &slHoundWakeUrgent[ 0 ]; } else { // hound is waking up on its own return &slHoundWakeLazy[ 0 ]; } } switch ( Type ) { case SCHED_IDLE_STAND: { // we may want to sleep instead of stand! if ( InSquad() && !IsLeader() && !m_fAsleep && RANDOM_LONG(0,29) < 1 ) { return &slHoundSleep[ 0 ]; } else { return CSquadMonster :: GetScheduleOfType( Type ); } } case SCHED_RANGE_ATTACK1: { return &slHoundRangeAttack[ 0 ]; /* if ( InSquad() ) { return &slHoundRangeAttack[ RANDOM_LONG( 0, 1 ) ]; } return &slHoundRangeAttack[ 1 ]; */ } case SCHED_SPECIAL_ATTACK1: { return &slHoundSpecialAttack1[ 0 ]; } case SCHED_GUARD: { return &slHoundGuardPack[ 0 ]; } case SCHED_HOUND_AGITATED: { return &slHoundAgitated[ 0 ]; } case SCHED_HOUND_HOP_RETREAT: { return &slHoundHopRetreat[ 0 ]; } case SCHED_FAIL: { if ( m_MonsterState == MONSTERSTATE_COMBAT ) { if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) { // client in PVS return &slHoundCombatFailPVS[ 0 ]; } else { // client has taken off! return &slHoundCombatFailNoPVS[ 0 ]; } } else { return CSquadMonster :: GetScheduleOfType ( Type ); } } default: { return CSquadMonster :: GetScheduleOfType ( Type ); } } }
//========================================================= // RunTask //========================================================= void CBaseMonster :: RunTask ( Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_TURN_RIGHT: case TASK_TURN_LEFT: { ChangeYaw( pev->yaw_speed ); if ( FacingIdeal() ) { TaskComplete(); } break; } case TASK_PLAY_SEQUENCE_FACE_ENEMY: case TASK_PLAY_SEQUENCE_FACE_TARGET: { CBaseEntity *pTarget; if ( pTask->iTask == TASK_PLAY_SEQUENCE_FACE_TARGET ) pTarget = m_hTargetEnt; else pTarget = m_hEnemy; if ( pTarget ) { pev->ideal_yaw = UTIL_VecToYaw( pTarget->pev->origin - pev->origin ); ChangeYaw( pev->yaw_speed ); } if ( m_fSequenceFinished ) TaskComplete(); } break; case TASK_PLAY_SEQUENCE: case TASK_PLAY_ACTIVE_IDLE: { if ( m_fSequenceFinished ) { TaskComplete(); } break; } case TASK_FACE_ENEMY: { MakeIdealYaw( m_vecEnemyLKP ); ChangeYaw( pev->yaw_speed ); if ( FacingIdeal() ) { TaskComplete(); } break; } case TASK_FACE_HINTNODE: case TASK_FACE_LASTPOSITION: case TASK_FACE_TARGET: case TASK_FACE_IDEAL: case TASK_FACE_ROUTE: { ChangeYaw( pev->yaw_speed ); if ( FacingIdeal() ) { TaskComplete(); } break; } case TASK_WAIT_PVS: { if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict())) ) { TaskComplete(); } break; } case TASK_WAIT_INDEFINITE: { // don't do anything. break; } case TASK_WAIT: case TASK_WAIT_RANDOM: { if ( gpGlobals->time >= m_flWaitFinished ) { TaskComplete(); } break; } case TASK_WAIT_FACE_ENEMY: { MakeIdealYaw ( m_vecEnemyLKP ); ChangeYaw( pev->yaw_speed ); if ( gpGlobals->time >= m_flWaitFinished ) { TaskComplete(); } break; } case TASK_MOVE_TO_TARGET_RANGE: { float distance; if ( m_hTargetEnt == NULL ) TaskFail(); else { distance = ( m_vecMoveGoal - pev->origin ).Length2D(); // Re-evaluate when you think your finished, or the target has moved too far if ( (distance < pTask->flData) || (m_vecMoveGoal - m_hTargetEnt->pev->origin).Length() > pTask->flData * 0.5 ) { m_vecMoveGoal = m_hTargetEnt->pev->origin; distance = ( m_vecMoveGoal - pev->origin ).Length2D(); FRefreshRoute(); } // Set the appropriate activity based on an overlapping range // overlap the range to prevent oscillation if ( distance < pTask->flData ) { TaskComplete(); RouteClear(); // Stop moving } else if ( distance < 190 && m_movementActivity != ACT_WALK ) m_movementActivity = ACT_WALK; else if ( distance >= 270 && m_movementActivity != ACT_RUN ) m_movementActivity = ACT_RUN; } break; } case TASK_WAIT_FOR_MOVEMENT: { if (MovementIsComplete()) { TaskComplete(); RouteClear(); // Stop moving } break; } case TASK_DIE: { if ( m_fSequenceFinished && pev->frame >= 255 ) { pev->deadflag = DEAD_DEAD; SetThink ( NULL ); StopAnimation(); if ( !BBoxFlat() ) { // a bit of a hack. If a corpses' bbox is positioned such that being left solid so that it can be attacked will // block the player on a slope or stairs, the corpse is made nonsolid. // pev->solid = SOLID_NOT; UTIL_SetSize ( pev, Vector ( -4, -4, 0 ), Vector ( 4, 4, 1 ) ); } else // !!!HACKHACK - put monster in a thin, wide bounding box until we fix the solid type/bounding volume problem UTIL_SetSize ( pev, Vector ( pev->mins.x, pev->mins.y, pev->mins.z ), Vector ( pev->maxs.x, pev->maxs.y, pev->mins.z + 1 ) ); if ( ShouldFadeOnDeath() ) { // this monster was created by a monstermaker... fade the corpse out. SUB_StartFadeOut(); } else { // body is gonna be around for a while, so have it stink for a bit. CSoundEnt::InsertSound ( bits_SOUND_CARCASS, pev->origin, 384, 30 ); } } break; } case TASK_RANGE_ATTACK1_NOTURN: case TASK_MELEE_ATTACK1_NOTURN: case TASK_MELEE_ATTACK2_NOTURN: case TASK_RANGE_ATTACK2_NOTURN: case TASK_RELOAD_NOTURN: { if ( m_fSequenceFinished ) { m_Activity = ACT_RESET; TaskComplete(); } break; } case TASK_RANGE_ATTACK1: case TASK_MELEE_ATTACK1: case TASK_MELEE_ATTACK2: case TASK_RANGE_ATTACK2: case TASK_SPECIAL_ATTACK1: case TASK_SPECIAL_ATTACK2: case TASK_RELOAD: { MakeIdealYaw ( m_vecEnemyLKP ); ChangeYaw ( pev->yaw_speed ); if ( m_fSequenceFinished ) { m_Activity = ACT_RESET; TaskComplete(); } break; } case TASK_SMALL_FLINCH: { if ( m_fSequenceFinished ) { TaskComplete(); } } break; case TASK_WAIT_FOR_SCRIPT: { if ( m_pCine->m_iDelay <= 0 && gpGlobals->time >= m_pCine->m_startTime ) { TaskComplete(); } break; } case TASK_PLAY_SCRIPT: { // ALERT(at_console, "Play Script\n"); if (m_fSequenceFinished) { // ALERT(at_console, "Anim Finished\n"); if (m_pCine->m_iRepeatsLeft > 0) { // ALERT(at_console, "Frame %f; Repeat %d from %f\n", pev->frame, m_pCine->m_iRepeatsLeft, m_pCine->m_fRepeatFrame); m_pCine->m_iRepeatsLeft--; pev->frame = m_pCine->m_fRepeatFrame; ResetSequenceInfo( ); } else { TaskComplete(); } } break; } } }
//========================================================= // MonsterThink, overridden for roaches. //========================================================= void CRoach :: MonsterThink( void ) { if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1,1.5); else pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking float flInterval = StudioFrameAdvance( ); // animate if ( !m_fLightHacked ) { // if light value hasn't been collection for the first time yet, // suspend the creature for a second so the world finishes spawning, then we'll collect the light level. pev->nextthink = gpGlobals->time + 1; m_fLightHacked = TRUE; return; } else if ( m_flLastLightLevel < 0 ) { // collect light level for the first time, now that all of the lightmaps in the roach's area have been calculated. m_flLastLightLevel = GETENTITYILLUM( ENT( pev ) ); } switch ( m_iMode ) { case ROACH_IDLE: case ROACH_EAT: { // if not moving, sample environment to see if anything scary is around. Do a radius search 'look' at random. if ( RANDOM_LONG(0,3) == 1 ) { Look( 150 ); if (HasConditions(bits_COND_SEE_FEAR)) { // if see something scary //ALERT ( at_aiconsole, "Scared\n" ); Eat( 30 + ( RANDOM_LONG(0,14) ) );// roach will ignore food for 30 to 45 seconds PickNewDest( ROACH_SCARED_BY_ENT ); SetActivity ( ACT_WALK ); } else if ( RANDOM_LONG(0,149) == 1 ) { // if roach doesn't see anything, there's still a chance that it will move. (boredom) //ALERT ( at_aiconsole, "Bored\n" ); PickNewDest( ROACH_BORED ); SetActivity ( ACT_WALK ); if ( m_iMode == ROACH_EAT ) { // roach will ignore food for 30 to 45 seconds if it got bored while eating. Eat( 30 + ( RANDOM_LONG(0,14) ) ); } } } // don't do this stuff if eating! if ( m_iMode == ROACH_IDLE ) { if ( FShouldEat() ) { Listen(); } if ( GETENTITYILLUM( ENT(pev) ) > m_flLastLightLevel ) { // someone turned on lights! //ALERT ( at_console, "Lights!\n" ); PickNewDest( ROACH_SCARED_BY_LIGHT ); SetActivity ( ACT_WALK ); } else if ( HasConditions(bits_COND_SMELL_FOOD) ) { CSound *pSound; pSound = CSoundEnt::SoundPointerForIndex( m_iAudibleList ); // roach smells food and is just standing around. Go to food unless food isn't on same z-plane. if ( pSound && abs( pSound->m_vecOrigin.z - pev->origin.z ) <= 3 ) { PickNewDest( ROACH_SMELL_FOOD ); SetActivity ( ACT_WALK ); } } } break; } case ROACH_SCARED_BY_LIGHT: { // if roach was scared by light, then stop if we're over a spot at least as dark as where we started! if ( GETENTITYILLUM( ENT( pev ) ) <= m_flLastLightLevel ) { SetActivity ( ACT_IDLE ); m_flLastLightLevel = GETENTITYILLUM( ENT ( pev ) );// make this our new light level. } break; } } if ( m_flGroundSpeed != 0 ) { Move( flInterval ); } }
void CFuncTank::TrackTarget(void) { TraceResult tr; edict_t *pPlayer = FIND_CLIENT_IN_PVS(edict()); BOOL updateTime = FALSE, lineOfSight; Vector angles, direction, targetPosition, barrelEnd; edict_t *pTarget = NULL; if (m_pController) { angles = m_pController->pev->v_angle; angles[0] = 0 - angles[0]; pev->nextthink = pev->ltime + 0.05; } else { if (IsActive()) pev->nextthink = pev->ltime + 0.1; else return; if (FNullEnt(pPlayer)) { if (IsActive()) pev->nextthink = pev->ltime + 2; return; } pTarget = FindTarget(pPlayer); if (!pTarget) return; barrelEnd = BarrelPosition(); targetPosition = pTarget->v.origin + pTarget->v.view_ofs; float range = (targetPosition - barrelEnd).Length(); if (!InRange(range)) return; UTIL_TraceLine(barrelEnd, targetPosition, dont_ignore_monsters, edict(), &tr); lineOfSight = FALSE; if (tr.flFraction == 1 || tr.pHit == pTarget) { lineOfSight = TRUE; CBaseEntity *pInstance = CBaseEntity::Instance(pTarget); if (InRange(range) && pInstance && pInstance->IsAlive()) { updateTime = TRUE; m_sightOrigin = UpdateTargetPosition(pInstance); } } direction = m_sightOrigin - pev->origin; angles = UTIL_VecToAngles(direction); AdjustAnglesForBarrel(angles, direction.Length()); } angles.x = -angles.x; angles.y = m_yawCenter + UTIL_AngleDistance(angles.y, m_yawCenter); angles.x = m_pitchCenter + UTIL_AngleDistance(angles.x, m_pitchCenter); if (angles.y > m_yawCenter + m_yawRange) { angles.y = m_yawCenter + m_yawRange; updateTime = FALSE; } else if (angles.y < (m_yawCenter - m_yawRange)) { angles.y = (m_yawCenter - m_yawRange); updateTime = FALSE; } if (updateTime) m_lastSightTime = gpGlobals->time; float distY = UTIL_AngleDistance(angles.y, pev->angles.y); pev->avelocity.y = distY * 10; if (pev->avelocity.y > m_yawRate) pev->avelocity.y = m_yawRate; else if (pev->avelocity.y < -m_yawRate) pev->avelocity.y = -m_yawRate; if (angles.x > m_pitchCenter + m_pitchRange) angles.x = m_pitchCenter + m_pitchRange; else if (angles.x < m_pitchCenter - m_pitchRange) angles.x = m_pitchCenter - m_pitchRange; float distX = UTIL_AngleDistance(angles.x, pev->angles.x); pev->avelocity.x = distX * 10; if (pev->avelocity.x > m_pitchRate) pev->avelocity.x = m_pitchRate; else if (pev->avelocity.x < -m_pitchRate) pev->avelocity.x = -m_pitchRate; if (m_pController) return; if (CanFire() && ((fabs(distX) < m_pitchTolerance && fabs(distY) < m_yawTolerance) || (pev->spawnflags & SF_TANK_LINEOFSIGHT))) { BOOL fire = FALSE; Vector forward; UTIL_MakeVectorsPrivate(pev->angles, forward, NULL, NULL); if (pev->spawnflags & SF_TANK_LINEOFSIGHT) { float length = direction.Length(); UTIL_TraceLine(barrelEnd, barrelEnd + forward * length, dont_ignore_monsters, edict(), &tr); if (tr.pHit == pTarget) fire = TRUE; } else fire = TRUE; if (fire) Fire(BarrelPosition(), forward, pev); else m_fireLast = 0; } else m_fireLast = 0; }
//========================================================= //========================================================= void CBarnacle :: BarnacleThink ( void ) { CBaseEntity *pTouchEnt; CBaseMonster *pVictim; float flLength; SetNextThink( 0.1 ); if ( m_hEnemy != NULL ) { // barnacle has prey. if ( !m_hEnemy->IsAlive() ) { // someone (maybe even the barnacle) killed the prey. Reset barnacle. m_fLiftingPrey = FALSE;// indicate that we're not lifting prey. m_hEnemy = NULL; return; } if ( m_fLiftingPrey ) { if ( m_hEnemy != NULL && m_hEnemy->pev->deadflag != DEAD_NO ) { // crap, someone killed the prey on the way up. m_hEnemy = NULL; m_fLiftingPrey = FALSE; return; } // still pulling prey. Vector vecNewEnemyOrigin = m_hEnemy->pev->origin; vecNewEnemyOrigin.x = pev->origin.x; vecNewEnemyOrigin.y = pev->origin.y; // guess as to where their neck is vecNewEnemyOrigin.x -= 6 * cos(m_hEnemy->pev->angles.y * M_PI/180.0); vecNewEnemyOrigin.y -= 6 * sin(m_hEnemy->pev->angles.y * M_PI/180.0); m_flAltitude -= BARNACLE_PULL_SPEED; vecNewEnemyOrigin.z += BARNACLE_PULL_SPEED; if ( fabs( pev->origin.z - ( vecNewEnemyOrigin.z + m_hEnemy->pev->view_ofs.z - 8 ) ) < BARNACLE_BODY_HEIGHT ) { // prey has just been lifted into position ( if the victim origin + eye height + 8 is higher // than the bottom of the barnacle, it is assumed that the head is within barnacle's body ) m_fLiftingPrey = FALSE; EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_bite3.wav", 1, ATTN_NORM ); pVictim = m_hEnemy->MyMonsterPointer(); m_flKillVictimTime = gpGlobals->time + 10;// now that the victim is in place, the killing bite will be administered in 10 seconds. if ( pVictim ) { pVictim->BarnacleVictimBitten( pev ); SetActivity ( ACT_EAT ); } } UTIL_SetOrigin ( m_hEnemy, vecNewEnemyOrigin ); } else { // prey is lifted fully into feeding position and is dangling there. pVictim = m_hEnemy->MyMonsterPointer(); if ( m_flKillVictimTime != -1 && gpGlobals->time > m_flKillVictimTime ) { // kill! if ( pVictim ) { pVictim->TakeDamage ( pev, pev, pVictim->pev->health, DMG_SLASH | DMG_ALWAYSGIB ); m_cGibs = 3; } return; } // bite prey every once in a while if ( pVictim && ( RANDOM_LONG(0,49) == 0 ) ) { switch ( RANDOM_LONG(0,2) ) { case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew1.wav", 1, ATTN_NORM ); break; case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew2.wav", 1, ATTN_NORM ); break; case 2: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew3.wav", 1, ATTN_NORM ); break; } pVictim->BarnacleVictimBitten( pev ); } } } else { // barnacle has no prey right now, so just idle and check to see if anything is touching the tongue. // If idle and no nearby client, don't think so often if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) && !HaveCamerasInPVS( edict() ) ) SetNextThink( RANDOM_FLOAT(1,1.5) ); // Stagger a bit to keep barnacles from thinking on the same frame if ( m_fSequenceFinished ) {// this is done so barnacle will fidget. SetActivity ( ACT_IDLE ); m_flTongueAdj = -100; } if ( m_cGibs && RANDOM_LONG(0,99) == 1 ) { // cough up a gib. CGib::SpawnRandomGibs( this, 1, 1 ); m_cGibs--; switch ( RANDOM_LONG(0,2) ) { case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew1.wav", 1, ATTN_NORM ); break; case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew2.wav", 1, ATTN_NORM ); break; case 2: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew3.wav", 1, ATTN_NORM ); break; } } pTouchEnt = TongueTouchEnt( &flLength ); if ( pTouchEnt != NULL && m_fTongueExtended ) { // tongue is fully extended, and is touching someone. if ( pTouchEnt->FBecomeProne() ) { EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_alert2.wav", 1, ATTN_NORM ); SetSequenceByName ( "attack1" ); m_flTongueAdj = -20; m_hEnemy = pTouchEnt; pTouchEnt->pev->movetype = MOVETYPE_FLY; pTouchEnt->pev->velocity = pev->velocity; //LRC- make him come _with_ me pTouchEnt->pev->basevelocity = pev->velocity; //LRC pTouchEnt->pev->origin.x = pev->origin.x; pTouchEnt->pev->origin.y = pev->origin.y; m_fLiftingPrey = TRUE;// indicate that we should be lifting prey. m_flKillVictimTime = -1;// set this to a bogus time while the victim is lifted. m_flAltitude = (pev->origin.z - pTouchEnt->EyePosition().z); } } else { // calculate a new length for the tongue to be clear of anything else that moves under it. if ( m_flAltitude < flLength ) { // if tongue is higher than is should be, lower it kind of slowly. m_flAltitude += BARNACLE_PULL_SPEED; m_fTongueExtended = FALSE; } else { m_flAltitude = flLength; m_fTongueExtended = TRUE; } } } // ALERT( at_console, "tounge %f\n", m_flAltitude + m_flTongueAdj ); SetBoneController( 0, -(m_flAltitude + m_flTongueAdj) ); StudioFrameAdvance( 0.1 ); }
void CLeech::SwimThink( void ) { TraceResult tr; float flLeftSide; float flRightSide; float targetSpeed; float targetYaw = 0; CBaseEntity *pTarget; if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) { pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1,1.5); pev->velocity = g_vecZero; return; } else pev->nextthink = gpGlobals->time + 0.1; targetSpeed = LEECH_SWIM_SPEED; if ( m_waterTime < gpGlobals->time ) RecalculateWaterlevel(); if ( m_stateTime < gpGlobals->time ) SwitchLeechState(); ClearConditions( bits_COND_CAN_MELEE_ATTACK1 ); switch( m_MonsterState ) { case MONSTERSTATE_COMBAT: pTarget = m_hEnemy; if ( !pTarget ) SwitchLeechState(); else { // Chase the enemy's eyes m_height = pTarget->pev->origin.z + pTarget->pev->view_ofs.z - 5; // Clip to viable water area if ( m_height < m_bottom ) m_height = m_bottom; else if ( m_height > m_top ) m_height = m_top; Vector location = pTarget->pev->origin - pev->origin; location.z += (pTarget->pev->view_ofs.z); if ( location.Length() < 40 ) SetConditions( bits_COND_CAN_MELEE_ATTACK1 ); // Turn towards target ent targetYaw = UTIL_VecToYaw( location ); targetYaw = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( pev->angles.y ) ); if ( targetYaw < (-LEECH_TURN_RATE*0.75) ) targetYaw = (-LEECH_TURN_RATE*0.75); else if ( targetYaw > (LEECH_TURN_RATE*0.75) ) targetYaw = (LEECH_TURN_RATE*0.75); else targetSpeed *= 2; } break; default: if ( m_zTime < gpGlobals->time ) { float newHeight = RANDOM_FLOAT( m_bottom, m_top ); m_height = 0.5 * m_height + 0.5 * newHeight; m_zTime = gpGlobals->time + RANDOM_FLOAT( 1, 4 ); } if ( RANDOM_LONG( 0, 100 ) < 10 ) targetYaw = RANDOM_LONG( -30, 30 ); pTarget = NULL; // oldorigin test if ( (pev->origin - pev->oldorigin).Length() < 1 ) { // If leech didn't move, there must be something blocking it, so try to turn m_sideTime = 0; } break; } m_obstacle = ObstacleDistance( pTarget ); pev->oldorigin = pev->origin; if ( m_obstacle < 0.1 ) m_obstacle = 0.1; // is the way ahead clear? if ( m_obstacle == 1.0 ) { // if the leech is turning, stop the trend. if ( m_flTurning != 0 ) { m_flTurning = 0; } m_fPathBlocked = FALSE; pev->speed = UTIL_Approach( targetSpeed, pev->speed, LEECH_SWIM_ACCEL * LEECH_FRAMETIME ); pev->velocity = gpGlobals->v_forward * pev->speed; } else { m_obstacle = 1.0 / m_obstacle; // IF we get this far in the function, the leader's path is blocked! m_fPathBlocked = TRUE; if ( m_flTurning == 0 )// something in the way and leech is not already turning to avoid { Vector vecTest; // measure clearance on left and right to pick the best dir to turn vecTest = pev->origin + (gpGlobals->v_right * LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST); UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr); flRightSide = tr.flFraction; vecTest = pev->origin + (gpGlobals->v_right * -LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST); UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr); flLeftSide = tr.flFraction; // turn left, right or random depending on clearance ratio float delta = (flRightSide - flLeftSide); if ( delta > 0.1 || (delta > -0.1 && RANDOM_LONG(0,100)<50) ) m_flTurning = -LEECH_TURN_RATE; else m_flTurning = LEECH_TURN_RATE; } pev->speed = UTIL_Approach( -(LEECH_SWIM_SPEED*0.5), pev->speed, LEECH_SWIM_DECEL * LEECH_FRAMETIME * m_obstacle ); pev->velocity = gpGlobals->v_forward * pev->speed; } pev->ideal_yaw = m_flTurning + targetYaw; UpdateMotion(); }