/* ================== BotLastClientInRankings ================== */ char *BotLastClientInRankings( void ) { int i, worstscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; static int maxclients; playerState_t ps; if ( !maxclients ) { maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" ); } worstscore = 999999; bestclient = 0; for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) { trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) ); //if no config string or no name if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) { continue; } //skip spectators if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) { continue; } // BotAI_GetClientState( i, &ps ); if ( ps.persistant[PERS_SCORE] < worstscore ) { worstscore = ps.persistant[PERS_SCORE]; bestclient = i; } } EasyClientName( bestclient, name, 32 ); return name; }
/* ================== BotIsFirstInRankings ================== */ int BotIsFirstInRankings( bot_state_t *bs ) { int i, score; char buf[MAX_INFO_STRING]; static int maxclients; playerState_t ps; if ( !maxclients ) { maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" ); } score = bs->cur_ps.persistant[PERS_SCORE]; for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) { trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) ); //if no config string or no name if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) { continue; } //skip spectators if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) { continue; } // BotAI_GetClientState( i, &ps ); if ( score < ps.persistant[PERS_SCORE] ) { return qfalse; } } return qtrue; }
/* ======================================================================================================================================= BotIsFirstInRankings ======================================================================================================================================= */ int BotIsFirstInRankings(bot_state_t *bs) { int i, score; char buf[MAX_INFO_STRING]; playerState_t ps; score = bs->cur_ps.persistant[PERS_SCORE]; for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); // if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) { continue; } // skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) { continue; } if (BotAI_GetClientState(i, &ps) && score < ps.persistant[PERS_SCORE]) { return qfalse; } } return qtrue; }
/* ======================================================================================================================================= BotFirstClientInRankings ======================================================================================================================================= */ char *BotFirstClientInRankings(void) { int i, bestscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; playerState_t ps; bestscore = -999999; bestclient = 0; for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); // if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) { continue; } // skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) { continue; } if (BotAI_GetClientState(i, &ps) && ps.persistant[PERS_SCORE] > bestscore) { bestscore = ps.persistant[PERS_SCORE]; bestclient = i; } } EasyClientName(bestclient, name, 32); return name; }
/* ================== BotClientTravelTimeToGoal ================== */ int BotClientTravelTimeToGoal(int client, bot_goal_t *goal) { playerState_t ps; int areanum; BotAI_GetClientState(client, &ps); areanum = BotPointAreaNum(ps.origin); if (!areanum) return 1; return trap_AAS_AreaTravelTimeToGoalArea(areanum, ps.origin, goal->areanum, TFL_DEFAULT); }
/* ============== BotAI ============== */ int BotAI( int client, float thinktime ) { bot_state_t *bs; char buf[1024], *args; int j; trap_EA_ResetInput( client, NULL ); // bs = botstates[client]; if ( !bs || !bs->inuse ) { BotAI_Print( PRT_FATAL, "client %d hasn't been setup\n", client ); return BLERR_AICLIENTNOTSETUP; } //retrieve the current client state BotAI_GetClientState( client, &bs->cur_ps ); //retrieve any waiting console messages while ( trap_BotGetServerCommand( client, buf, sizeof( buf ) ) ) { //have buf point to the command and args to the command arguments args = strchr( buf, ' ' ); if ( !args ) { continue; } *args++ = '\0'; //remove color espace sequences from the arguments Q_CleanStr( args ); //botai_import.Print(PRT_MESSAGE, "ConsoleMessage: \"%s\"\n", buf); if ( !Q_stricmp( buf, "cp " ) ) { /*CenterPrintf*/ } else if ( !Q_stricmp( buf, "cs" ) ) { /*ConfigStringModified*/ } else if ( !Q_stricmp( buf, "print" ) ) { trap_BotQueueConsoleMessage( bs->cs, CMS_NORMAL, args ); } else if ( !Q_stricmp( buf, "chat" ) ) { trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, args ); } else if ( !Q_stricmp( buf, "tchat" ) ) { trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, args ); } else if ( !Q_stricmp( buf, "scores" ) ) { /*FIXME: parse scores?*/ } else if ( !Q_stricmp( buf, "clientLevelShot" ) ) { /*ignore*/ } } //add the delta angles to the bot's current view angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } //increase the local time of the bot bs->ltime += thinktime; // bs->thinktime = thinktime; //origin of the bot VectorCopy( bs->cur_ps.origin, bs->origin ); //eye coordinates of the bot VectorCopy( bs->cur_ps.origin, bs->eye ); bs->eye[2] += bs->cur_ps.viewheight; //get the area the bot is in bs->areanum = BotPointAreaNum( bs->origin ); //the real AI BotDeathmatchAI( bs, thinktime ); //set the weapon selection every AI frame trap_EA_SelectWeapon( bs->client, bs->weaponnum ); //subtract the delta angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } //everything was ok return BLERR_NOERROR; }
/* ============== BotAI ============== */ int BotAI(int client, float thinktime) { bot_state_t *bs; char buf[1024], *args; int j; trap_EA_ResetInput(client); // bs = botstates[client]; if (!bs || !bs->inuse) { BotAI_Print(PRT_FATAL, "BotAI: client %d is not setup\n", client); return qfalse; } //retrieve the current client state BotAI_GetClientState( client, &bs->cur_ps ); //retrieve any waiting server commands while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) { //have buf point to the command and args to the command arguments args = strchr( buf, ' '); if (!args) continue; *args++ = '\0'; //remove color espace sequences from the arguments RemoveColorEscapeSequences( args ); if (!Q_stricmp(buf, "cp ")) { /*CenterPrintf*/ } else if (!Q_stricmp(buf, "cs")) { /*ConfigStringModified*/ } else if (!Q_stricmp(buf, "print")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; trap_BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args); } else if (!Q_stricmp(buf, "chat")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } else if (!Q_stricmp(buf, "tchat")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } #if 1 //def MPACK else if (!Q_stricmp(buf, "vchat")) { BotVoiceChatCommand(bs, SAY_ALL, args); } else if (!Q_stricmp(buf, "vtchat")) { BotVoiceChatCommand(bs, SAY_TEAM, args); } else if (!Q_stricmp(buf, "vtell")) { BotVoiceChatCommand(bs, SAY_TELL, args); } #endif else if (!Q_stricmp(buf, "scores")) { /*FIXME: parse scores?*/ } else if (!Q_stricmp(buf, "clientLevelShot")) { /*ignore*/ } } //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //increase the local time of the bot bs->ltime += thinktime; // bs->thinktime = thinktime; //origin of the bot VectorCopy(bs->cur_ps.origin, bs->origin); //eye coordinates of the bot VectorCopy(bs->cur_ps.origin, bs->eye); bs->eye[2] += bs->cur_ps.viewheight; //get the area the bot is in bs->areanum = BotPointAreaNum(bs->origin); //the real AI BotDeathmatchAI(bs, thinktime); //set the weapon selection every AI frame trap_EA_SelectWeapon(bs->client, bs->weaponnum); //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //everything was ok return qtrue; }
/* ============ AICast_Think entry point for all cast AI ============ */ void AICast_Think( int client, float thinktime ) { gentity_t *ent; cast_state_t *cs; int i; int animIndex; animation_t *anim; // if (saveGamePending || (strlen( g_missionStats.string ) > 2 )) { // return; // } // // get the cast ready for processing // cs = AICast_GetCastState( client ); ent = &g_entities[client]; // // make sure we are using the right AAS data for this entity (one's that don't get set will default to the player's AAS data) trap_AAS_SetCurrentWorld( cs->aasWorldIndex ); // // make sure we have a valid navigation system // if ( !trap_AAS_Initialized() ) { return; } // trap_EA_ResetInput( client, NULL ); cs->aiFlags &= ~AIFL_VIEWLOCKED; //cs->bs->weaponnum = ent->client->ps.weapon; // // turn off flags that are set each frame if needed ent->client->ps.eFlags &= ~( EF_NOSWINGANGLES | EF_MONSTER_EFFECT | EF_MONSTER_EFFECT2 | EF_MONSTER_EFFECT3 ); // conditional flags if ( ent->aiCharacter == AICHAR_ZOMBIE ) { if ( COM_BitCheck( ent->client->ps.weapons, WP_MONSTER_ATTACK1 ) ) { cs->aiFlags |= AIFL_NO_FLAME_DAMAGE; SET_FLAMING_ZOMBIE( ent->s, 1 ); } else { SET_FLAMING_ZOMBIE( ent->s, 0 ); } } // // if we're dead, do special stuff only if ( ent->health <= 0 || cs->revivingTime || cs->rebirthTime ) { // if ( cs->revivingTime && cs->revivingTime < level.time ) { // start us thinking again ent->client->ps.pm_type = PM_NORMAL; cs->revivingTime = 0; } // if ( cs->rebirthTime && cs->rebirthTime < level.time ) { vec3_t mins, maxs; int touch[10], numTouch; float oldmaxZ; oldmaxZ = ent->r.maxs[2]; // make sure the area is clear AIChar_SetBBox( ent, cs ); VectorAdd( ent->r.currentOrigin, ent->r.mins, mins ); VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs ); trap_UnlinkEntity( ent ); numTouch = trap_EntitiesInBox( mins, maxs, touch, 10 ); if ( numTouch ) { for ( i = 0; i < numTouch; i++ ) { //if (!g_entities[touch[i]].client || g_entities[touch[i]].r.contents == CONTENTS_BODY) if ( g_entities[touch[i]].r.contents & MASK_PLAYERSOLID ) { break; } } if ( i == numTouch ) { numTouch = 0; } } if ( numTouch == 0 ) { // ok to spawn // give them health when they start reviving, so we won't gib after // just a couple shots while reviving ent->health = ent->client->ps.stats[STAT_HEALTH] = ent->client->ps.stats[STAT_MAX_HEALTH] = ( ( cs->attributes[STARTING_HEALTH] - 50 ) > 30 ? ( cs->attributes[STARTING_HEALTH] - 50 ) : 30 ); ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->takedamage = qtrue; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; ent->die = AICast_Die; ent->client->ps.eFlags &= ~EF_DEAD; ent->s.eFlags &= ~EF_DEAD; cs->rebirthTime = 0; cs->deathTime = 0; // play the revive animation cs->revivingTime = level.time + BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_REVIVE, qfalse, qtrue );; } else { // can't spawn yet, so set bbox back, and wait ent->r.maxs[2] = oldmaxZ; ent->client->ps.maxs[2] = ent->r.maxs[2]; } trap_LinkEntity( ent ); } // ZOMBIE should set effect flag if really dead if ( cs->aiCharacter == AICHAR_ZOMBIE && !ent->r.contents ) { ent->client->ps.eFlags |= EF_MONSTER_EFFECT2; } // if ( ent->health > GIB_HEALTH && cs->deathTime && cs->deathTime < ( level.time - 3000 ) ) { /* // been dead for long enough, set our animation to the end frame switch ( ent->s.legsAnim & ~ANIM_TOGGLEBIT ) { case BOTH_DEATH1: case BOTH_DEAD1: anim = BOTH_DEAD1; break; case BOTH_DEATH2: case BOTH_DEAD2: anim = BOTH_DEAD2; break; case BOTH_DEATH3: case BOTH_DEAD3: anim = BOTH_DEAD3; break; default: G_Error( "%s has unknown death animation\n", ent->classname); } ent->client->ps.torsoAnim = ( ( ent->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; ent->client->ps.legsAnim = ( ( ent->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; */ cs->deathTime = 0; ent->r.svFlags &= ~SVF_BROADCAST; } // // no more thinking required return; } // // set some anim conditions if ( cs->secondDeadTime ) { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qtrue, qfalse ); } else { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qfalse, qfalse ); } // set health value if ( ent->health <= 0.25 * cs->attributes[STARTING_HEALTH] ) { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 3, qfalse ); } else if ( ent->health <= 0.5 * cs->attributes[STARTING_HEALTH] ) { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 2, qfalse ); } else { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 1, qfalse ); } // cs->speedScale = 1.0; // reset each frame, set if required cs->actionFlags = 0; // FIXME: move this to a Cast AI movement init function! //retrieve the current client state BotAI_GetClientState( client, &( cs->bs->cur_ps ) ); // // setup movement speeds for the given state // walking animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALK ); if ( animIndex >= 0 ) { anim = BG_GetAnimationForIndex( cs->entityNum, animIndex ); cs->attributes[WALKING_SPEED] = anim->moveSpeed; } // crouching animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALKCR ); if ( animIndex >= 0 ) { anim = BG_GetAnimationForIndex( cs->entityNum, animIndex ); cs->attributes[CROUCHING_SPEED] = anim->moveSpeed; } // running animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_RUN ); if ( animIndex >= 0 ) { anim = BG_GetAnimationForIndex( cs->entityNum, animIndex ); cs->attributes[RUNNING_SPEED] = anim->moveSpeed; } // update crouch speed scale ent->client->ps.crouchSpeedScale = cs->attributes[CROUCHING_SPEED] / cs->attributes[RUNNING_SPEED]; // // only enable headlook if we want to this frame ent->client->ps.eFlags &= ~EF_HEADLOOK; if ( cs->bs->enemy >= 0 ) { ent->client->ps.eFlags &= ~EF_STAND_IDLE2; // never use alt idle if fighting } // // check for dead leader if ( cs->leaderNum >= 0 && g_entities[cs->leaderNum].health <= 0 ) { cs->leaderNum = -1; } // #if 0 // HACK for village2, if they are stuck, find a good position (there is a friendly guy placed inside a table) { trace_t tr; vec3_t org; trap_Trace( &tr, cs->bs->cur_ps.origin, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, cs->bs->cur_ps.origin, cs->entityNum, CONTENTS_SOLID ); while ( tr.startsolid ) { VectorCopy( cs->bs->cur_ps.origin, org ); org[0] += 96 * crandom(); org[1] += 96 * crandom(); org[2] += 16 * crandom(); trap_Trace( &tr, org, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, org, cs->entityNum, CONTENTS_SOLID ); G_SetOrigin( &g_entities[cs->entityNum], org ); VectorCopy( org, g_entities[cs->entityNum].client->ps.origin ); } } #endif //add the delta angles to the cast's current view angles for ( i = 0; i < 3; i++ ) { cs->bs->viewangles[i] = AngleMod( cs->bs->viewangles[i] + SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) ); } // //increase the local time of the cast cs->bs->ltime += thinktime; // cs->bs->thinktime = thinktime; //origin of the cast VectorCopy( cs->bs->cur_ps.origin, cs->bs->origin ); //eye coordinates of the cast VectorCopy( cs->bs->cur_ps.origin, cs->bs->eye ); cs->bs->eye[2] += cs->bs->cur_ps.viewheight; //get the area the cast is in cs->bs->areanum = BotPointAreaNum( cs->bs->origin ); // clear flags each frame cs->bs->flags = 0; // // check enemy health if ( cs->bs->enemy >= 0 && g_entities[cs->bs->enemy].health <= 0 ) { cs->bs->enemy = -1; } // // if the previous movetype was temporary, set it back if ( cs->movestateType == MSTYPE_TEMPORARY ) { cs->movestate = MS_DEFAULT; cs->movestateType = MSTYPE_NONE; } // crouching? if ( ( cs->bs->attackcrouch_time > trap_AAS_Time() ) && ( ( cs->lastAttackCrouch > level.time - 500 ) || ( cs->thinkFuncChangeTime < level.time - 1000 ) ) ) { // if we are not moving, and we are firing, always stand, unless we are allowed to crouch + fire if ( VectorLength( cs->bs->cur_ps.velocity ) || ( cs->lastWeaponFired < level.time - 2000 ) || ( cs->aiFlags & AIFL_ATTACK_CROUCH ) ) { cs->lastAttackCrouch = level.time; trap_EA_Crouch( cs->bs->client ); } } // //if (cs->bs->enemy >= 0) { //update the attack inventory values AICast_UpdateBattleInventory( cs, cs->bs->enemy ); //} // // if we don't have ammo for the current weapon, get rid of it if ( !( COM_BitCheck( cs->bs->cur_ps.weapons, cs->bs->weaponnum ) ) || !AICast_GotEnoughAmmoForWeapon( cs, cs->bs->weaponnum ) ) { // select a weapon AICast_ChooseWeapon( cs, qfalse ); // if still no ammo, select a blank weapon //if (!AICast_GotEnoughAmmoForWeapon( cs, cs->bs->weaponnum )) { // cs->bs->weaponnum = WP_NONE; //} } // // in query mode, we do special handling (pause scripting, check for transition to alert/combat, etc) if ( cs->aiState == AISTATE_QUERY ) { AICast_QueryThink( cs ); } else if ( cs->pauseTime < level.time ) { // do the thinking AICast_ProcessAIFunctions( cs, thinktime ); // // make sure the correct weapon is selected trap_EA_SelectWeapon( cs->bs->client, cs->bs->weaponnum ); // // process current script if it exists cs->castScriptStatusCurrent = cs->castScriptStatus; AICast_ScriptRun( cs, qfalse ); } // // set special movestate if necessary if ( cs->movestateType != MSTYPE_NONE ) { switch ( cs->movestate ) { case MS_WALK: cs->actionFlags |= CASTACTION_WALK; break; case MS_CROUCH: trap_EA_Crouch( cs->entityNum ); break; default: break; // TTimo gcc: MS_DEFAULT MS_RUN not handled in switch } } // //subtract the delta angles for ( i = 0; i < 3; i++ ) { cs->bs->viewangles[i] = AngleMod( cs->bs->viewangles[i] - SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) ); } }
/* ============== BotAI ============== */ int BotAI(int client, float thinktime) { bot_state_t *bs; char buf[1024], *args; int j; #ifdef _DEBUG int start = 0; int end = 0; #endif trap_EA_ResetInput(client); // bs = botstates[client]; if (!bs || !bs->inuse) { return qfalse; } //retrieve the current client state BotAI_GetClientState( client, &bs->cur_ps ); //retrieve any waiting server commands while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) { //have buf point to the command and args to the command arguments args = strchr( buf, ' '); if (!args) continue; *args++ = '\0'; //remove color espace sequences from the arguments RemoveColorEscapeSequences( args ); if (!Q_stricmp(buf, "cp ")) { /*CenterPrintf*/ } else if (!Q_stricmp(buf, "cs")) { /*ConfigStringModified*/ } else if (!Q_stricmp(buf, "scores")) { /*FIXME: parse scores?*/ } else if (!Q_stricmp(buf, "clientLevelShot")) { /*ignore*/ } } //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //increase the local time of the bot bs->ltime += thinktime; // bs->thinktime = thinktime; //origin of the bot VectorCopy(bs->cur_ps.origin, bs->origin); //eye coordinates of the bot VectorCopy(bs->cur_ps.origin, bs->eye); bs->eye[2] += bs->cur_ps.viewheight; //get the area the bot is in #ifdef _DEBUG start = trap_Milliseconds(); #endif DOM_StandardBotAI(bs, thinktime); //DOM_StandardBotAI2(bs, thinktime); #ifdef _DEBUG end = trap_Milliseconds(); trap_Cvar_Update(&bot_debugmessages); if (bot_debugmessages.integer) { Com_Printf("Single AI frametime: %i\n", (end - start)); } #endif //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //everything was ok return qtrue; }