//========================================================= // IdleHello // Try to greet player first time he's seen //========================================================= bool CTalkMonster::FIdleHello() { if (!FOkToSpeak()) return false; // if this is first time scientist has seen player, greet him if (!FBitSet(m_bitsSaid, bit_saidHelloPlayer)) { // get a player CBaseEntity *pPlayer = FindNearestFriend(true); if (pPlayer) { if (FInViewCone(pPlayer) && FVisible(pPlayer)) { m_hTalkTarget = pPlayer; if ( GetSpawnFlags().Any( SF_MONSTER_PREDISASTER ) ) PlaySentence( m_szGrp[TLK_PHELLO], RANDOM_FLOAT(3, 3.5), VOL_NORM, ATTN_IDLE ); else PlaySentence( m_szGrp[TLK_HELLO], RANDOM_FLOAT(3, 3.5), VOL_NORM, ATTN_IDLE ); SetBits(m_bitsSaid, bit_saidHelloPlayer); return true; } } } return false; }
//========================================================= // IdleHello // Try to greet player first time he's seen //========================================================= int CMTalkMonster :: FIdleHello( void ) { if (!FOkToSpeak()) return FALSE; // if this is first time scientist has seen player, greet him if (!FBitSet(m_bitsSaid, bit_saidHelloPlayer)) { // get a player edict_t *pPlayer = FindNearestFriend(TRUE); if (pPlayer) { if (UTIL_FInViewCone(pPlayer, this->edict(), m_flFieldOfView) && UTIL_FVisible(pPlayer, this->edict())) { m_hTalkTarget = pPlayer; if (FBitSet(pev->spawnflags, SF_MONSTER_PREDISASTER)) PlaySentence( m_szGrp[TLK_PHELLO], RANDOM_FLOAT(3, 3.5), VOL_NORM, ATTN_IDLE ); else PlaySentence( m_szGrp[TLK_HELLO], RANDOM_FLOAT(3, 3.5), VOL_NORM, ATTN_IDLE ); SetBits(m_bitsSaid, bit_saidHelloPlayer); return TRUE; } } } return FALSE; }
void CScientist :: Scream( void ) { if ( FOkToSpeak() ) { Talk( 10 ); m_hTalkTarget = m_hEnemy; PlaySentence( "SC_SCREAM", RANDOM_FLOAT(3, 6), VOL_NORM, ATTN_NORM ); } }
//========================================================= // ALertSound - otis says "Freeze!" //========================================================= void COtis :: AlertSound( void ) { if ( m_hEnemy != NULL ) { if ( FOkToSpeak() ) PlaySentence( "OT_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); } }
//========================================================= // FIdleStare //========================================================= int CMTalkMonster :: FIdleStare( void ) { if (!FOkToSpeak()) return FALSE; PlaySentence( m_szGrp[TLK_STARE], RANDOM_FLOAT(5, 7.5), VOL_NORM, ATTN_IDLE ); m_hTalkTarget = FindNearestFriend( TRUE ); return TRUE; }
//========================================================= // FIdleStare //========================================================= bool CTalkMonster::FIdleStare() { if (!FOkToSpeak()) return false; PlaySentence( m_szGrp[TLK_STARE], RANDOM_FLOAT(5, 7.5), VOL_NORM, ATTN_IDLE ); m_hTalkTarget = FindNearestFriend( true ); return true; }
void CScientist :: Scream( void ) { if ( FOkToSpeak() ) { Talk( 10 ); m_hTalkTarget = m_hEnemy; if ( FClassnameIs(pev, "monster_rosenberg")) PlaySentence( "RO_SCREAM", RANDOM_FLOAT(3, 6), VOL_NORM, ATTN_NORM ); else PlaySentence( "SC_SCREAM", RANDOM_FLOAT(3, 6), VOL_NORM, ATTN_NORM ); } }
// try to smell something void CTalkMonster :: TrySmellTalk( void ) { if ( !FOkToSpeak() ) return; // clear smell bits periodically if ( gpGlobals->time > m_flLastSaidSmelled ) { // ALERT ( at_aiconsole, "Clear smell bits\n" ); ClearBits(m_bitsSaid, bit_saidSmelled); } // smelled something? if (!FBitSet(m_bitsSaid, bit_saidSmelled) && HasConditions ( bits_COND_SMELL )) { PlaySentence( m_szGrp[TLK_SMELL], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); m_flLastSaidSmelled = gpGlobals->time + 60;// don't talk about the stinky for a while. SetBits(m_bitsSaid, bit_saidSmelled); } }
//========================================================= // FIdleSpeak // ask question of nearby friend, or make statement //========================================================= int CSittingScientist :: FIdleSpeak ( void ) { // try to start a conversation, or make statement int pitch; if (!FOkToSpeak()) return FALSE; // set global min delay for next conversation CTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(4.8, 5.2); pitch = GetVoicePitch(); // if there is a friend nearby to speak to, play sentence, set friend's response time, return // try to talk to any standing or sitting scientists nearby CBaseEntity *pentFriend = FindNearestFriend(FALSE); if (pentFriend && RANDOM_LONG(0,1)) { CTalkMonster *pTalkMonster = GetClassPtr((CTalkMonster *)pentFriend->pev); pTalkMonster->SetAnswerQuestion( this ); IdleHeadTurn(pentFriend->pev->origin); SENTENCEG_PlayRndSz( ENT(pev), m_szGrp[TLK_PQUESTION], 1.0, ATTN_IDLE, 0, pitch ); // set global min delay for next conversation CTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(4.8, 5.2); return TRUE; } // otherwise, play an idle statement if (RANDOM_LONG(0,1)) { SENTENCEG_PlayRndSz( ENT(pev), m_szGrp[TLK_PIDLE], 1.0, ATTN_IDLE, 0, pitch ); // set global min delay for next conversation CTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(4.8, 5.2); return TRUE; } // never spoke CTalkMonster::g_talkWaitTime = 0; return FALSE; }
//========================================================= // ALertSound - barney says "Freeze!" //========================================================= void CFriend :: AlertSound( void ) { if ( m_hEnemy != NULL ) { if ( FOkToSpeak() ) { if (m_iszSpeakAs) { char szBuf[32]; strcpy(szBuf,STRING(m_iszSpeakAs)); strcat(szBuf,"_ATTACK"); PlaySentence( szBuf, RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); } else { PlaySentence( "FG_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); } } } }
bool CTalkMonster::CanPlaySentence( const bool fDisregardState ) const { if ( fDisregardState ) return CBaseMonster::CanPlaySentence( fDisregardState ); return FOkToSpeak(); }
Schedule_t* CTalkMonster :: GetScheduleOfType ( int Type ) { switch( Type ) { case SCHED_MOVE_AWAY: return slMoveAway; case SCHED_MOVE_AWAY_FOLLOW: return slMoveAwayFollow; case SCHED_MOVE_AWAY_FAIL: return slMoveAwayFail; case SCHED_TARGET_FACE: // speak during 'use' if (RANDOM_LONG(0,99) < 2) //ALERT ( at_console, "target chase speak\n" ); return slIdleSpeakWait; else return slIdleStand; case SCHED_IDLE_STAND: { // if never seen player, try to greet him if (!FBitSet(m_bitsSaid, bit_saidHelloPlayer)) { return slIdleHello; } // sustained light wounds? if (!FBitSet(m_bitsSaid, bit_saidWoundLight) && ( GetHealth() <= ( GetMaxHealth() * 0.75))) { //SENTENCEG_PlayRndSz( this, m_szGrp[TLK_WOUND], 1.0, ATTN_IDLE, 0, GetVoicePitch() ); //CTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(2.8, 3.2); PlaySentence( m_szGrp[TLK_WOUND], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); SetBits(m_bitsSaid, bit_saidWoundLight); return slIdleStand; } // sustained heavy wounds? else if (!FBitSet(m_bitsSaid, bit_saidWoundHeavy) && ( GetHealth() <= ( GetMaxHealth() * 0.5))) { //SENTENCEG_PlayRndSz( this, m_szGrp[TLK_MORTAL], 1.0, ATTN_IDLE, 0, GetVoicePitch() ); //CTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(2.8, 3.2); PlaySentence( m_szGrp[TLK_MORTAL], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); SetBits(m_bitsSaid, bit_saidWoundHeavy); return slIdleStand; } // talk about world if (FOkToSpeak() && RANDOM_LONG(0,m_nSpeak * 2) == 0) { //ALERT ( at_console, "standing idle speak\n" ); return slIdleSpeak; } if ( !IsTalking() && HasConditions ( bits_COND_SEE_CLIENT ) && RANDOM_LONG( 0, 6 ) == 0 ) { edict_t *pPlayer = g_engfuncs.pfnPEntityOfEntIndex( 1 ); if ( pPlayer ) { // watch the client. UTIL_MakeVectors ( pPlayer->v.angles ); if ( ( pPlayer->v.origin - GetAbsOrigin() ).Length2D() < TLK_STARE_DIST && UTIL_DotPoints( pPlayer->v.origin, GetAbsOrigin(), gpGlobals->v_forward ) >= m_flFieldOfView ) { // go into the special STARE schedule if the player is close, and looking at me too. return &slTlkIdleWatchClient[ 1 ]; } return slTlkIdleWatchClient; } } else { if (IsTalking()) // look at who we're talking to return slTlkIdleEyecontact; else // regular standing idle return slIdleStand; } // NOTE - caller must first CTalkMonster::GetScheduleOfType, // then check result and decide what to return ie: if sci gets back // slIdleStand, return slIdleSciStand } break; } return CBaseMonster::GetScheduleOfType( Type ); }
//========================================================= // FIdleSpeak // ask question of nearby friend, or make statement //========================================================= bool CTalkMonster::FIdleSpeak() { // try to start a conversation, or make statement const char *szIdleGroup; const char *szQuestionGroup; float duration; if (!FOkToSpeak()) return false; // set idle groups based on pre/post disaster if ( GetSpawnFlags().Any( SF_MONSTER_PREDISASTER ) ) { szIdleGroup = m_szGrp[TLK_PIDLE]; szQuestionGroup = m_szGrp[TLK_PQUESTION]; // set global min delay for next conversation duration = RANDOM_FLOAT(4.8, 5.2); } else { szIdleGroup = m_szGrp[TLK_IDLE]; szQuestionGroup = m_szGrp[TLK_QUESTION]; // set global min delay for next conversation duration = RANDOM_FLOAT(2.8, 3.2); } /*int pitch = */GetVoicePitch(); // player using this entity is alive and wounded? CBaseEntity *pTarget = m_hTargetEnt; if ( pTarget != NULL ) { if ( pTarget->IsPlayer() ) { if ( pTarget->IsAlive() ) { m_hTalkTarget = m_hTargetEnt; if (!FBitSet(m_bitsSaid, bit_saidDamageHeavy) && (m_hTargetEnt->GetHealth() <= m_hTargetEnt->GetMaxHealth() / 8)) { //EMIT_SOUND_DYN( this, CHAN_VOICE, m_szGrp[TLK_PLHURT3], 1.0, ATTN_IDLE, 0, pitch); PlaySentence( m_szGrp[TLK_PLHURT3], duration, VOL_NORM, ATTN_IDLE ); SetBits(m_bitsSaid, bit_saidDamageHeavy); return true; } else if (!FBitSet(m_bitsSaid, bit_saidDamageMedium) && (m_hTargetEnt->GetHealth() <= m_hTargetEnt->GetMaxHealth() / 4)) { //EMIT_SOUND_DYN( this, CHAN_VOICE, m_szGrp[TLK_PLHURT2], 1.0, ATTN_IDLE, 0, pitch); PlaySentence( m_szGrp[TLK_PLHURT2], duration, VOL_NORM, ATTN_IDLE ); SetBits(m_bitsSaid, bit_saidDamageMedium); return true; } else if (!FBitSet(m_bitsSaid, bit_saidDamageLight) && (m_hTargetEnt->GetHealth() <= m_hTargetEnt->GetMaxHealth() / 2)) { //EMIT_SOUND_DYN( this, CHAN_VOICE, m_szGrp[TLK_PLHURT1], 1.0, ATTN_IDLE, 0, pitch); PlaySentence( m_szGrp[TLK_PLHURT1], duration, VOL_NORM, ATTN_IDLE ); SetBits(m_bitsSaid, bit_saidDamageLight); return true; } } else { //!!!KELLY - here's a cool spot to have the talkmonster talk about the dead player if we want. // "Oh dear, Gordon Freeman is dead!" -Scientist // "Damn, I can't do this without you." -Barney } } } { // if there is a friend nearby to speak to, play sentence, set friend's response time, return CBaseEntity *pFriend = FindNearestFriend( false ); if( pFriend && !( pFriend->IsMoving() ) && ( RANDOM_LONG( 0, 99 ) < 75 ) ) { PlaySentence( szQuestionGroup, duration, VOL_NORM, ATTN_IDLE ); //SENTENCEG_PlayRndSz( this, szQuestionGroup, 1.0, ATTN_IDLE, 0, pitch ); // force friend to answer CTalkMonster *pTalkMonster = ( CTalkMonster * ) pFriend; m_hTalkTarget = pFriend; pTalkMonster->SetAnswerQuestion( this ); // UNDONE: This is EVIL!!! pTalkMonster->m_flStopTalkTime = m_flStopTalkTime; m_nSpeak++; return true; } } // otherwise, play an idle statement, try to face client when making a statement. if ( RANDOM_LONG(0,1) ) { //SENTENCEG_PlayRndSz( this, szIdleGroup, 1.0, ATTN_IDLE, 0, pitch ); CBaseEntity *pFriend = FindNearestFriend(true); if ( pFriend ) { m_hTalkTarget = pFriend; PlaySentence( szIdleGroup, duration, VOL_NORM, ATTN_IDLE ); m_nSpeak++; return true; } } // didn't speak Talk( 0 ); CTalkMonster::g_talkWaitTime = 0; return false; }
void CScientist :: StartTask( Task_t *pTask ) { switch( pTask->iTask ) { case TASK_SAY_HEAL: // if ( FOkToSpeak() ) Talk( 2 ); m_hTalkTarget = m_hTargetEnt; PlaySentence( "SC_HEAL", 2, VOL_NORM, ATTN_IDLE ); TaskComplete(); break; case TASK_SCREAM: Scream(); TaskComplete(); break; case TASK_RANDOM_SCREAM: if ( RANDOM_FLOAT( 0, 1 ) < pTask->flData ) Scream(); TaskComplete(); break; case TASK_SAY_FEAR: if ( FOkToSpeak() ) { Talk( 2 ); m_hTalkTarget = m_hEnemy; if ( m_hEnemy->IsPlayer() ) PlaySentence( "SC_PLFEAR", 5, VOL_NORM, ATTN_NORM ); else PlaySentence( "SC_FEAR", 5, VOL_NORM, ATTN_NORM ); } TaskComplete(); break; case TASK_HEAL: m_IdealActivity = ACT_MELEE_ATTACK1; break; case TASK_RUN_PATH_SCARED: m_movementActivity = ACT_RUN_SCARED; break; case TASK_MOVE_TO_TARGET_RANGE_SCARED: { if ( (m_hTargetEnt->pev->origin - pev->origin).Length() < 1 ) TaskComplete(); else { m_vecMoveGoal = m_hTargetEnt->pev->origin; if ( !MoveToTarget( ACT_WALK_SCARED, 0.5 ) ) TaskFail(); } } break; default: CTalkMonster::StartTask( pTask ); break; } }
//========================================================= // GetSchedule - Decides which type of schedule best suits // the monster's current state and conditions. Then calls // monster's member function to get a pointer to a schedule // of the proper type. //========================================================= Schedule_t *CBarney :: GetSchedule ( void ) { if ( HasConditions( bits_COND_HEAR_SOUND ) ) { CSound *pSound; pSound = PBestSound(); ASSERT( pSound != NULL ); if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); } if ( HasConditions( bits_COND_ENEMY_DEAD ) && FOkToSpeak() ) { // Hey, be careful with that if (m_iszSpeakAs) { char szBuf[32]; strcpy(szBuf,STRING(m_iszSpeakAs)); strcat(szBuf,"_KILL"); PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM ); } else { PlaySentence( "BA_KILL", 4, VOL_NORM, ATTN_NORM ); } } switch( m_MonsterState ) { case MONSTERSTATE_COMBAT: { // dead enemy if ( HasConditions( bits_COND_ENEMY_DEAD ) ) { // call base class, all code to handle dead enemies is centralized there. return CBaseMonster :: GetSchedule(); } // always act surprized with a new enemy if ( HasConditions( bits_COND_NEW_ENEMY ) && HasConditions( bits_COND_LIGHT_DAMAGE) ) return GetScheduleOfType( SCHED_SMALL_FLINCH ); // wait for one schedule to draw gun if (!m_fGunDrawn ) return GetScheduleOfType( SCHED_ARM_WEAPON ); if ( HasConditions( bits_COND_HEAVY_DAMAGE ) ) return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); } break; case MONSTERSTATE_ALERT: case MONSTERSTATE_IDLE: if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) { // flinch if hurt return GetScheduleOfType( SCHED_SMALL_FLINCH ); } if ( m_hEnemy == NULL && IsFollowing() ) { if ( !m_hTargetEnt->IsAlive() ) { // UNDONE: Comment about the recently dead player here? StopFollowing( FALSE ); break; } else { if ( HasConditions( bits_COND_CLIENT_PUSH ) ) { return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW ); } return GetScheduleOfType( SCHED_TARGET_FACE ); } } if ( HasConditions( bits_COND_CLIENT_PUSH ) ) { return GetScheduleOfType( SCHED_MOVE_AWAY ); } // try to say something about smells TrySmellTalk(); break; } return CTalkMonster::GetSchedule(); }
int CMTalkMonster::CanPlaySentence( BOOL fDisregardState ) { if ( fDisregardState ) return CMBaseMonster::CanPlaySentence( fDisregardState ); return FOkToSpeak(); }
//========================================================= // GetSchedule - Decides which type of schedule best suits // the monster's current state and conditions. Then calls // monster's member function to get a pointer to a schedule // of the proper type. //========================================================= Schedule_t *CFriend :: GetSchedule ( void ) { if ( HasConditions( bits_COND_HEAR_SOUND ) ) { CSound *pSound; pSound = PBestSound(); ASSERT( pSound != NULL ); if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); } if ( HasConditions( bits_COND_ENEMY_DEAD ) && FOkToSpeak() ) { // Hey, be careful with that if (m_iszSpeakAs) { char szBuf[32]; strcpy(szBuf,STRING(m_iszSpeakAs)); strcat(szBuf,"_KILL"); PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM ); } else { PlaySentence( "FG_KILL", 4, VOL_NORM, ATTN_NORM ); } } switch( m_MonsterState ) { case MONSTERSTATE_COMBAT: { // dead enemy if ( HasConditions( bits_COND_ENEMY_DEAD ) ) { // call base class, all code to handle dead enemies is centralized there. return CBaseMonster :: GetSchedule(); } // always act surprized with a new enemy if ( HasConditions( bits_COND_NEW_ENEMY ) && HasConditions( bits_COND_LIGHT_DAMAGE) ) return GetScheduleOfType( SCHED_SMALL_FLINCH ); // wait for one schedule to draw gun if (!m_fGunDrawn ) return GetScheduleOfType( SCHED_ARM_WEAPON ); if ( HasConditions( bits_COND_HEAVY_DAMAGE ) ) return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); // no ammo else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) ) { //!!!KELLY - this individual just realized he's out of bullet ammo. // He's going to try to find cover to run to and reload, but rarely, if // none is available, he'll drop and reload in the open here. return GetScheduleOfType ( SCHED_GRUNT_COVER_AND_RELOAD ); } //new add //enemy is occluded else if ( HasConditions( bits_COND_ENEMY_OCCLUDED ) ) { if ( HasConditions( bits_COND_CAN_RANGE_ATTACK2 ) ) { //!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc if (FOkToSpeak()) { } return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); } } } break; case MONSTERSTATE_ALERT: case MONSTERSTATE_IDLE: if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) { // flinch if hurt return GetScheduleOfType( SCHED_SMALL_FLINCH ); } if ( m_hEnemy == NULL && IsFollowing() ) { if ( !m_hTargetEnt->IsAlive() ) { // UNDONE: Comment about the recently dead player here? StopFollowing( FALSE ); break; } else { if ( HasConditions( bits_COND_CLIENT_PUSH ) ) { return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW ); } return GetScheduleOfType( SCHED_TARGET_FACE ); } } if ( HasConditions( bits_COND_CLIENT_PUSH ) ) { return GetScheduleOfType( SCHED_MOVE_AWAY ); } // try to say something about smells TrySmellTalk(); break; } return CTalkMonster::GetSchedule(); }