//========================================== // BOT_CreateUserinfo // Creates UserInfo string to connect with //========================================== static void BOT_CreateUserinfo( char *userinfo, size_t userinfo_size, int bot_pers ) { char bot_skin[MAX_INFO_STRING]; char bot_name[MAX_NAME_BYTES]; char bot_model[MAX_INFO_STRING]; //jalfixme: we have only one skin yet //GetUnusedSkin doesn't repeat already used skins/names if( !BOT_GetUnusedSkin( bot_model, bot_skin, bot_name ) ) { float r; int i, botcount = 0; edict_t *ent; //count spawned bots for the names for( i = 0, ent = game.edicts + 1; i < gs.maxclients; i++, ent++ ) { if( !ent->r.inuse || !ent->ai ) continue; if( ent->r.svflags & SVF_FAKECLIENT && AI_GetType( ent->ai ) == AI_ISBOT ) botcount++; } // Set the name for the bot. Q_snprintfz( bot_name, sizeof( bot_name ), "Bot%d", botcount+1 ); // randomly choose skin r = random(); if( r > 0.8f ) Q_snprintfz( bot_model, sizeof( bot_model ), "bigvic" ); else if( r > 0.6f ) Q_snprintfz( bot_model, sizeof( bot_model ), "padpork" ); else if( r > 0.4f ) Q_snprintfz( bot_model, sizeof( bot_model ), "silverclaw" ); else if( r > 0.2f ) Q_snprintfz( bot_model, sizeof( bot_model ), "bobot" ); else Q_snprintfz( bot_model, sizeof( bot_model ), "monada" ); Q_snprintfz( bot_skin, sizeof( bot_skin ), "default" ); } //Q_strncpyz( bot_name, bot_personalities[bot_pers].name, sizeof( bot_name ) ); // initialize userinfo memset( userinfo, 0, userinfo_size ); // add bot's name/skin/hand to userinfo Info_SetValueForKey( userinfo, "name", bot_name ); Info_SetValueForKey( userinfo, "model", bot_model ); //Info_SetValueForKey( userinfo, "skin", bot_skin ); Info_SetValueForKey( userinfo, "skin", "default" ); // JALFIXME Info_SetValueForKey( userinfo, "hand", va( "%i", (int)( random()*2.5 ) ) ); Info_SetValueForKey( userinfo, "color", va( "%i %i %i", (uint8_t)( random()*255 ), (uint8_t)( random()*255 ), (uint8_t)( random()*255 ) ) ); }
/* * G_CheckNumBots */ static void G_CheckNumBots( void ) { edict_t *ent; int desiredNumBots; if( level.spawnedTimeStamp + 5000 > game.realtime ) return; // check sanity of g_numbots if( g_numbots->integer < 0 ) trap_Cvar_Set( "g_numbots", "0" ); if( g_numbots->integer > gs.maxclients ) trap_Cvar_Set( "g_numbots", va( "%i", gs.maxclients ) ); if( level.gametype.numBots > gs.maxclients ) level.gametype.numBots = gs.maxclients; desiredNumBots = level.gametype.numBots ? level.gametype.numBots : g_numbots->integer; if( desiredNumBots < game.numBots ) { // kick one bot for( ent = game.edicts + gs.maxclients; PLAYERNUM( ent ) >= 0; ent-- ) { if( !ent->r.inuse || !( ent->r.svflags & SVF_FAKECLIENT ) ) continue; if( AI_GetType( ent->ai ) == AI_ISBOT ) { trap_DropClient( ent, DROP_TYPE_GENERAL, NULL ); break; } } return; } if( desiredNumBots > game.numBots ) { // add a bot if there is room for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients && game.numBots < desiredNumBots; ent++ ) { if( !ent->r.inuse && trap_GetClientState( PLAYERNUM( ent ) ) == CS_FREE ) BOT_SpawnBot( NULL ); } } }
//========================================== // BOT_RemoveBot // Remove a bot by name or all bots //========================================== void BOT_RemoveBot( const char *name ) { int i; bool freed = false; edict_t *ent; for( i = 0, ent = game.edicts + 1; i < gs.maxclients; i++, ent++ ) { if( !ent->r.inuse || AI_GetType( ent->ai ) != AI_ISBOT ) continue; if( !Q_stricmp( ent->r.client->netname, name ) || !Q_stricmp( name, "all" ) ) { trap_DropClient( ent, DROP_TYPE_GENERAL, NULL ); freed = true; } } if( !freed && Q_stricmp( name, "all" ) ) G_Printf( "BOT: %s not found\n", name ); }
//========================================== // BOT_Respawn // Set up bot for Spawn. Called at first spawn & each respawn //========================================== void BOT_Respawn( edict_t *self ) { if( AI_GetType( self->ai ) != AI_ISBOT ) return; self->enemy = NULL; self->movetarget = NULL; self->pain = BOT_pain; self->ai->statusUpdateTimeout = 0; self->ai->changeweapon_timeout = 0; self->ai->combatmovepush_timeout = 0; self->ai->state_combat_timeout = 0; self->ai->enemyReactionDelay = 0; self->ai->last_attacker = NULL; VectorClear( self->r.client->ps.pmove.delta_angles ); self->r.client->level.last_activity = level.time; AI_ResetNavigation( self ); }
/* * G_ClientThink * Client frame think, and call to execute its usercommands thinking */ void G_ClientThink( edict_t *ent ) { if( !ent || !ent->r.client ) return; if( trap_GetClientState( PLAYERNUM( ent ) ) < CS_SPAWNED ) return; ent->r.client->ps.POVnum = ENTNUM( ent ); // set self // load instashield if( GS_Instagib() && g_instashield->integer ) { if( ent->s.team >= TEAM_PLAYERS && ent->s.team < GS_MAX_TEAMS ) { if( ent->r.client->ps.inventory[POWERUP_SHELL] > 0 ) { ent->r.client->resp.instashieldCharge -= ( game.frametime * 0.001f ) * 60.0f; clamp( ent->r.client->resp.instashieldCharge, 0, INSTA_SHIELD_MAX ); if( ent->r.client->resp.instashieldCharge == 0 ) ent->r.client->ps.inventory[POWERUP_SHELL] = 0; } else { ent->r.client->resp.instashieldCharge += ( game.frametime * 0.001f ) * 20.0f; clamp( ent->r.client->resp.instashieldCharge, 0, INSTA_SHIELD_MAX ); } } } // run bots thinking with the rest of clients if( ent->r.svflags & SVF_FAKECLIENT ) { if( !ent->think && AI_GetType( ent->ai ) == AI_ISBOT ) AI_Think( ent ); } trap_ExecuteClientThinks( PLAYERNUM( ent ) ); }
//========================================== // AI_InitLevel // Inits Map local parameters //========================================== void AI_InitLevel( void ) { edict_t *ent; //Init developer mode bot_showpath = trap_Cvar_Get( "bot_showpath", "0", 0 ); bot_showcombat = trap_Cvar_Get( "bot_showcombat", "0", 0 ); bot_showsrgoal = trap_Cvar_Get( "bot_showsrgoal", "0", 0 ); bot_showlrgoal = trap_Cvar_Get( "bot_showlrgoal", "0", 0 ); bot_dummy = trap_Cvar_Get( "bot_dummy", "0", 0 ); sv_botpersonality = trap_Cvar_Get( "sv_botpersonality", "0", CVAR_ARCHIVE ); nav.debugMode = false; AI_InitNavigationData( false ); // count bots game.numBots = 0; for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) { if( !ent->r.inuse || !ent->ai ) continue; if( ent->r.svflags & SVF_FAKECLIENT && AI_GetType( ent->ai ) == AI_ISBOT ) game.numBots++; } // set up weapon usage weights memset( &AIWeapons, 0, sizeof( ai_weapon_t )*WEAP_TOTAL ); //WEAP_GUNBLADE AIWeapons[WEAP_GUNBLADE].aimType = AI_AIMSTYLE_INSTANTHIT; AIWeapons[WEAP_GUNBLADE].RangeWeight[AIWEAP_LONG_RANGE] = 0.1f; AIWeapons[WEAP_GUNBLADE].RangeWeight[AIWEAP_MEDIUM_RANGE] = 0.2f; AIWeapons[WEAP_GUNBLADE].RangeWeight[AIWEAP_SHORT_RANGE] = 0.3f; AIWeapons[WEAP_GUNBLADE].RangeWeight[AIWEAP_MELEE_RANGE] = 0.4f; //WEAP_MACHINEGUN AIWeapons[WEAP_MACHINEGUN].aimType = AI_AIMSTYLE_INSTANTHIT; AIWeapons[WEAP_MACHINEGUN].RangeWeight[AIWEAP_LONG_RANGE] = 0.8f; AIWeapons[WEAP_MACHINEGUN].RangeWeight[AIWEAP_MEDIUM_RANGE] = 0.7f; AIWeapons[WEAP_MACHINEGUN].RangeWeight[AIWEAP_SHORT_RANGE] = 0.4f; AIWeapons[WEAP_MACHINEGUN].RangeWeight[AIWEAP_MELEE_RANGE] = 0.1f; //WEAP_RIOTGUN AIWeapons[WEAP_RIOTGUN].aimType = AI_AIMSTYLE_INSTANTHIT; AIWeapons[WEAP_RIOTGUN].RangeWeight[AIWEAP_LONG_RANGE] = 0.1f; AIWeapons[WEAP_RIOTGUN].RangeWeight[AIWEAP_MEDIUM_RANGE] = 0.5f; AIWeapons[WEAP_RIOTGUN].RangeWeight[AIWEAP_SHORT_RANGE] = 0.8f; AIWeapons[WEAP_RIOTGUN].RangeWeight[AIWEAP_MELEE_RANGE] = 0.5f; //ROCKETLAUNCHER AIWeapons[WEAP_ROCKETLAUNCHER].aimType = AI_AIMSTYLE_PREDICTION_EXPLOSIVE; AIWeapons[WEAP_ROCKETLAUNCHER].RangeWeight[AIWEAP_LONG_RANGE] = 0.2f; AIWeapons[WEAP_ROCKETLAUNCHER].RangeWeight[AIWEAP_MEDIUM_RANGE] = 0.5f; AIWeapons[WEAP_ROCKETLAUNCHER].RangeWeight[AIWEAP_SHORT_RANGE] = 0.9f; AIWeapons[WEAP_ROCKETLAUNCHER].RangeWeight[AIWEAP_MELEE_RANGE] = 0.6f; //WEAP_GRENADELAUNCHER AIWeapons[WEAP_GRENADELAUNCHER].aimType = AI_AIMSTYLE_DROP; AIWeapons[WEAP_GRENADELAUNCHER].RangeWeight[AIWEAP_LONG_RANGE] = 0.0f; AIWeapons[WEAP_GRENADELAUNCHER].RangeWeight[AIWEAP_MEDIUM_RANGE] = 0.1f; AIWeapons[WEAP_GRENADELAUNCHER].RangeWeight[AIWEAP_SHORT_RANGE] = 0.4f; AIWeapons[WEAP_GRENADELAUNCHER].RangeWeight[AIWEAP_MELEE_RANGE] = 0.3f; //WEAP_PLASMAGUN AIWeapons[WEAP_PLASMAGUN].aimType = AI_AIMSTYLE_PREDICTION; AIWeapons[WEAP_PLASMAGUN].RangeWeight[AIWEAP_LONG_RANGE] = 0.1f; AIWeapons[WEAP_PLASMAGUN].RangeWeight[AIWEAP_MEDIUM_RANGE] = 0.5f; AIWeapons[WEAP_PLASMAGUN].RangeWeight[AIWEAP_SHORT_RANGE] = 0.7f; AIWeapons[WEAP_PLASMAGUN].RangeWeight[AIWEAP_MELEE_RANGE] = 0.4f; //WEAP_ELECTROBOLT AIWeapons[WEAP_ELECTROBOLT].aimType = AI_AIMSTYLE_INSTANTHIT; AIWeapons[WEAP_ELECTROBOLT].RangeWeight[AIWEAP_LONG_RANGE] = 0.9f; AIWeapons[WEAP_ELECTROBOLT].RangeWeight[AIWEAP_MEDIUM_RANGE] = 0.7f; AIWeapons[WEAP_ELECTROBOLT].RangeWeight[AIWEAP_SHORT_RANGE] = 0.4f; AIWeapons[WEAP_ELECTROBOLT].RangeWeight[AIWEAP_MELEE_RANGE] = 0.3f; //WEAP_LASERGUN AIWeapons[WEAP_LASERGUN].aimType = AI_AIMSTYLE_INSTANTHIT; AIWeapons[WEAP_LASERGUN].RangeWeight[AIWEAP_LONG_RANGE] = 0.0f; AIWeapons[WEAP_LASERGUN].RangeWeight[AIWEAP_MEDIUM_RANGE] = 0.0f; AIWeapons[WEAP_LASERGUN].RangeWeight[AIWEAP_SHORT_RANGE] = 0.7f; AIWeapons[WEAP_LASERGUN].RangeWeight[AIWEAP_MELEE_RANGE] = 0.6f; //WEAP_INSTAGUN AIWeapons[WEAP_INSTAGUN].aimType = AI_AIMSTYLE_INSTANTHIT; AIWeapons[WEAP_INSTAGUN].RangeWeight[AIWEAP_LONG_RANGE] = 0.9f; AIWeapons[WEAP_INSTAGUN].RangeWeight[AIWEAP_MEDIUM_RANGE] = 0.9f; AIWeapons[WEAP_INSTAGUN].RangeWeight[AIWEAP_SHORT_RANGE] = 0.9f; AIWeapons[WEAP_INSTAGUN].RangeWeight[AIWEAP_MELEE_RANGE] = 0.9f; }
/* * G_ClientRespawn */ void G_ClientRespawn( edict_t *self, bool ghost ) { int i; edict_t *spawnpoint; vec3_t hull_mins, hull_maxs; vec3_t spawn_origin, spawn_angles; gclient_t *client; int old_team; G_DeathAwards( self ); G_SpawnQueue_RemoveClient( self ); self->r.svflags &= ~SVF_NOCLIENT; //if invalid be spectator if( self->r.client->team < 0 || self->r.client->team >= GS_MAX_TEAMS ) self->r.client->team = TEAM_SPECTATOR; // force ghost always to true when in spectator team if( self->r.client->team == TEAM_SPECTATOR ) ghost = true; old_team = self->s.team; if( self->r.client->teamstate.is_coach ) ghost = true; GClip_UnlinkEntity( self ); client = self->r.client; memset( &client->resp, 0, sizeof( client->resp ) ); memset( &client->ps, 0, sizeof( client->ps ) ); client->resp.timeStamp = level.time; client->ps.playerNum = PLAYERNUM( self ); // clear entity values memset( &self->snap, 0, sizeof( self->snap ) ); memset( &self->s, 0, sizeof( self->s ) ); memset( &self->olds, 0, sizeof( self->olds ) ); memset( &self->invpak, 0, sizeof( self->invpak ) ); self->s.number = self->olds.number = ENTNUM( self ); // relink client struct self->r.client = &game.clients[PLAYERNUM( self )]; // update team self->s.team = client->team; self->deadflag = DEAD_NO; self->s.type = ET_PLAYER; self->groundentity = NULL; self->takedamage = DAMAGE_AIM; self->think = player_think; self->pain = player_pain; self->die = player_die; self->viewheight = playerbox_stand_viewheight; self->r.inuse = true; self->mass = PLAYER_MASS; self->air_finished = level.time + ( 12 * 1000 ); self->r.clipmask = MASK_PLAYERSOLID; self->waterlevel = 0; self->watertype = 0; self->flags &= ~FL_NO_KNOCKBACK; self->r.svflags &= ~SVF_CORPSE; self->enemy = NULL; self->r.owner = NULL; self->max_health = 100; self->health = self->max_health; if( AI_GetType( self->ai ) == AI_ISBOT ) { self->think = NULL; self->classname = "bot"; } else if( self->r.svflags & SVF_FAKECLIENT ) self->classname = "fakeclient"; else self->classname = "player"; VectorCopy( playerbox_stand_mins, self->r.mins ); VectorCopy( playerbox_stand_maxs, self->r.maxs ); VectorClear( self->velocity ); VectorClear( self->avelocity ); VectorCopy( self->r.mins, hull_mins ); VectorCopy( self->r.maxs, hull_maxs ); trap_CM_RoundUpToHullSize( hull_mins, hull_maxs, NULL ); if( self->r.maxs[2] > hull_maxs[2] ) self->viewheight -= (self->r.maxs[2] - hull_maxs[2]); client->ps.POVnum = ENTNUM( self ); // set movement info client->ps.pmove.stats[PM_STAT_MAXSPEED] = (short)DEFAULT_PLAYERSPEED; client->ps.pmove.stats[PM_STAT_JUMPSPEED] = (short)DEFAULT_JUMPSPEED; client->ps.pmove.stats[PM_STAT_DASHSPEED] = (short)DEFAULT_DASHSPEED; if( ghost ) { self->r.solid = SOLID_NOT; self->movetype = MOVETYPE_NOCLIP; if( self->s.team == TEAM_SPECTATOR ) self->r.svflags |= SVF_NOCLIENT; } else { self->r.client->resp.takeStun = true; self->r.solid = SOLID_YES; self->movetype = MOVETYPE_PLAYER; client->ps.pmove.stats[PM_STAT_FEATURES] = static_cast<unsigned short>(PMFEAT_DEFAULT); if( !g_allow_bunny->integer ) client->ps.pmove.stats[PM_STAT_FEATURES] &= ~( PMFEAT_AIRCONTROL|PMFEAT_FWDBUNNY ); } ClientUserinfoChanged( self, client->userinfo ); if( old_team != self->s.team ) G_Teams_UpdateMembersList(); SelectSpawnPoint( self, &spawnpoint, spawn_origin, spawn_angles ); VectorCopy( spawn_origin, client->ps.pmove.origin ); VectorCopy( spawn_origin, self->s.origin ); VectorCopy( self->s.origin, self->s.old_origin ); // set angles self->s.angles[PITCH] = 0; self->s.angles[YAW] = anglemod( spawn_angles[YAW] ); self->s.angles[ROLL] = 0; VectorCopy( self->s.angles, client->ps.viewangles ); // set the delta angle for( i = 0; i < 3; i++ ) client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i]; // don't put spectators in the game if( !ghost ) { if( KillBox( self ) ) { } } self->s.attenuation = ATTN_NORM; self->s.teleported = true; // hold in place briefly client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; client->ps.pmove.pm_time = 14; client->ps.pmove.stats[PM_STAT_NOUSERCONTROL] = CLIENT_RESPAWN_FREEZE_DELAY; client->ps.pmove.stats[PM_STAT_NOAUTOATTACK] = 1000; // set race stats to invisible client->ps.stats[STAT_TIME_SELF] = STAT_NOTSET; client->ps.stats[STAT_TIME_BEST] = STAT_NOTSET; client->ps.stats[STAT_TIME_RECORD] = STAT_NOTSET; client->ps.stats[STAT_TIME_ALPHA] = STAT_NOTSET; client->ps.stats[STAT_TIME_BETA] = STAT_NOTSET; BOT_Respawn( self ); self->r.client->level.respawnCount++; G_UseTargets( spawnpoint, self ); GClip_LinkEntity( self ); // let the gametypes perform their changes if( game.asEngine != NULL ) GT_asCallPlayerRespawn( self, old_team, self->s.team ); else G_Gametype_GENERIC_ClientRespawn( self, old_team, self->s.team ); }