static void Cmd_Follow_f( gentity_t *ent ) { int i; char arg[MAX_TOKEN_CHARS]; if ( trap->Cmd_Argc() != 2 ) { if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { StopFollowing( ent ); } return; } trap->Cmd_Argv( 1, arg, sizeof( arg ) ); i = ClientNumberFromString( ent, arg ); if ( i == -1 ) { return; } // can't follow self if ( &level.clients[ i ] == ent->client ) { return; } // can't follow another spectator if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) { return; } // first set them to spectator if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { SetTeam( ent, "spectator" ); } ent->client->sess.spectatorState = SPECTATOR_FOLLOW; ent->client->sess.spectatorClient = i; }
void CSyncCoreObjectMediator::DisbindConnection() { if( !m_pConn ) return; if(!IsActive()) StopFollowing(); if(IsMoving()) StopMoving(); m_bDisbindingConn = false; GetScene()->DelFromMulticast( m_pConn ); m_fDirKnownMaxSpeed = 0; //cout<<"DisbindConnection DestroyObjForConnection "<<GetGlobalID()<<endl; DestroyObjForConnection( m_pConn ); m_pConn->SetMediator(NULL); AddConnBlockCount(); //关联某个连接,必须最后将m_pConn设置为NULL IntSetConnection(NULL); (new CCoreObjOnConnectionDisbindedResult(GetGlobalID()))->Add(); }
__cdecl void StopFollowingOnDeath( gentity_t *ent ) { if(ent->client->sess.spectatorClient != -1) ent->client->sess.lastFollowedClient = ent->client->sess.spectatorClient; //saving the last followed player StopFollowing(ent); }
void CTalkMonster :: FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { // Don't allow use during a scripted_sentence if ( m_useTime > gpGlobals->time ) return; if ( pCaller != NULL && pCaller->IsPlayer() ) { // Pre-disaster followers can't be used if ( GetSpawnFlags().Any( SF_MONSTER_PREDISASTER ) ) { DeclineFollowing(); } else if ( CanFollow() ) { LimitFollowers( pCaller , 1 ); if ( m_afMemory & bits_MEMORY_PROVOKED ) ALERT( at_console, "I'm not following you, you evil person!\n" ); else { StartFollowing( pCaller ); SetBits(m_bitsSaid, bit_saidHelloPlayer); // Don't say hi after you've started following } } else { StopFollowing( true ); } } }
void CNPCSimpleTalker::FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { // Don't allow use during a scripted_sentence if ( m_useTime > gpGlobals->curtime ) return; if ( pCaller != NULL && pCaller->IsPlayer() ) { if ( !m_FollowBehavior.GetFollowTarget() && IsInterruptable() ) { #if TOML_TODO LimitFollowers( pCaller , 1 ); #endif if ( m_afMemory & bits_MEMORY_PROVOKED ) Msg( "I'm not following you, you evil person!\n" ); else { StartFollowing( pCaller ); } } else { StopFollowing(); } } }
/* LQ3A: Added eType variable. See LQ3A_FOLLOW_TYPE_* definitions for usage. */ void Cmd_Follow_f(gentity_t *ent, lq3a_follow_type_t eType) { int i; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc() != 2 ) { /* LQ3A */ if ((ent->client->sess.spectatorState == SPECTATOR_FOLLOW) || (ent->client->sess.spectatorState == SPECTATOR_FOLLOW_THIRDPERSON)) { StopFollowing( ent ); } return; } trap_Argv( 1, arg, sizeof( arg ) ); i = ClientNumberFromString( ent, arg ); if ( i == -1 ) { return; } // can't follow self if ( &level.clients[ i ] == ent->client ) { return; } // can't follow another spectator if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) { return; } // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_TOURNAMENT ) && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } // first set them to spectator if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { /* LQ3A */ SetTeam(ent, "spectator", qtrue); } /* LQ3A */ switch (eType) { case LQ3A_FOLLOW_TYPE_THIRDPERSON: ent->client->sess.spectatorState = SPECTATOR_FOLLOW_THIRDPERSON; break; // case LQ3A_FOLLOW_AUTO: // case LQ3A_FOLLOW_TYPE_FIRSTPERSON: default: ent->client->sess.spectatorState = SPECTATOR_FOLLOW; break; } ent->client->sess.spectatorClient = i; }
/* =========== ClientDisconnect Called when a player drops from the server. Will not be called between levels. This should NOT be called directly by any game logic, call trap_DropClient(), which will call this and do server system housekeeping. ============ */ void ClientDisconnect( int clientNum ) { gentity_t *ent; gentity_t *tent; int i; // cleanup if we are kicking a bot that // hasn't spawned yet G_RemoveQueuedBotBegin( clientNum ); ent = g_entities + clientNum; if ( !ent->client ) { return; } // stop any following clients for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].sess.sessionTeam == TEAM_SPECTATOR && level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW && level.clients[i].sess.spectatorClient == clientNum ) { StopFollowing( &g_entities[i] ); } } // send effect if they were completely connected if ( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { // They don't get to take powerups with them! // Especially important for stuff like CTF flags TossClientItems( ent ); #ifdef MISSIONPACK TossClientPersistantPowerups( ent ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( ent ); } #endif } G_LogPrintf( "ClientDisconnect: %i\n", clientNum ); trap_UnlinkEntity (ent); ent->s.modelindex = 0; ent->inuse = qfalse; ent->classname = "disconnected"; ent->client->pers.connected = CON_DISCONNECTED; ent->client->ps.stats[STAT_TEAM] = TEAM_FREE; ent->client->sess.sessionTeam = TEAM_FREE; trap_SetConfigstring( CS_PLAYERS + clientNum, ""); CalculateRanks(); if ( ent->r.svFlags & SVF_BOT ) { BotAIShutdownClient( clientNum, qfalse ); } }
int CScientist :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) { if ( pevInflictor && pevInflictor->flags & FL_CLIENT ) { Remember( bits_MEMORY_PROVOKED ); StopFollowing( TRUE ); } // make sure friends talk about it if player hurts scientist... return CTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); }
/** * @brief Update specs for blackout, as needed * @param[in] nTeam * @param[in] fLock */ void G_updateSpecLock(int nTeam, qboolean fLock) { int i; gentity_t *ent; teamInfo[nTeam].spec_lock = fLock; for (i = 0; i < level.numConnectedClients; i++) { ent = g_entities + level.sortedClients[i]; if (ent->client->sess.referee) { continue; } ent->client->sess.spec_invite &= ~nTeam; if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { continue; } if (!fLock) { continue; } #ifdef FEATURE_MULTIVIEW if (ent->client->pers.mvCount > 0) { G_smvRemoveInvalidClients(ent, nTeam); } else #endif if (ent->client->sess.spectatorState == SPECTATOR_FOLLOW) { StopFollowing(ent); ent->client->sess.spec_team &= ~nTeam; } #ifdef FEATURE_MULTIVIEW // ClientBegin sets blackout if (ent->client->pers.mvCount < 1) { #endif SetTeam(ent, "s", qtrue, -1, -1, qfalse); #ifdef FEATURE_MULTIVIEW } #endif } }
int COtis :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) { int ret; // make sure friends talk about it if player hurts talkmonsters... if ( !m_fHostile ) ret = CTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); else ret = CBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); if ( !IsAlive() || pev->deadflag == DEAD_DYING || m_fHostile ) return ret; if ( !m_fHostile ) { if ( m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT) ) { m_flPlayerDamage += flDamage; // This is a heurstic to determine if the player intended to harm me // If I have an enemy, we can't establish intent (may just be crossfire) if ( m_hEnemy == NULL ) { // If the player was facing directly at me, or I'm already suspicious, get mad if ( (m_afMemory & bits_MEMORY_SUSPICIOUS) || IsFacing( pevAttacker, pev->origin ) ) { // Alright, now I'm pissed! PlaySentence( "OT_MAD", 4, VOL_NORM, ATTN_NORM ); Remember( bits_MEMORY_PROVOKED ); StopFollowing( TRUE ); } else { // Hey, be careful with that PlaySentence( "OT_SHOT", 4, VOL_NORM, ATTN_NORM ); Remember( bits_MEMORY_SUSPICIOUS ); } } else if ( !(m_hEnemy->IsPlayer()) && pev->deadflag == DEAD_NO ) PlaySentence( "OT_SHOT", 4, VOL_NORM, ATTN_NORM ); } } return ret; }
bool CSyncCoreObjectMediator::SetConnection( CSynConnServer* pConn ) { if( m_bDisbindingConn ) return false; if( pConn == m_pConn ) return true; if( !IsActive() ) { StopFollowing(); } if( IsMoving() ) StopMoving(); if( pConn ) { if( pConn->GetObjectMediator() ) GenErr( "CSyncCoreObjectMediator::SetConnnection failed, MediatorObject already binded on the connection." ); if( m_pConn ) GenErr( "CSyncCoreObjectMediator::SetConnectino failed, connection already binded." ); IntSetConnection(pConn); m_pConn->SetMediator(this); SetClientMainScene(); m_uAoiBlockCount++; IntPostMsgToSelf(new CAoiDecBlockCountCmd); CSyncCoreObjectServer::SetEyeSight( m_fZeroDimEyeSight,0 ); } else { Ver( SetDisbindConnState() ); IntPostMsgToSelf(new CAoiDisbindConnCmd); } return true; }
/* ================= SpectatorThink ================= */ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) { pmove_t pm; gclient_t *client; client = ent->client; if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) { client->ps.pm_type = PM_SPECTATOR; client->ps.speed = 400; // faster than normal client->ps.basespeed = 400; // set up for pmove memset (&pm, 0, sizeof(pm)); pm.ps = &client->ps; pm.cmd = *ucmd; pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; // spectators can fly through bodies pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; pm.animations = NULL; // perform a pmove Pmove (&pm); // save results of pmove VectorCopy( client->ps.origin, ent->s.origin ); G_TouchTriggers( ent ); trap_UnlinkEntity( ent ); } client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; // attack button cycles through spectators if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) { Cmd_FollowCycle_f( ent, 1 ); } if (client->sess.spectatorState == SPECTATOR_FOLLOW && (ucmd->upmove > 0)) { //jump now removes you from follow mode StopFollowing(ent); } }
/* ================= Cmd_Follow_f ================= */ void Cmd_Follow_f( gentity_t *ent ) { int i; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc() != 2 ) { if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { StopFollowing( ent ); } return; } trap_Argv( 1, arg, sizeof( arg ) ); i = ClientNumberFromString( ent, arg ); if ( i == -1 ) { return; } // can't follow self if ( &level.clients[ i ] == ent->client ) { return; } // can't follow another spectator if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) { return; } // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_TOURNAMENT ) && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } // first set them to spectator if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { SetTeam( ent, "spectator" ); } ent->client->sess.spectatorState = SPECTATOR_FOLLOW; ent->client->sess.spectatorClient = i; }
int CNPC_HL1Barney::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) { // make sure friends talk about it if player hurts talkmonsters... int ret = BaseClass::OnTakeDamage_Alive( inputInfo ); if ( !IsAlive() || m_lifeState == LIFE_DYING ) return ret; if ( m_NPCState != NPC_STATE_PRONE && ( inputInfo.GetAttacker()->GetFlags() & FL_CLIENT ) ) { // This is a heurstic to determine if the player intended to harm me // If I have an enemy, we can't establish intent (may just be crossfire) if ( GetEnemy() == NULL ) { // If the player was facing directly at me, or I'm already suspicious, get mad if ( HasMemory( bits_MEMORY_SUSPICIOUS ) || IsFacing( inputInfo.GetAttacker(), GetAbsOrigin() ) ) { // Alright, now I'm pissed! Speak( BA_MAD ); Remember( bits_MEMORY_PROVOKED ); StopFollowing(); } else { // Hey, be careful with that Speak( BA_SHOT ); Remember( bits_MEMORY_SUSPICIOUS ); } } else if ( !(GetEnemy()->IsPlayer()) && m_lifeState == LIFE_ALIVE ) { Speak( BA_SHOT ); } } return ret; }
void CSyncCoreObjectMediator::IntEndTransferObject() { if( !IsActive() ) StopFollowing(); //这里无需进行StopMoving,让下面的函数通过基类完成这个事情 if( m_pConn ) GetScene()->DelFromMulticast( m_pConn ); CSyncCoreObjectServer::IntEndTransferObject(); m_bDisbindingConn = false; m_fDirKnownMaxSpeed = 0; if( !m_pConn ) return; AddConnBlockCount(); SetClientMainScene(); CSyncCoreObjectServer::SetEyeSight( m_fZeroDimEyeSight, 0 ); }
/* ======================== MoveClientToIntermission When the intermission starts, this will be called for all players. If a New client connects, this will be called after the spawn function. ======================== */ void MoveClientToIntermission( GameEntity *ent ) { // take out of follow mode if needed if ( ent->client_->sess_.spectatorState_ == GameClient::ClientSession::SPECTATOR_FOLLOW ) StopFollowing( ent ); // move to the spot VectorCopy( theLevel.intermission_origin_, ent->s.origin ); VectorCopy( theLevel.intermission_origin_, ent->client_->ps_.origin ); VectorCopy( theLevel.intermission_angle_, ent->client_->ps_.viewangles); ent->client_->ps_.pm_type = PM_INTERMISSION; // clean up objective info ent->client_->ps_.objectives = 0; ent->client_->ps_.eFlags = 0; ent->s.eFlags = 0; ent->s.eType = ET_GENERAL; ent->s.modelindex = 0; ent->s.loopSound = 0; ent->s.event = 0; ent->r.contents = 0; }
/* ======================== MoveClientToIntermission When the intermission starts, this will be called for all players. If a new client connects, this will be called after the spawn function. ======================== */ void MoveClientToIntermission( gentity_t *ent ) { // take out of follow mode if needed if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { StopFollowing( ent ); } FindIntermissionPoint(); // move to the spot VectorCopy( level.intermission_origin, ent->s.origin ); VectorCopy( level.intermission_origin, ent->client->ps.origin ); VectorCopy (level.intermission_angle, ent->client->ps.viewangles); ent->client->ps.pm_type = PM_INTERMISSION; // clean up powerup info memset( ent->client->ps.powerups, 0, sizeof(ent->client->ps.powerups) ); ent->client->ps.eFlags = 0; ent->s.eFlags = 0; ent->s.eType = ET_GENERAL; ent->s.modelindex = 0; ent->s.loopSound = 0; ent->s.event = 0; ent->r.contents = 0; }
//========================================================= // SelectSchedule - 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. //========================================================= int CNPC_HL1Barney::SelectSchedule( void ) { if ( m_NPCState == NPC_STATE_COMBAT || GetEnemy() != NULL ) { // Priority action! if (!m_fGunDrawn ) return SCHED_ARM_WEAPON; } if ( GetFollowTarget() == NULL ) { if ( HasCondition( COND_PLAYER_PUSHING ) && !(GetSpawnFlags() & SF_NPC_PREDISASTER ) ) // Player wants me to move return SCHED_HL1TALKER_FOLLOW_MOVE_AWAY; } if ( BehaviorSelectSchedule() ) return BaseClass::SelectSchedule(); if ( HasCondition( COND_HEAR_DANGER ) ) { CSound *pSound; pSound = GetBestSound(); ASSERT( pSound != NULL ); if ( pSound && pSound->IsSoundType( SOUND_DANGER ) ) return SCHED_TAKE_COVER_FROM_BEST_SOUND; } if ( HasCondition( COND_ENEMY_DEAD ) && IsOkToSpeak() ) { Speak( BA_KILL ); } switch( m_NPCState ) { case NPC_STATE_COMBAT: { // dead enemy if ( HasCondition( COND_ENEMY_DEAD ) ) return BaseClass::SelectSchedule(); // call base class, all code to handle dead enemies is centralized there. // always act surprized with a new enemy if ( HasCondition( COND_NEW_ENEMY ) && HasCondition( COND_LIGHT_DAMAGE) ) return SCHED_SMALL_FLINCH; if ( HasCondition( COND_HEAVY_DAMAGE ) ) return SCHED_TAKE_COVER_FROM_ENEMY; if ( !HasCondition(COND_SEE_ENEMY) ) { // we can't see the enemy if ( !HasCondition(COND_ENEMY_OCCLUDED) ) { // enemy is unseen, but not occluded! // turn to face enemy return SCHED_COMBAT_FACE; } else { return SCHED_CHASE_ENEMY; } } } break; case NPC_STATE_ALERT: case NPC_STATE_IDLE: if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) ) { // flinch if hurt return SCHED_SMALL_FLINCH; } if ( GetEnemy() == NULL && GetFollowTarget() ) { if ( !GetFollowTarget()->IsAlive() ) { // UNDONE: Comment about the recently dead player here? StopFollowing(); break; } else { return SCHED_TARGET_FACE; } } // try to say something about smells TrySmellTalk(); break; } return BaseClass::SelectSchedule(); }
/* ================= SpectatorThink ================= */ void SpectatorThink(gentity_t *ent, usercmd_t *ucmd) { pmove_t pm; gclient_t *client; client = ent->client; if (client->sess.spectatorState != SPECTATOR_FOLLOW) { client->ps.pm_type = PM_SPECTATOR; client->ps.speed = SPECTATOR_SPEED; // was: 400 // faster than normal if (client->ps.sprintExertTime) { client->ps.speed *= 3; // (SA) allow sprint in free-cam mode } // OSP - dead players are frozen too, in a timeout if (client->ps.pm_flags & PMF_LIMBO) { client->ps.pm_type = PM_FREEZE; } else if (client->noclip) { client->ps.pm_type = PM_NOCLIP; } // set up for pmove memset(&pm, 0, sizeof (pm)); pm.ps = &client->ps; pm.pmext = &client->pmext; pm.character = client->pers.character; pm.cmd = *ucmd; pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; // spectators can fly through bodies pm.trace = trap_TraceCapsuleNoEnts; pm.pointcontents = trap_PointContents; Pmove(&pm); // JPW NERVE // Rafael - Activate // Ridah, made it a latched event (occurs on keydown only) if (client->latched_buttons & BUTTON_ACTIVATE) { Cmd_Activate_f(ent); } // save results of pmove VectorCopy(client->ps.origin, ent->s.origin); G_TouchTriggers(ent); trap_UnlinkEntity(ent); } client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; //----(SA) added client->oldwbuttons = client->wbuttons; client->wbuttons = ucmd->wbuttons; // attack button cycles through spectators if ((client->buttons & BUTTON_ATTACK) && !(client->oldbuttons & BUTTON_ATTACK)) { Cmd_FollowCycle_f(ent, 1); } else if ( (client->sess.sessionTeam == TEAM_SPECTATOR) && // don't let dead team players do free fly (client->sess.spectatorState == SPECTATOR_FOLLOW) && (((client->buttons & BUTTON_ACTIVATE) && !(client->oldbuttons & BUTTON_ACTIVATE)) || ucmd->upmove > 0)) { // code moved to StopFollowing StopFollowing(ent); } }
int CBarney :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) { // make sure friends talk about it if player hurts talkmonsters... int ret = CTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); if ( !IsAlive() || pev->deadflag == DEAD_DYING ) return ret; // LRC - if my reaction to the player has been overridden, don't do this stuff if (m_iPlayerReact) return ret; if ( m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT) ) { m_flPlayerDamage += flDamage; // This is a heurstic to determine if the player intended to harm me // If I have an enemy, we can't establish intent (may just be crossfire) if ( m_hEnemy == NULL ) { // If the player was facing directly at me, or I'm already suspicious, get mad if( (m_afMemory & bits_MEMORY_SUSPICIOUS) || UTIL_IsFacing( pevAttacker, GetAbsOrigin() )) { // Alright, now I'm pissed! if (m_iszSpeakAs) { char szBuf[32]; strcpy(szBuf,STRING(m_iszSpeakAs)); strcat(szBuf,"_MAD"); PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM ); } else { PlaySentence( "BA_MAD", 4, VOL_NORM, ATTN_NORM ); } Remember( bits_MEMORY_PROVOKED ); StopFollowing( TRUE ); } else { // Hey, be careful with that if (m_iszSpeakAs) { char szBuf[32]; strcpy(szBuf,STRING(m_iszSpeakAs)); strcat(szBuf,"_SHOT"); PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM ); } else { PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM ); } Remember( bits_MEMORY_SUSPICIOUS ); } } else if ( !(m_hEnemy->IsPlayer()) && pev->deadflag == DEAD_NO ) { if (m_iszSpeakAs) { char szBuf[32]; strcpy(szBuf,STRING(m_iszSpeakAs)); strcat(szBuf,"_SHOT"); PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM ); } else { PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM ); } } } return ret; }
/* ================= SpectatorThink NiceAss: Heavy modifications will be here for AQ2-like spectator mode and zcam!? ================= */ void SpectatorThink(gentity_t * ent, usercmd_t * ucmd) { pmove_t pm; gclient_t *client; int clientNum; client = ent->client; clientNum = client - level.clients; if (client->sess.spectatorState == SPECTATOR_ZCAM) { client->ps.commandTime = ucmd->serverTime; camera_think(ent); } if (client->sess.spectatorState == SPECTATOR_FREE) { if (g_gametype.integer == GT_CTF && level.team_round_going && (client->sess.savedTeam == TEAM_RED || client->sess.savedTeam == TEAM_BLUE)) client->ps.pm_type = PM_FREEZE; else client->ps.pm_type = PM_SPECTATOR; client->ps.speed = 400; // faster than normal // set up for pmove memset(&pm, 0, sizeof(pm)); pm.ps = &client->ps; pm.cmd = *ucmd; pm.tracemask = 0; // spectators can fly through bodies pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; pm.predict = qtrue; // perform a pmove Pmove(&pm); // save results of pmove VectorCopy(client->ps.origin, ent->s.origin); G_TouchTriggers(ent); trap_UnlinkEntity(ent); } // JBravo: Lets not allow bots to use any specmode other than FREE if (ent->r.svFlags & SVF_BOT) return; //Slicer - Changing this for aq2 way // Jump button cycles throught spectators if (client->sess.spectatorState == SPECTATOR_FOLLOW || client->sess.spectatorState == SPECTATOR_ZCAM) { if (ucmd->upmove >= 10) { if (!(client->ps.pm_flags & PMF_JUMP_HELD)) { client->ps.pm_flags |= PMF_JUMP_HELD; if (client->sess.spectatorState == SPECTATOR_ZCAM) { if (client->camera->mode == CAMERA_MODE_SWING) CameraSwingCycle(ent, 1); } else Cmd_FollowCycle_f(ent, 1); } } else client->ps.pm_flags &= ~PMF_JUMP_HELD; } client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; if (g_gametype.integer == GT_CTF && client->sess.spectatorState == SPECTATOR_FREE && (client->sess.savedTeam == TEAM_RED || client->sess.savedTeam == TEAM_BLUE)) return; // Attack Button cycles throught free view, follow or zcam if ((ucmd->buttons & BUTTON_ATTACK) && !(client->oldbuttons & BUTTON_ATTACK)) { if (g_gametype.integer == GT_TEAMPLAY && g_RQ3_limchasecam.integer != 0 && client->sess.referee == 0 ) { if (!OKtoFollow(clientNum)) return; if (client->sess.spectatorState != SPECTATOR_FOLLOW) { client->sess.spectatorState = SPECTATOR_FOLLOW; client->specMode = SPECTATOR_FOLLOW; client->ps.pm_flags |= PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; } return; } else if (client->sess.spectatorState == SPECTATOR_FREE && OKtoFollow(clientNum)) { client->sess.spectatorState = SPECTATOR_ZCAM; client->specMode = SPECTATOR_ZCAM; client->camera->mode = CAMERA_MODE_SWING; client->ps.stats[STAT_RQ3] |= RQ3_ZCAM; client->ps.pm_flags &= ~PMF_FOLLOW; CameraSwingCycle(ent, 1); RQ3_SpectatorMode(ent); } else if (client->sess.spectatorState == SPECTATOR_ZCAM && client->camera->mode == CAMERA_MODE_SWING && OKtoFollow(clientNum)) { client->sess.spectatorState = SPECTATOR_ZCAM; client->specMode = SPECTATOR_ZCAM; client->camera->mode = CAMERA_MODE_FLIC; client->ps.stats[STAT_RQ3] |= RQ3_ZCAM; client->ps.pm_flags &= ~PMF_FOLLOW; CameraFlicBegin(ent); RQ3_SpectatorMode(ent); } else if (client->sess.spectatorState == SPECTATOR_ZCAM && OKtoFollow(clientNum)) { client->sess.spectatorState = SPECTATOR_FOLLOW; client->specMode = SPECTATOR_FOLLOW; client->ps.pm_flags |= PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; Cmd_FollowCycle_f(ent, 1); RQ3_SpectatorMode(ent); } else if (client->sess.spectatorState == SPECTATOR_FOLLOW) { client->sess.spectatorState = SPECTATOR_FREE; client->specMode = SPECTATOR_FREE; client->ps.pm_flags &= ~PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; StopFollowing(ent); RQ3_SpectatorMode(ent); } } }
void CTalkMonster :: StartTask( const Task_t& task ) { switch ( task.iTask ) { case TASK_TLK_SPEAK: // ask question or make statement FIdleSpeak(); TaskComplete(); break; case TASK_TLK_RESPOND: // respond to question IdleRespond(); TaskComplete(); break; case TASK_TLK_HELLO: // greet player FIdleHello(); TaskComplete(); break; case TASK_TLK_STARE: // let the player know I know he's staring at me. FIdleStare(); TaskComplete(); break; case TASK_FACE_PLAYER: case TASK_TLK_LOOK_AT_CLIENT: case TASK_TLK_CLIENT_STARE: // track head to the client for a while. m_flWaitFinished = gpGlobals->time + task.flData; break; case TASK_TLK_EYECONTACT: break; case TASK_TLK_IDEALYAW: if (m_hTalkTarget != NULL) { SetYawSpeed( 60 ); float yaw = VecToYaw(m_hTalkTarget->GetAbsOrigin() - GetAbsOrigin()) - GetAbsAngles().y; if (yaw > 180) yaw -= 360; if (yaw < -180) yaw += 360; if (yaw < 0) { SetIdealYaw( min( yaw + 45, 0.0f ) + GetAbsAngles().y ); } else { SetIdealYaw( max( yaw - 45, 0.0f ) + GetAbsAngles().y ); } } TaskComplete(); break; case TASK_TLK_HEADRESET: // reset head position after looking at something m_hTalkTarget = NULL; TaskComplete(); break; case TASK_TLK_STOPSHOOTING: // tell player to stop shooting PlaySentence( m_szGrp[TLK_NOSHOOT], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_NORM ); TaskComplete(); break; case TASK_CANT_FOLLOW: StopFollowing( false ); PlaySentence( m_szGrp[TLK_STOP], RANDOM_FLOAT(2, 2.5), VOL_NORM, ATTN_NORM ); TaskComplete(); break; case TASK_WALK_PATH_FOR_UNITS: m_movementActivity = ACT_WALK; break; case TASK_MOVE_AWAY_PATH: { Vector dir = GetAbsAngles(); dir.y = GetIdealYaw() + 180; Vector move; UTIL_MakeVectorsPrivate( dir, &move, nullptr, nullptr ); dir = GetAbsOrigin() + move * task.flData; if ( MoveToLocation( ACT_WALK, 2, dir ) ) { TaskComplete(); } else if ( FindCover( GetAbsOrigin(), GetViewOffset(), 0, CoverRadius() ) ) { // then try for plain ole cover m_flMoveWaitFinished = gpGlobals->time + 2; TaskComplete(); } else { // nowhere to go? TaskFail(); } } break; case TASK_PLAY_SCRIPT: m_hTalkTarget = NULL; CBaseMonster::StartTask( task ); break; default: CBaseMonster::StartTask( task ); } }
/* =========== PlayerDisconnect Called when a player drops from the server. Will not be called between levels. This should NOT be called directly by any game logic, call trap_DropPlayer(), which will call this and do server system housekeeping. ============ */ void PlayerDisconnect( int playerNum ) { gentity_t *ent; gentity_t *tent; int i; // cleanup if we are kicking a bot that // hasn't spawned yet G_RemoveQueuedBotBegin( playerNum ); ent = g_entities + playerNum; if (!ent->player || ent->player->pers.connected == CON_DISCONNECTED) { return; } // stop any following players for ( i = 0 ; i < level.maxplayers ; i++ ) { if ( level.players[i].sess.sessionTeam == TEAM_SPECTATOR && level.players[i].sess.spectatorState == SPECTATOR_FOLLOW && level.players[i].sess.spectatorPlayer == playerNum ) { StopFollowing( &g_entities[i] ); } } // send effect if they were completely connected if ( ent->player->pers.connected == CON_CONNECTED && ent->player->sess.sessionTeam != TEAM_SPECTATOR ) { tent = G_TempEntity( ent->player->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.playerNum = ent->s.playerNum; // They don't get to take powerups with them! // Especially important for stuff like CTF flags TossPlayerItems( ent ); #ifdef MISSIONPACK TossPlayerPersistantPowerups( ent ); if( g_gametype.integer == GT_HARVESTER ) { TossPlayerCubes( ent ); } #endif } G_LogPrintf( "PlayerDisconnect: %i\n", playerNum ); // if we are playing in tourney mode and losing, give a win to the other player if ( (g_gametype.integer == GT_TOURNAMENT ) && !level.intermissiontime && !level.warmupTime && level.sortedPlayers[1] == playerNum ) { level.players[ level.sortedPlayers[0] ].sess.wins++; PlayerUserinfoChanged( level.sortedPlayers[0] ); } if( g_gametype.integer == GT_TOURNAMENT && ent->player->sess.sessionTeam == TEAM_FREE && level.intermissiontime ) { trap_Cmd_ExecuteText( EXEC_APPEND, "map_restart 0\n" ); level.restarted = qtrue; level.changemap = NULL; level.intermissiontime = 0; } trap_UnlinkEntity (ent); ent->s.modelindex = 0; ent->inuse = qfalse; ent->classname = "disconnected"; ent->player->pers.connected = CON_DISCONNECTED; ent->player->ps.persistant[PERS_TEAM] = TEAM_FREE; ent->player->sess.sessionTeam = TEAM_FREE; trap_SetConfigstring( CS_PLAYERS + playerNum, ""); CalculateRanks(); if ( ent->r.svFlags & SVF_BOT ) { BotAIShutdownPlayer( playerNum, qfalse ); } // clear player connection info level.connections[ent->player->pers.connectionNum].numLocalPlayers--; level.connections[ent->player->pers.connectionNum].localPlayerNums[ent->player->pers.localPlayerNum] = -1; ent->player->pers.localPlayerNum = ent->player->pers.connectionNum = -1; }
/* =========== ClientDisconnect Called when a player drops from the server. Will not be called between levels. This should NOT be called directly by any game logic, call trap_DropClient(), which will call this and do server system housekeeping. ============ */ void ClientDisconnect( int clientNum ) { gentity_t *ent; gentity_t *tent; int i; // cleanup if we are kicking a bot that // hasn't spawned yet G_RemoveQueuedBotBegin( clientNum ); ent = g_entities + clientNum; if ( !ent->client ) { return; } // stop any following clients for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].sess.sessionTeam == TEAM_SPECTATOR && level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW && level.clients[i].sess.spectatorClient == clientNum ) { StopFollowing( &g_entities[i] ); } } // send effect if they were completely connected if ( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = ent->s.clientNum; // They don't get to take powerups with them! // Especially important for stuff like CTF flags TossClientItems( ent ); #ifdef MISSIONPACK TossClientPersistantPowerups( ent ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( ent ); } #endif } G_LogPrintf( "ClientDisconnect: %i\n", clientNum ); // if we are playing in tourney mode and losing, give a win to the other player if ( (g_gametype.integer == GT_TOURNAMENT ) && !level.intermissiontime && !level.warmupTime && level.sortedClients[1] == clientNum ) { level.clients[ level.sortedClients[0] ].sess.wins++; ClientUserinfoChanged( level.sortedClients[0] ); } trap_UnlinkEntity (ent); ent->s.modelindex = 0; ent->inuse = qfalse; ent->classname = "disconnected"; ent->client->pers.connected = CON_DISCONNECTED; ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE; ent->client->sess.sessionTeam = TEAM_FREE; trap_SetConfigstring( CS_PLAYERS + clientNum, ""); CalculateRanks(); if ( ent->r.svFlags & SVF_BOT ) { BotAIShutdownClient( clientNum, qfalse ); } }
void CMTalkMonster :: StartTask( Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_TLK_SPEAK: // ask question or make statement FIdleSpeak(); TaskComplete(); break; case TASK_TLK_RESPOND: // respond to question IdleRespond(); TaskComplete(); break; case TASK_TLK_HELLO: // greet player FIdleHello(); TaskComplete(); break; case TASK_TLK_STARE: // let the player know I know he's staring at me. FIdleStare(); TaskComplete(); break; case TASK_FACE_PLAYER: case TASK_TLK_LOOK_AT_CLIENT: case TASK_TLK_CLIENT_STARE: // track head to the client for a while. m_flWaitFinished = gpGlobals->time + pTask->flData; break; case TASK_TLK_EYECONTACT: break; case TASK_TLK_IDEALYAW: if (m_hTalkTarget != NULL) { pev->yaw_speed = 60; float yaw = VecToYaw(m_hTalkTarget->v.origin - pev->origin) - pev->angles.y; if (yaw > 180) yaw -= 360; if (yaw < -180) yaw += 360; if (yaw < 0) { pev->ideal_yaw = min( yaw + 45.0f, 0.0f ) + pev->angles.y; } else { pev->ideal_yaw = max( yaw - 45.0f, 0.0f ) + pev->angles.y; } } TaskComplete(); break; case TASK_TLK_HEADRESET: // reset head position after looking at something m_hTalkTarget = NULL; TaskComplete(); break; case TASK_TLK_STOPSHOOTING: // tell player to stop shooting PlaySentence( m_szGrp[TLK_NOSHOOT], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_NORM ); TaskComplete(); break; case TASK_CANT_FOLLOW: StopFollowing( FALSE ); PlaySentence( m_szGrp[TLK_STOP], RANDOM_FLOAT(2, 2.5), VOL_NORM, ATTN_NORM ); TaskComplete(); break; case TASK_WALK_PATH_FOR_UNITS: m_movementActivity = ACT_WALK; break; case TASK_MOVE_AWAY_PATH: { Vector dir = pev->angles; dir.y = pev->ideal_yaw + 180; Vector move; UTIL_MakeVectorsPrivate( dir, move, NULL, NULL ); dir = pev->origin + move * pTask->flData; if ( MoveToLocation( ACT_WALK, 2, dir ) ) { TaskComplete(); } else if ( FindCover( pev->origin, pev->view_ofs, 0, CoverRadius() ) ) { // then try for plain ole cover m_flMoveWaitFinished = gpGlobals->time + 2; TaskComplete(); } else { // nowhere to go? TaskFail(); } } break; case TASK_PLAY_SCRIPT: m_hTalkTarget = NULL; CMBaseMonster::StartTask( pTask ); break; default: CMBaseMonster::StartTask( pTask ); } }
Schedule_t *CScientist :: GetSchedule ( void ) { // so we don't keep calling through the EHANDLE stuff CBaseEntity *pEnemy = m_hEnemy; 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 ); } switch( m_MonsterState ) { case MONSTERSTATE_ALERT: case MONSTERSTATE_IDLE: if ( pEnemy ) { if ( HasConditions( bits_COND_SEE_ENEMY ) ) m_fearTime = gpGlobals->time; else if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert { m_hEnemy = NULL; pEnemy = NULL; } } if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) { // flinch if hurt return GetScheduleOfType( SCHED_SMALL_FLINCH ); } // Cower when you hear something scary if ( HasConditions( bits_COND_HEAR_SOUND ) ) { CSound *pSound; pSound = PBestSound(); ASSERT( pSound != NULL ); if ( pSound ) { if ( pSound->m_iType & (bits_SOUND_DANGER | bits_SOUND_COMBAT) ) { if ( gpGlobals->time - m_fearTime > 3 ) // Only cower every 3 seconds or so { m_fearTime = gpGlobals->time; // Update last fear return GetScheduleOfType( SCHED_STARTLE ); // This will just duck for a second } } } } // Behavior for following the player if ( IsFollowing() ) { if ( !m_hTargetEnt->IsAlive() ) { // UNDONE: Comment about the recently dead player here? StopFollowing( FALSE ); break; } int relationship = R_NO; // Nothing scary, just me and the player if ( pEnemy != NULL ) relationship = IRelationship( pEnemy ); // UNDONE: Model fear properly, fix R_FR and add multiple levels of fear if ( relationship != R_DL && relationship != R_HT ) { // If I'm already close enough to my target if ( TargetDistance() <= 128 ) { if ( CanHeal() ) // Heal opportunistically return slHeal; if ( HasConditions( bits_COND_CLIENT_PUSH ) ) // Player wants me to move return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW ); } return GetScheduleOfType( SCHED_TARGET_FACE ); // Just face and follow. } else // UNDONE: When afraid, scientist won't move out of your way. Keep This? If not, write move away scared { if ( HasConditions( bits_COND_NEW_ENEMY ) ) // I just saw something new and scary, react return GetScheduleOfType( SCHED_FEAR ); // React to something scary return GetScheduleOfType( SCHED_TARGET_FACE_SCARED ); // face and follow, but I'm scared! } } if ( HasConditions( bits_COND_CLIENT_PUSH ) ) // Player wants me to move return GetScheduleOfType( SCHED_MOVE_AWAY ); // try to say something about smells TrySmellTalk(); break; case MONSTERSTATE_COMBAT: if ( HasConditions( bits_COND_NEW_ENEMY ) ) return slFear; // Point and scream! if ( HasConditions( bits_COND_SEE_ENEMY ) ) return slScientistCover; // Take Cover if ( HasConditions( bits_COND_HEAR_SOUND ) ) return slTakeCoverFromBestSound; // Cower and panic from the scary sound! return slScientistCover; // Run & Cower break; } return CTalkMonster::GetSchedule(); }
void CSyncCoreObjectMediator::SetDirectorMaxSpeed(float fDirectorMaxSpeed) { if(fDirectorMaxSpeed < 0) fDirectorMaxSpeed = 0; if(fDirectorMaxSpeed == m_fDirectorMaxSpeed) return; bool bNeedToNotify = m_pConn?true:false; if( fDirectorMaxSpeed == 0) { ++m_uDirBlockCount; //对象要切换到主动工作模式 Ast( !GetMovState() ); #ifdef LOG_COREOBJ_MOVE LogMsg("SetDirectorMaxSpeed 0"); #endif m_queDirMaxSpeed.clear(); StopFollowing(); if( m_pConn ) { //告诉主动对象,我的工作模式改变了 CGas2GacOC_Set_Dir_Max_Speed_Zero Cmd; Cmd.uqbGlobalID=GetGlobalID(); Cmd.PixelPos=GetPixelPos(); m_pConn->SendCoreCmd(&Cmd); bNeedToNotify = false; } } else { #ifdef LOG_COREOBJ_MOVE LogMsg("fDirectorMaxSpeed !0"); #endif if(m_fDirectorMaxSpeed == 0) { //对象要切换到被动工作模式 Ast( !IsFollowing() ); //如果对象此时正在进行主动移动,那么就应该终止移动 StopMoving(); //会间接触发StopTracing() if( m_pConn ) { CGas2GacGC_Tell_Server_Time cmd; cmd.uobGlobalTime = CSyncTimeSystemServer::Inst()->GetFrameTime(); m_pConn->SendCoreCmd(&cmd); //告诉主动对象,我的工作模式改变了 CGas2GacOC_Set_Dir_Max_Speed Cmd; Cmd.uqbGlobalID = GetGlobalID(); Cmd.fDirectorMaxSpeed = fDirectorMaxSpeed; Cmd.uqbSessionID = m_uCurDirMaxSpeedChangeSessionID++; CreateDirMaxSpeedChangeSession(Cmd.uqbSessionID, Cmd.fDirectorMaxSpeed); m_pConn->SendCoreCmd(&Cmd); bNeedToNotify = false; } } AdjustMaxStepNumInOneCheck(fDirectorMaxSpeed); } //cout << fDirectorMaxSpeed << endl; m_fDirectorMaxSpeed = fDirectorMaxSpeed; if(m_pConn) { if(bNeedToNotify) NotifyDirToSetMaxSpeed(); } else { Ast(m_fDirKnownMaxSpeed == 0); } }
MONSTERSTATE CScientist :: GetIdealState ( void ) { switch ( m_MonsterState ) { case MONSTERSTATE_ALERT: case MONSTERSTATE_IDLE: if ( HasConditions( bits_COND_NEW_ENEMY ) ) { if ( IsFollowing() ) { int relationship = IRelationship( m_hEnemy ); if ( relationship != R_FR || relationship != R_HT && !HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) { // Don't go to combat if you're following the player m_IdealMonsterState = MONSTERSTATE_ALERT; return m_IdealMonsterState; } StopFollowing( TRUE ); } } else if ( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) { // Stop following if you take damage if ( IsFollowing() ) StopFollowing( TRUE ); } break; case MONSTERSTATE_COMBAT: { CBaseEntity *pEnemy = m_hEnemy; if ( pEnemy != NULL ) { if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert { // Strip enemy when going to alert m_IdealMonsterState = MONSTERSTATE_ALERT; m_hEnemy = NULL; return m_IdealMonsterState; } // Follow if only scared a little if ( m_hTargetEnt != NULL ) { m_IdealMonsterState = MONSTERSTATE_ALERT; return m_IdealMonsterState; } if ( HasConditions ( bits_COND_SEE_ENEMY ) ) { m_fearTime = gpGlobals->time; m_IdealMonsterState = MONSTERSTATE_COMBAT; return m_IdealMonsterState; } } } break; } return CTalkMonster::GetIdealState(); }
void CCSBot::__MAKE_VHOOK(OnEvent)(GameEventType event, CBaseEntity *entity, CBaseEntity *other) { GetGameState()->OnEvent(event, entity, other); GetChatter()->OnEvent(event, entity, other); // Morale adjustments happen even for dead players switch (event) { case EVENT_TERRORISTS_WIN: if (m_iTeam == CT) { DecreaseMorale(); } else { IncreaseMorale(); } break; case EVENT_CTS_WIN: if (m_iTeam == CT) { IncreaseMorale(); } else { DecreaseMorale(); } break; } if (!IsAlive()) return; CBasePlayer *player = static_cast<CBasePlayer *>(entity); // If we just saw a nearby friend die, and we haven't yet acquired an enemy // automatically acquire our dead friend's killer if (!IsAttacking() && (GetDisposition() == ENGAGE_AND_INVESTIGATE || GetDisposition() == OPPORTUNITY_FIRE)) { if (event == EVENT_PLAYER_DIED) { if (BotRelationship(player) == BOT_TEAMMATE) { CBasePlayer *killer = static_cast<CBasePlayer *>(other); // check that attacker is an enemy (for friendly fire, etc) if (killer != NULL && killer->IsPlayer()) { // check if we saw our friend die - dont check FOV - assume we're aware of our surroundings in combat // snipers stay put if (!IsSniper() && IsVisible(&player->pev->origin)) { // people are dying - we should hurry Hurry(RANDOM_FLOAT(10.0f, 15.0f)); // if we're hiding with only our knife, be a little more cautious const float knifeAmbushChance = 50.0f; if (!IsHiding() || !IsUsingKnife() || RANDOM_FLOAT(0, 100) < knifeAmbushChance) { PrintIfWatched("Attacking our friend's killer!\n"); Attack(killer); return; } } } } } } switch (event) { case EVENT_PLAYER_DIED: { CBasePlayer *victim = player; CBasePlayer *killer = (other != NULL && other->IsPlayer()) ? static_cast<CBasePlayer *>(other) : NULL; // if the human player died in the single player game, tell the team if (CSGameRules()->IsCareer() && !victim->IsBot() && BotRelationship(victim) == BOT_TEAMMATE) { GetChatter()->Say("CommanderDown", 20.0f); } // keep track of the last player we killed if (killer == this) { m_lastVictimID = victim->entindex(); } // react to teammate death if (BotRelationship(victim) == BOT_TEAMMATE) { // chastise friendly fire from humans if (killer != NULL && !killer->IsBot() && BotRelationship(killer) == BOT_TEAMMATE && killer != this) { GetChatter()->KilledFriend(); } if (IsHunting()) { PrintIfWatched("Rethinking hunt due to teammate death\n"); Idle(); return; } if (IsAttacking()) { if (GetTimeSinceLastSawEnemy() > 0.4f) { PrintIfWatched("Rethinking my attack due to teammate death\n"); // allow us to sneak past windows, doors, etc IgnoreEnemies(1.0f); // move to last known position of enemy - this could cause us to flank if // the danger has changed due to our teammate's recent death SetTask(MOVE_TO_LAST_KNOWN_ENEMY_POSITION, GetEnemy()); MoveTo(&GetLastKnownEnemyPosition()); return; } } } // an enemy was killed else { if (killer != NULL && BotRelationship(killer) == BOT_TEAMMATE) { // only chatter about enemy kills if we see them occur, and they were the last one we see if (GetNearbyEnemyCount() <= 1) { // report if number of enemies left is few and we killed the last one we saw locally GetChatter()->EnemiesRemaining(); if (IsVisible(&victim->pev->origin, CHECK_FOV)) { // congratulate teammates on their kills if (killer != this) { float delay = RANDOM_FLOAT(2.0f, 3.0f); if (killer->IsBot()) { if (RANDOM_FLOAT(0.0f, 100.0f) < 40.0f) GetChatter()->Say("NiceShot", 3.0f, delay); } else { // humans get the honorific if (CSGameRules()->IsCareer()) GetChatter()->Say("NiceShotCommander", 3.0f, delay); else GetChatter()->Say("NiceShotSir", 3.0f, delay); } } } } } } return; } case EVENT_TERRORISTS_WIN: if (m_iTeam == TERRORIST) GetChatter()->CelebrateWin(); return; case EVENT_CTS_WIN: if (m_iTeam == CT) GetChatter()->CelebrateWin(); return; case EVENT_BOMB_DEFUSED: if (m_iTeam == CT && TheCSBots()->GetBombTimeLeft() < 2.0) GetChatter()->Say("BarelyDefused"); return; case EVENT_BOMB_PICKED_UP: { if (m_iTeam == CT && player != NULL) { // check if we're close enough to hear it const float bombPickupHearRangeSq = 1000.0f * 1000.0f; if ((pev->origin - player->pev->origin).LengthSquared() < bombPickupHearRangeSq) { GetChatter()->TheyPickedUpTheBomb(); } } return; } case EVENT_BOMB_BEEP: { // if we don't know where the bomb is, but heard it beep, we've discovered it if (GetGameState()->IsPlantedBombLocationKnown() == false) { // check if we're close enough to hear it const float bombBeepHearRangeSq = 1000.0f * 1000.0f; if ((pev->origin - entity->pev->origin).LengthSquared() < bombBeepHearRangeSq) { // radio the news to our team if (m_iTeam == CT && GetGameState()->GetPlantedBombsite() == CSGameState::UNKNOWN) { const CCSBotManager::Zone *zone = TheCSBots()->GetZone(&entity->pev->origin); if (zone != NULL) GetChatter()->FoundPlantedBomb(zone->m_index); } // remember where the bomb is GetGameState()->UpdatePlantedBomb(&entity->pev->origin); } } return; } case EVENT_BOMB_PLANTED: { // if we're a CT, forget what we're doing and go after the bomb if (m_iTeam == CT) { Idle(); } // if we are following someone, stop following if (IsFollowing()) { StopFollowing(); Idle(); } OnEvent(EVENT_BOMB_BEEP, other); return; } case EVENT_BOMB_DEFUSE_ABORTED: PrintIfWatched("BOMB DEFUSE ABORTED\n"); return; case EVENT_WEAPON_FIRED: case EVENT_WEAPON_FIRED_ON_EMPTY: case EVENT_WEAPON_RELOADED: { if (m_enemy == entity && IsUsingKnife()) ForceRun(5.0f); break; } default: break; } // Process radio events from our team if (player != NULL && BotRelationship(player) == BOT_TEAMMATE && event > EVENT_START_RADIO_1 && event < EVENT_END_RADIO) { // TODO: Distinguish between radio commands and responses if (event != EVENT_RADIO_AFFIRMATIVE && event != EVENT_RADIO_NEGATIVE && event != EVENT_RADIO_REPORTING_IN) { m_lastRadioCommand = event; m_lastRadioRecievedTimestamp = gpGlobals->time; m_radioSubject = player; m_radioPosition = player->pev->origin; } } // player_follows needs a player if (player == NULL) return; if (!IsRogue() && event == EVENT_HOSTAGE_CALLED_FOR_HELP && m_iTeam == CT && IsHunting()) { if ((entity->pev->origin - pev->origin).IsLengthGreaterThan(1000.0f)) return; if (IsVisible(&entity->Center())) { m_task = COLLECT_HOSTAGES; m_taskEntity = NULL; Run(); m_goalEntity = entity; MoveTo(&entity->pev->origin, m_hostageEscortCount == 0 ? SAFEST_ROUTE : FASTEST_ROUTE); PrintIfWatched("I'm fetching a hostage that called out to me\n"); return; } } // don't pay attention to noise that friends make if (!IsEnemy(player)) return; float range; PriorityType priority; bool isHostile; if (IsGameEventAudible(event, entity, other, &range, &priority, &isHostile) == false) return; if (event == EVENT_HOSTAGE_USED) { if (m_iTeam == CT) return; if ((entity->pev->origin - pev->origin).IsLengthGreaterThan(range)) return; GetChatter()->HostagesBeingTaken(); if (!GetGameState()->GetNearestVisibleFreeHostage() && m_task != GUARD_HOSTAGE_RESCUE_ZONE && GuardRandomZone()) { m_task = GUARD_HOSTAGE_RESCUE_ZONE; m_taskEntity = NULL; SetDisposition(OPPORTUNITY_FIRE); PrintIfWatched("Trying to beat them to an escape zone!\n"); } } // check if noise is close enough for us to hear const Vector *newNoisePosition = &player->pev->origin; float newNoiseDist = (pev->origin - *newNoisePosition).Length(); if (newNoiseDist < range) { // we heard the sound if ((IsLocalPlayerWatchingMe() && cv_bot_debug.value == 3.0f) || cv_bot_debug.value == 4.0f) { PrintIfWatched("Heard noise (%s from %s, pri %s, time %3.1f)\n", (event == EVENT_WEAPON_FIRED) ? "Weapon fire " : "", STRING(player->pev->netname), (priority == PRIORITY_HIGH) ? "HIGH" : ((priority == PRIORITY_MEDIUM) ? "MEDIUM" : "LOW"), gpGlobals->time); } if (event == EVENT_PLAYER_FOOTSTEP && IsUsingSniperRifle() && newNoiseDist < 300.0) EquipPistol(); // should we pay attention to it // if noise timestamp is zero, there is no prior noise if (m_noiseTimestamp > 0.0f) { // only overwrite recent sound if we are louder (closer), or more important - if old noise was long ago, its faded const float shortTermMemoryTime = 3.0f; if (gpGlobals->time - m_noiseTimestamp < shortTermMemoryTime) { // prior noise is more important - ignore new one if (priority < m_noisePriority) return; float oldNoiseDist = (pev->origin - m_noisePosition).Length(); if (newNoiseDist >= oldNoiseDist) return; } } // find the area in which the noise occured // TODO: Better handle when noise occurs off the nav mesh // TODO: Make sure noise area is not through a wall or ceiling from source of noise // TODO: Change GetNavTravelTime to better deal with NULL destination areas CNavArea *noiseArea = TheNavAreaGrid.GetNavArea(newNoisePosition); if (noiseArea == NULL) noiseArea = TheNavAreaGrid.GetNearestNavArea(newNoisePosition); if (noiseArea == NULL) { PrintIfWatched(" *** Noise occurred off the nav mesh - ignoring!\n"); return; } m_noiseArea = noiseArea; // remember noise priority m_noisePriority = priority; // randomize noise position in the area a bit - hearing isn't very accurate // the closer the noise is, the more accurate our placement // TODO: Make sure not to pick a position on the opposite side of ourselves. const float maxErrorRadius = 400.0f; const float maxHearingRange = 2000.0f; float errorRadius = maxErrorRadius * newNoiseDist / maxHearingRange; m_noisePosition.x = newNoisePosition->x + RANDOM_FLOAT(-errorRadius, errorRadius); m_noisePosition.y = newNoisePosition->y + RANDOM_FLOAT(-errorRadius, errorRadius); // make sure noise position remains in the same area m_noiseArea->GetClosestPointOnArea(&m_noisePosition, &m_noisePosition); m_isNoiseTravelRangeChecked = false; // note when we heard the noise m_noiseTimestamp = gpGlobals->time; } }
//========================================================= // 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(); }