/* =============== G_ParseMapRotationFile Load the map rotations from a map rotation file =============== */ static bool G_ParseMapRotationFile( const char *fileName ) { char *text_p; int i; int len; char *token; char text[ 20000 ]; char mrName[ MAX_QPATH ]; bool mrNameSet = false; fileHandle_t f; // load the file len = trap_FS_FOpenFile( fileName, &f, FS_READ ); if( len <= 0 ) return false; if( len >= sizeof( text ) - 1 ) { G_Printf( S_COLOR_RED "ERROR: map rotation file %s too long\n", fileName ); return false; } trap_FS_Read( text, len, f ); text[ len ] = 0; trap_FS_FCloseFile( f ); // parse the text text_p = text; // read optional parameters while( 1 ) { token = COM_Parse( &text_p ); if( !token ) break; if( !Q_stricmp( token, "" ) ) break; if( !Q_stricmp( token, "{" ) ) { if( mrNameSet ) { //check for name space clashes for( i = 0; i < mapRotations.numRotations; i++ ) { if( !Q_stricmp( mapRotations.rotations[ i ].name, mrName ) ) { G_Printf( S_COLOR_RED "ERROR: a map rotation is already named %s\n", mrName ); return false; } } Q_strncpyz( mapRotations.rotations[ mapRotations.numRotations ].name, mrName, MAX_QPATH ); if( !G_ParseMapRotation( &mapRotations.rotations[ mapRotations.numRotations ], &text_p ) ) { G_Printf( S_COLOR_RED "ERROR: %s: failed to parse map rotation %s\n", fileName, mrName ); return false; } //start parsing particle systems again mrNameSet = false; if( mapRotations.numRotations == MAX_MAP_ROTATIONS ) { G_Printf( S_COLOR_RED "ERROR: maximum number of map rotations (%d) reached\n", MAX_MAP_ROTATIONS ); return false; } else mapRotations.numRotations++; continue; } else { G_Printf( S_COLOR_RED "ERROR: unamed map rotation\n" ); return false; } } if( !mrNameSet ) { Q_strncpyz( mrName, token, sizeof( mrName ) ); mrNameSet = true; } else { G_Printf( S_COLOR_RED "ERROR: map rotation already named\n" ); return false; } } return true; }
/* =================== Svcmd_EntityShow_f =================== */ void Svcmd_EntityShow_f( void ) { int entityNum; int lastTargetIndex, targetIndex; gentity_t *selection; gentity_t *possibleTarget = NULL; char argument[ 6 ]; if (trap_Argc() != 2) { G_Printf("usage: entityShow <entityId>\n"); return; } trap_Argv( 1, argument, sizeof( argument ) ); entityNum = atoi( argument ); if (entityNum >= level.num_entities || entityNum < MAX_CLIENTS) { G_Printf("entityId %d is out of range\n", entityNum); return; } selection = &g_entities[entityNum]; if (!selection->inuse) { G_Printf("entity slot %d is unused/free\n", entityNum); return; } G_Printf( "⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼\n" ); G_Printf( S_COLOR_CYAN "#%3i" S_COLOR_WHITE ": %16s", entityNum, Com_EntityTypeName( selection->s.eType ) ); if (IS_NON_NULL_VEC3(selection->s.origin)) { G_Printf("%26s", vtos( selection->s.origin ) ); } G_Printf( "\n⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼\n" ); G_Printf( "Classname: " S_COLOR_CYAN "%s" S_COLOR_WHITE "\n", selection->classname ); G_Printf( "Capabilities:%s%s%s%s%s%s%s\n\n", selection->act ? " acts" : "", selection->think ? " thinks" : "", selection->pain ? " pains" : "", selection->die ? " dies" : "", selection->reset ? " resets" : "", selection->touch ? " touchable" : "", selection->use ? " usable" : ""); if (selection->names[0]) { G_Printf( "Names: "); G_PrintEntityNameList( selection ); } G_Printf("State: %s\n", selection->enabled ? "enabled" : "disabled"); if (selection->groupName) { G_Printf("Member of Group: %s%s\n", selection->groupName, !selection->groupMaster ? " [master]" : ""); } G_Printf( "\n"); if(selection->targetCount) { G_Printf( "Aims at\n"); while ((possibleTarget = G_IterateTargets(possibleTarget, &targetIndex, selection)) != NULL ) { G_Printf(" • %s %s\n", etos( possibleTarget ), vtos( possibleTarget->s.origin)); } G_Printf( "\n"); } if(selection->callTargetCount) { lastTargetIndex = -1; while ((possibleTarget = G_IterateCallEndpoints(possibleTarget, &targetIndex, selection)) != NULL ) { if(lastTargetIndex != targetIndex) { G_Printf("Calls %s \"%s:%s\"\n", selection->calltargets[ targetIndex ].event ? selection->calltargets[ targetIndex ].event : "onUnknown", selection->calltargets[ targetIndex ].name, selection->calltargets[ targetIndex ].action ? selection->calltargets[ targetIndex ].action : "default"); lastTargetIndex = targetIndex; } G_Printf(" • %s", etos(possibleTarget)); if(possibleTarget->names[1]) { G_Printf(" using \"%s\" ∈ ", selection->calltargets[ targetIndex ].name); G_PrintEntityNameList( possibleTarget ); } G_Printf("\n"); } } G_Printf( "\n" ); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; char *key; char *s; char *botname; char *model; char *headmodel; char userinfo[MAX_INFO_STRING]; // have the server allocate a client slot clientNum = trap->BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); trap->BotFreeClient( clientNum ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "visor/default"; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap->Print( S_COLOR_RED "Error: bot has no aifile specified\n" ); trap->BotFreeClient( clientNum ); return; } Info_SetValueForKey( userinfo, "characterfile", s ); if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "team", team ); // register the userinfo trap->SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, true, true ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* ================= G_ScriptAction_PlayAnim syntax: playanim <startframe> <endframe> [looping <FOREVER/duration>] [rate <FPS>] NOTE: all source animations must be at 20fps ================= */ qboolean G_ScriptAction_PlayAnim( gentity_t *ent, const char* params ) { const char* pString; char* token; char tokens[2][MAX_QPATH]; int i, endtime = 0; // TTimo: init qboolean looping = qfalse, forever = qfalse; int startframe, endframe, idealframe; int rate = 20; if ( ( ent->scriptStatus.scriptFlags & SCFL_ANIMATING ) && ( ent->scriptStatus.scriptStackChangeTime == level.time ) ) { // this is a new call, so cancel the previous animation ent->scriptStatus.scriptFlags &= ~SCFL_ANIMATING; } pString = params; for ( i = 0; i < 2; i++ ) { token = COM_ParseExt( &pString, qfalse ); if ( !token || !token[0] ) { G_Printf( "G_Scripting: syntax error\n\nplayanim <startframe> <endframe> [LOOPING <duration>]\n" ); return qtrue; } else { Q_strncpyz( tokens[i], token, sizeof( tokens[i] ) ); } } startframe = atoi( tokens[0] ); endframe = atoi( tokens[1] ); // check for optional parameters token = COM_ParseExt( &pString, qfalse ); if ( token[0] ) { if ( !Q_strcasecmp( token, "looping" ) ) { looping = qtrue; token = COM_ParseExt( &pString, qfalse ); if ( !token || !token[0] ) { G_Printf( "G_Scripting: syntax error\n\nplayanim <startframe> <endframe> [LOOPING <duration>]\n" ); return qtrue; } if ( !Q_strcasecmp( token, "untilreachmarker" ) ) { if ( level.time < ent->s.pos.trTime + ent->s.pos.trDuration ) { endtime = level.time + 100; } else { endtime = 0; } } else if ( !Q_strcasecmp( token, "forever" ) ) { ent->scriptStatus.animatingParams = params; ent->scriptStatus.scriptFlags |= SCFL_ANIMATING; endtime = level.time + 100; // we don't care when it ends, since we are going forever! forever = qtrue; } else { endtime = ent->scriptStatus.scriptStackChangeTime + atoi( token ); } token = COM_ParseExt( &pString, qfalse ); } if ( token[0] && !Q_strcasecmp( token, "rate" ) ) { token = COM_ParseExt( &pString, qfalse ); if ( !token[0] ) { G_Error( "G_Scripting: playanim has RATE parameter without an actual rate specified" ); } rate = atoi( token ); } if ( !looping ) { endtime = ent->scriptStatus.scriptStackChangeTime + ( ( endframe - startframe ) * ( 1000 / 20 ) ); } } idealframe = startframe + (int)c::floor( (float)( level.time - ent->scriptStatus.scriptStackChangeTime ) / ( 1000.0 / (float)rate ) ); if ( looping ) { ent->s.frame = startframe + ( idealframe - startframe ) % ( endframe - startframe ); ent->s.eFlags |= EF_MOVER_ANIMATE; } else { if ( idealframe > endframe ) { ent->s.frame = endframe; ent->s.eFlags &= ~EF_MOVER_ANIMATE; // stop interpolation, since we have gone passed the endframe } else { ent->s.frame = idealframe; ent->s.eFlags |= EF_MOVER_ANIMATE; } } if ( forever ) { ent->s.eFlags |= EF_MOVER_ANIMATE; return qtrue; // continue to the next command } if ( endtime <= level.time ) { ent->s.eFlags &= ~EF_MOVER_ANIMATE; // stop animating return qtrue; } else { return qfalse; } }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; // char *headmodel; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); /* if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } */ key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "visor/default"; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); /* key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); */ key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "personality"); if (!*s ) { Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" ); } else { Info_SetValueForKey( userinfo, "personality", s ); } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { // G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); // G_Printf( S_COLOR_RED "Start server with more 'open' slots.\n" ); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "UNABLE_TO_ADD_BOT"))); return; } // initialize the bot settings if( !team || !*team || ( Q_stricmp(team, "red") && Q_stricmp(team, "blue") ) ) { team = ""; } // Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ void ClientUserinfoChanged(int clientNum) { gentity_t *ent; char *s; char oldname[MAX_STRING_CHARS]; char userinfo[MAX_INFO_STRING]; gclient_t *client; char *ip = NULL; // Nico, used to store client ip char *rate = NULL; // Nico, used to store client rate char *snaps = NULL; // Nico, used to store client snaps char *name = NULL; // Nico, used to store client name char oldAuthToken[MAX_QPATH]; // Nico, used to see if auth token was changed ent = g_entities + clientNum; client = ent->client; client->ps.clientNum = clientNum; // Nico, flood protection if (ClientIsFlooding(ent)) { G_LogPrintf("Dropping client %d: flooded userinfo\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of flooded userinfo", 0); return; } trap_GetUserinfo(clientNum, userinfo, sizeof (userinfo)); // Nico, perform security checks on userinfo string if (!checkUserinfoString(clientNum, userinfo)) { return; } if (g_developer.integer || *g_log.string || g_dedicated.integer) { G_Printf("Userinfo: %s\n", userinfo); } // check for local client ip = Info_ValueForKey(userinfo, "ip"); Q_strncpyz(client->pers.ip, ip, IP_MAX_LENGTH); if (ip && !strcmp(ip, "localhost")) { client->pers.localClient = qtrue; level.fLocalHost = qtrue; client->sess.referee = RL_REFEREE; } // Nico, store rate and snaps rate = Info_ValueForKey(userinfo, "rate"); client->pers.rate = atoi(rate); snaps = Info_ValueForKey(userinfo, "snaps"); client->pers.snaps = atoi(snaps); // Nico, backup old auth token Q_strncpyz(oldAuthToken, client->pers.authToken, sizeof (oldAuthToken)); s = Info_ValueForKey(userinfo, "cg_uinfo"); sscanf(s, "%i %i %i %i %s %i %i %i %i %i %i %i %i %i", &client->pers.clientFlags, &client->pers.clientTimeNudge, &client->pers.clientMaxPackets, // Nico, max FPS &client->pers.maxFPS, // Nico, auth Token (char *)&client->pers.authToken, // Nico, load view angles on load &client->pers.loadViewAngles, // Nico, load weapon on load &client->pers.loadWeapon, // Nico, load position when player dies &client->pers.autoLoad, // Nico, cgaz &client->pers.cgaz, // Nico, hideme &client->pers.hideme, // Nico, client auto demo record setting &client->pers.autoDemo, // Nico, automatically load checkpoints &client->pers.autoLoadCheckpoints, // Nico, persistant specLock (int *)&client->sess.specLocked, // Nico, keep all demos &client->pers.keepAllDemos ); // Nico, check if auth token was changed if (oldAuthToken[0] != '\0' && Q_stricmp(oldAuthToken, client->pers.authToken) && client->sess.logged) { // Nico, auth token was changed => logout player if he was logged in CP("cp \"You are no longer logged in!\n\""); G_LogPrintf("ClientUserinfoChanged: authToken changed for client %d, forcing logout\n", clientNum); ent->client->sess.logged = qfalse; } client->pers.autoActivate = (client->pers.clientFlags & CGF_AUTOACTIVATE) ? PICKUP_TOUCH : PICKUP_ACTIVATE; client->pers.predictItemPickup = ((client->pers.clientFlags & CGF_PREDICTITEMS) != 0); if (client->pers.clientFlags & CGF_AUTORELOAD) { client->pers.bAutoReloadAux = qtrue; client->pmext.bAutoReload = qtrue; } else { client->pers.bAutoReloadAux = qfalse; client->pmext.bAutoReload = qfalse; } // Nico, pmove_fixed client->pers.pmoveFixed = client->pers.clientFlags & CGF_PMOVEFIXED; // Nico, autologin client->pers.autoLogin = client->pers.clientFlags & CGF_AUTOLOGIN; // // Nico, name handling // // Backup old name Q_strncpyz(oldname, client->pers.netname, sizeof (oldname)); // Get new name from userinfo string name = Info_ValueForKey(userinfo, "name"); // Clean the new name ClientCleanName(name, client->pers.netname, sizeof (client->pers.netname)); // Check it's valid if (CheckName(client->pers.netname) != qtrue) { // Invalid name, restore old name Q_strncpyz(client->pers.netname, oldname, sizeof (client->pers.netname)); Info_SetValueForKey(userinfo, "name", oldname); trap_SetUserinfo(clientNum, userinfo); CPx(clientNum, "print \"^1Invalid name, name change refused\n\""); G_LogPrintf("Client %d name change refused\n", clientNum); } else { // Name is valid // Now, check if name was changed or not if (client->pers.connected == CON_CONNECTED && strcmp(oldname, client->pers.netname) != 0) { // Name was changed // Now, check name changes limit if (g_maxNameChanges.integer > -1 && client->pers.nameChanges >= g_maxNameChanges.integer) { // Nico, limit reached, forbid name change Q_strncpyz(client->pers.netname, oldname, sizeof (client->pers.netname)); Info_SetValueForKey(userinfo, "name", oldname); trap_SetUserinfo(clientNum, userinfo); CPx(clientNum, "print \"^1You had too many namechanges\n\""); G_LogPrintf("Client %d name change refused\n", clientNum); return; } client->pers.nameChanges++; trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname)); } } // // Nico, end of name handling // client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // To communicate it to cgame client->ps.stats[STAT_PLAYER_CLASS] = client->sess.playerType; // Gordon: Not needed any more as it's in clientinfo? // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds s = va("n\\%s\\t\\%i\\c\\%i\\w\\%i\\lw\\%i\\sw\\%i\\mu\\%i\\ref\\%i\\pm\\%i\\l\\%i\\h\\%i\\cc\\%i", client->pers.netname, client->sess.sessionTeam, client->sess.playerType, client->sess.playerWeapon, client->sess.latchPlayerWeapon, client->sess.latchPlayerWeapon2, client->sess.muted ? 1 : 0, client->sess.referee, client->pers.pmoveFixed ? 1 : 0, // Nico, pmove_fixed client->sess.logged ? 1 : 0, // Nico, login status client->pers.hideme, // Nico, hideme client->sess.countryCode// Nico, country code (GeoIP) ); trap_GetConfigstring(CS_PLAYERS + clientNum, oldname, sizeof (oldname)); trap_SetConfigstring(CS_PLAYERS + clientNum, s); if (!Q_stricmp(oldname, s)) { return; } G_LogPrintf("ClientUserinfoChanged: %i %s\n", clientNum, s); G_DPrintf("ClientUserinfoChanged: %i :: %s\n", clientNum, s); }
static gentity_t *SpawnModelOnVictoryPad( gentity_t *pad, vec3_t offset, gentity_t *ent, int place ) { gentity_t *body; vec3_t vec; vec3_t f, r, u; body = G_Spawn(); if ( !body ) { G_Printf( S_COLOR_RED "ERROR: out of gentities\n" ); return NULL; } body->classname = ent->client->pers.netname; body->client = ent->client; body->s = ent->s; body->s.eType = ET_PLAYER; // could be ET_INVISIBLE body->s.eFlags = 0; // clear EF_TALK, etc body->s.powerups = 0; // clear powerups body->s.loopSound = 0; // clear lava burning body->s.number = body - g_entities; body->timestamp = level.time; body->physicsObject = qtrue; body->physicsBounce = 0; // don't bounce body->s.event = 0; body->s.pos.trType = TR_STATIONARY; body->s.groundEntityNum = ENTITYNUM_WORLD; body->s.legsAnim = LEGS_IDLE; body->s.torsoAnim = TORSO_STAND; if( body->s.weapon == WP_NONE ) { body->s.weapon = WP_MACHINEGUN; } if( body->s.weapon == WP_GAUNTLET) { body->s.torsoAnim = TORSO_STAND2; } body->s.event = 0; body->r.svFlags = ent->r.svFlags; VectorCopy (ent->s.mins, body->s.mins); VectorCopy (ent->s.maxs, body->s.maxs); VectorCopy (ent->r.absmin, body->r.absmin); VectorCopy (ent->r.absmax, body->r.absmax); body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->s.contents = CONTENTS_BODY; body->r.ownerNum = ent->r.ownerNum; body->takedamage = qfalse; VectorSubtract( level.intermission_origin, pad->r.currentOrigin, vec ); vectoangles( vec, body->s.apos.trBase ); body->s.apos.trBase[PITCH] = 0; body->s.apos.trBase[ROLL] = 0; AngleVectors( body->s.apos.trBase, f, r, u ); VectorMA( pad->r.currentOrigin, offset[0], f, vec ); VectorMA( vec, offset[1], r, vec ); VectorMA( vec, offset[2], u, vec ); G_SetOrigin( body, vec ); trap_LinkEntity (body); body->count = place; return body; }
static void G_AddBot(const char *name, int skill, const char *team, const char *spawnPoint, int playerClass, int playerWeapon, int characerIndex, const char *respawn, const char *scriptName, int rank, int skills[], qboolean pow) { #define MAX_BOTNAMES 1024 int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; // char *model; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName("wolfbot"); if(!botinfo) { G_Printf(S_COLOR_RED "Error: Bot '%s' not defined\n", name); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey(botinfo, "funname"); if(!botname[0]) { botname = Info_ValueForKey(botinfo, "name"); } Info_SetValueForKey(userinfo, "name", botname); Info_SetValueForKey(userinfo, "rate", "25000"); Info_SetValueForKey(userinfo, "snaps", "20"); Info_SetValueForKey(userinfo, "skill", va("%i", skill)); s = Info_ValueForKey(botinfo, "aifile"); if(!*s) { trap_Printf(S_COLOR_RED "Error: bot has no aifile specified\n"); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(0); // Arnout: 0 means no prefered clientslot if(clientNum == -1) { G_Printf(S_COLOR_RED "Unable to add bot. All player slots are in use.\n"); G_Printf(S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n"); return; } // initialize the bot settings if(!team || !*team) { if(PickTeam(clientNum) == TEAM_AXIS) { team = "red"; } else { team = "blue"; } } Info_SetValueForKey(userinfo, "characterfile", Info_ValueForKey(botinfo, "aifile")); //Info_SetValueForKey( userinfo, "skill", va( "%i", skill ) ); Info_SetValueForKey(userinfo, "team", team); if(spawnPoint && spawnPoint[0]) { Info_SetValueForKey(userinfo, "spawnPoint", spawnPoint); } if(scriptName && scriptName[0]) { Info_SetValueForKey(userinfo, "scriptName", scriptName); } /* if (playerClass > 0) { Info_SetValueForKey( userinfo, "pClass", va("%i", playerClass) ); } if (playerWeapon) { Info_SetValueForKey( userinfo, "pWeapon", va("%i", playerWeapon) ); }*/ // END Mad Doc - TDF key = "wolfbot"; if(!Q_stricmp((char *)name, key)) { // read the botnames file, and pick a name that doesnt exist fileHandle_t f; int len, i, j, k; qboolean setname = qfalse; char botnames[8192], *pbotnames, *listbotnames[MAX_BOTNAMES], *token, *oldpbotnames; int lengthbotnames[MAX_BOTNAMES]; len = trap_FS_FOpenFile("botfiles/botnames.txt", &f, FS_READ); if(len >= 0) { if(len > sizeof(botnames)) { G_Error("botfiles/botnames.txt is too big (max = %i)", (int)sizeof(botnames)); } memset(botnames, 0, sizeof(botnames)); trap_FS_Read(botnames, len, f); pbotnames = botnames; // read them in i = 0; oldpbotnames = pbotnames; while((token = COM_Parse(&pbotnames))) { if(!token[0]) { break; } listbotnames[i] = strstr(oldpbotnames, token); lengthbotnames[i] = strlen(token); listbotnames[i][lengthbotnames[i]] = 0; oldpbotnames = pbotnames; if(++i == MAX_BOTNAMES) { break; } } // if(i > 2) { j = rand() % (i - 1); // start at a random spot inthe list for(k = j + 1; k != j; k++) { if(k == i) { k = -1; // gets increased on next loop continue; } if(ClientFromName(listbotnames[k]) == -1) { // found an unused name Info_SetValueForKey(userinfo, "name", listbotnames[k]); setname = qtrue; break; } } } // trap_FS_FCloseFile(f); } if(!setname) { Info_SetValueForKey(userinfo, "name", va("wolfbot_%i", clientNum + 1)); } } else { Info_SetValueForKey(userinfo, "name", name); } // if a character was specified, put the index of that character filename in the CS_CHARACTERS table in the userinfo if(characerIndex != -1) { Info_SetValueForKey(userinfo, "ch", va("%i", characerIndex)); } // if a rank was specified, use that /* if (rank != -1) { Info_SetValueForKey(userinfo, "rank", va("%i", rank)); }*/ // END Mad Doc - TDF bot = &g_entities[clientNum]; bot->r.svFlags |= SVF_BOT; if(pow) { bot->r.svFlags |= SVF_POW; } bot->inuse = qtrue; bot->aiName = bot->client->pers.netname; // register the userinfo trap_SetUserinfo(clientNum, userinfo); // have it connect to the game as a normal client if((s = ClientConnect(clientNum, qtrue, qtrue))) { G_Printf(S_COLOR_RED "Unable to add bot: %s\n", s); return; } SetTeam(bot, (char *)team, qtrue, -1, -1, qfalse); /* if( skills ) { int i; for( i = 0; i < SK_NUM_SKILLS; i++ ) { bot->client->sess.skill[i] = skills[i]; } }*/ return; }
/* ======================================================================================================================================= G_Damage targ: Entity that is being damaged. inflictor: Entity that is causing the damage. attacker: Entity that caused the inflictor to damage targ. example: targ = monster, inflictor = rocket, attacker = player. dir : Direction of the attack for knockback. point: Point at which the damage is being inflicted, used for headshots. damage: Amount of damage being inflicted. knockback: Force to be applied against targ as a result of the damage. inflictor, attacker, dir, and point can be NULL for environmental effects. dflags These flags are used to control how G_Damage works. DAMAGE_RADIUS Damage was indirect (from a nearby explosion). DAMAGE_NO_ARMOR Armor does not protect from this damage. DAMAGE_NO_KNOCKBACK Do not affect velocity, just view angles. DAMAGE_NO_PROTECTION Kills godmode, armor, everything. ======================================================================================================================================= */ void G_Damage(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod) { gclient_t *client; int take; int asave; int knockback; int max; #ifdef MISSIONPACK vec3_t bouncedir, impactpoint; #endif if (!targ->takedamage) { return; } // the intermission has already been qualified for, so don't allow any extra scoring if (level.intermissionQueued) { return; } #ifdef MISSIONPACK if (targ->client && mod != MOD_JUICED) { if (targ->client->invulnerabilityTime > level.time) { if (dir && point) { G_InvulnerabilityEffect(targ, dir, point, impactpoint, bouncedir); } return; } } #endif if (!inflictor) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if (!attacker) { attacker = &g_entities[ENTITYNUM_WORLD]; } // shootable doors / buttons don't actually have any health if (targ->s.eType == ET_MOVER) { if (targ->use && targ->moverState == MOVER_POS1) { targ->use(targ, inflictor, attacker); } return; } #ifdef MISSIONPACK if (g_gametype.integer == GT_OBELISK && CheckObeliskAttack(targ, attacker)) { return; } #endif // reduce damage by the attacker's handicap value unless they are rocket jumping if (attacker->client && attacker != targ) { max = attacker->client->ps.stats[STAT_MAX_HEALTH]; #ifdef MISSIONPACK if (bg_itemlist[attacker->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD) { max /= 2; } #endif damage = damage * max / 100; } client = targ->client; if (client) { if (client->noclip) { return; } } if (!dir) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } knockback = damage; if (knockback > 200) { knockback = 200; } if (targ->flags & FL_NO_KNOCKBACK) { knockback = 0; } if (dflags & DAMAGE_NO_KNOCKBACK) { knockback = 0; } // figure momentum add, even if the damage won't be taken if (knockback && targ->client) { vec3_t kvel; float mass; mass = 200; VectorScale(dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd(targ->client->ps.velocity, kvel, targ->client->ps.velocity); // set the timer so that the other client can't cancel out the movement immediately if (!targ->client->ps.pm_time) { int t; t = knockback * 2; if (t < 50) { t = 50; } if (t > 200) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // check for completely getting out of the damage if (!(dflags & DAMAGE_NO_PROTECTION)) { // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target if the attacker was on the same team #ifdef MISSIONPACK if (mod != MOD_JUICED && targ != attacker && !(dflags & DAMAGE_NO_TEAM_PROTECTION) && OnSameTeam(targ, attacker)) { #else if (targ != attacker && OnSameTeam(targ, attacker)) { #endif if (!g_friendlyFire.integer) { return; } } #ifdef MISSIONPACK if (mod == MOD_PROXIMITY_MINE) { if (inflictor && inflictor->parent && OnSameTeam(targ, inflictor->parent)) { return; } if (targ == attacker) { return; } } #endif // check for godmode if (targ->flags & FL_GODMODE) { return; } } // battlesuit protects from all radius damage (but takes knockback) and protects 50% against all damage if (client && client->ps.powerups[PW_BATTLESUIT]) { G_AddEvent(targ, EV_POWERUP_BATTLESUIT, 0); if ((dflags & DAMAGE_RADIUS) || (mod == MOD_FALLING)) { return; } damage *= 0.5; } // add to the attacker's hit counter (if the target isn't a general entity like a prox mine) if (attacker->client && client && targ != attacker && targ->health > 0 && targ->s.eType != ET_MISSILE && targ->s.eType != ET_GENERAL) { if (OnSameTeam(targ, attacker)) { attacker->client->ps.persistant[PERS_HITS]--; } else { attacker->client->ps.persistant[PERS_HITS]++; } attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health << 8)|(client->ps.stats[STAT_ARMOR]); } // always give half damage if hurting self, calculated after knockback, so rocket jumping works if (targ == attacker) { damage *= 0.5; } if (damage < 1) { damage = 1; } take = damage; // save some from armor asave = CheckArmor(targ, take, dflags); take -= asave; if (g_debugDamage.integer) { G_Printf("%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number, targ->health, take, asave); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks at the end of the frame if (client) { if (attacker) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; if (dir) { VectorCopy(dir, client->damage_from); client->damage_fromWorld = qfalse; } else { VectorCopy(targ->r.currentOrigin, client->damage_from); client->damage_fromWorld = qtrue; } } // see if it's the player hurting the emeny flag carrier #ifdef MISSIONPACK if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF) { #else if (g_gametype.integer == GT_CTF) { #endif Team_CheckHurtCarrier(targ, attacker); } if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if (take) { targ->health = targ->health - take; if (targ->client) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } if (targ->health <= 0) { if (client) { targ->flags |= FL_NO_KNOCKBACK; } if (targ->health < -999) { targ->health = -999; } targ->enemy = attacker; targ->die(targ, inflictor, attacker, take, mod); return; } else if (targ->pain) { targ->pain(targ, attacker, take); } } } /* ======================================================================================================================================= CanDamage Returns qtrue if the inflictor can directly damage the target. Used for explosions and melee attacks. ======================================================================================================================================= */ qboolean CanDamage(gentity_t *targ, vec3_t origin) { vec3_t dest; trace_t tr; vec3_t midpoint; vec3_t offsetmins = {-15, -15, -15}; vec3_t offsetmaxs = {15, 15, 15}; // use the midpoint of the bounds instead of the origin, because bmodels may have their origin is 0, 0, 0 VectorAdd(targ->r.absmin, targ->r.absmax, midpoint); VectorScale(midpoint, 0.5, midpoint); VectorCopy(midpoint, dest); trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0 || tr.entityNum == targ->s.number) { return qtrue; } // this should probably check in the plane of projection, rather than in world coordinate VectorCopy(midpoint, dest); dest[0] += offsetmaxs[0]; dest[1] += offsetmaxs[1]; dest[2] += offsetmaxs[2]; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) { return qtrue; } VectorCopy(midpoint, dest); dest[0] += offsetmaxs[0]; dest[1] += offsetmins[1]; dest[2] += offsetmaxs[2]; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) { return qtrue; } VectorCopy(midpoint, dest); dest[0] += offsetmins[0]; dest[1] += offsetmaxs[1]; dest[2] += offsetmaxs[2]; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) { return qtrue; } VectorCopy(midpoint, dest); dest[0] += offsetmins[0]; dest[1] += offsetmins[1]; dest[2] += offsetmaxs[2]; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) { return qtrue; } VectorCopy(midpoint, dest); dest[0] += offsetmaxs[0]; dest[1] += offsetmaxs[1]; dest[2] += offsetmins[2]; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) { return qtrue; } VectorCopy(midpoint, dest); dest[0] += offsetmaxs[0]; dest[1] += offsetmins[1]; dest[2] += offsetmins[2]; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) { return qtrue; } VectorCopy(midpoint, dest); dest[0] += offsetmins[0]; dest[1] += offsetmaxs[1]; dest[2] += offsetmins[2]; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) { return qtrue; } VectorCopy(midpoint, dest); dest[0] += offsetmins[0]; dest[1] += offsetmins[1]; dest[2] += offsetmins[2]; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) { return qtrue; } return qfalse; } /* ======================================================================================================================================= G_RadiusDamage ======================================================================================================================================= */ qboolean G_RadiusDamage(vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod) { float points, dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; qboolean hitClient = qfalse; if (radius < 1) { radius = 1; } for (i = 0; i < 3; i++) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES); for (e = 0; e < numListedEntities; e++) { ent = &g_entities[entityList[e]]; if (ent == ignore) { continue; } if (!ent->takedamage) { continue; } // find the distance from the edge of the bounding box for (i = 0; i < 3; i++) { if (origin[i] < ent->r.absmin[i]) { v[i] = ent->r.absmin[i] - origin[i]; } else if (origin[i] > ent->r.absmax[i]) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } dist = VectorLength(v); if (dist >= radius) { continue; } points = damage * (1.0 - dist / radius); if (CanDamage(ent, origin)) { if (LogAccuracyHit(ent, attacker)) { hitClient = qtrue; } VectorSubtract(ent->r.currentOrigin, origin, dir); // push the center of mass higher than the origin so players get knocked into the air more dir[2] += 24; G_Damage(ent, NULL, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod); } } return hitClient; }
//KK-OAX This was moved from g_mem.c to keep functionality from being broken. void Svcmd_GameMem_f( void ) { int usedMem; usedMem = POOLSIZE - freeMem; G_Printf( "Game memory status: %i out of %i bytes allocated\n", usedMem, POOLSIZE ); }
/* ================== G_SpawnBot ================== */ void G_SpawnBot(const char *text) { // bot parameters char name[MAX_TOKEN_CHARS] = "wolfBot"; //GS prevent bot health from counting down to 70 (i.e. don't set STAT_MAX_HEALTH = 70) char skill[MAX_TOKEN_CHARS] = "4"; char team[MAX_TOKEN_CHARS] = ""; char pClass[MAX_TOKEN_CHARS] = ""; char pWeapon[MAX_TOKEN_CHARS] = "0"; char spawnPoint[MAX_TOKEN_CHARS] = ""; char respawn[MAX_TOKEN_CHARS] = ""; char scriptName[MAX_TOKEN_CHARS] = "wolfBot"; char characterFile[MAX_TOKEN_CHARS] = ""; // START - Mad Doc - TDF char rank[MAX_TOKEN_CHARS] = ""; char botSkills[MAX_TOKEN_CHARS] = ""; // END Mad Doc - TDF char pow[MAX_TOKEN_CHARS] = "no"; // This is the selection meny index used in SetWolfSpawnWeapons int weaponSpawnNumber = -1; // parsing vars char *token, *pStr, *old_pStr; char cmd[MAX_TOKEN_CHARS], last_cmd[MAX_TOKEN_CHARS]; char cmd_var[MAX_TOKEN_CHARS]; char string[MAX_TOKEN_CHARS]; int j, pClassInt; int characterInt, rankNum; int skills[SK_NUM_SKILLS]; qboolean prisonerOfWar; int teamNum; // parameters spawnBotCommand_t params[] = { {"/name", name, qfalse, "[name]"}, {"/skill", skill, qfalse, "[0-4]"}, {"/team", team, qfalse, "[AXIS/ALLIES]"}, {"/class", pClass, qfalse, "[SOLDIER/MEDIC/LIEUTENANT/ENGINEER/COVERTOPS/FIELDOPS]"}, // FIXME: remove LIEUTENANT from missionpack {"/weapon", pWeapon, qfalse, "[weaponValue]"}, {"/spawnpoint", spawnPoint, qtrue, "[targetname]"}, {"/respawn", respawn, qfalse, "[ON/OFF]"}, {"/scriptName", scriptName, qfalse, "[scriptName]"}, {"/character", characterFile, qfalse, "[character]"}, // START Mad Doc - TDF {"/rank", rank, qfalse, "[rank]"}, {"/skills", botSkills, qfalse, "[botskills]"}, // not really to be exposed to script // END Mad Doc - TDF {"/pow", pow, qfalse, "[yes/no]"}, {NULL} }; // special tables typedef struct { char *weapon; int index; } spawnBotWeapons_t; // TAT 1/16/2003 - uninit'ed data here - getting crazy data for the skills memset(&skills, 0, sizeof(skills)); // // parse the vars pStr = (char *)text; token = COM_Parse(&pStr); Q_strncpyz(cmd, token, sizeof(cmd)); // if this is a question mark, show help info if(!Q_stricmp(cmd, "?") || !Q_stricmp(cmd, "/?")) { G_Printf ("Spawns a bot into the game, with the given parameters.\n\nSPAWNBOT [/param [value]] [/param [value]] ...\n\n where [/param [value]] may consist of:\n\n"); for(j = 0; params[j].cmd; j++) { G_Printf(" %s %s\n", params[j].cmd, params[j].help); } return; } // // intitializations for(j = 0; params[j].cmd; j++) { params[j].count = 0; } memset(last_cmd, 0, sizeof(last_cmd)); pStr = (char *)text; // // parse each command while(cmd[0]) { // // build the string up to the next parameter change token = COM_Parse(&pStr); Q_strncpyz(cmd, token, sizeof(cmd)); if(!cmd[0]) { break; } // if we find an "or", then use the last command if(!Q_stricmp(cmd, "or")) { // use the last command Q_strncpyz(cmd, last_cmd, sizeof(cmd)); } // // read the parameters memset(string, 0, sizeof(string)); token = COM_Parse(&pStr); Q_strncpyz(cmd_var, token, sizeof(cmd_var)); if(!cmd_var[0]) { break; } while(qtrue) { Q_strcat(string, sizeof(string), cmd_var); old_pStr = pStr; token = COM_Parse(&pStr); Q_strncpyz(cmd_var, token, sizeof(cmd_var)); if(cmd_var[0] && (cmd_var[0] == '/' || !Q_stricmp(cmd_var, "or"))) { pStr = old_pStr; break; } if(!cmd_var[0]) { break; } Q_strcat(string, sizeof(string), " "); } // // see if this command exists in the parameters table for(j = 0; params[j].cmd; j++) { if(!Q_stricmp(params[j].cmd, cmd)) { // found a match, if this field already has an entry, then randomly override it if(!params[j].count || (!params[j].appendParams && ((rand() % (++params[j].count)) == 0))) { Q_strncpyz(params[j].string, string, sizeof(string)); } else if(params[j].appendParams) { // append this token Q_strcat(params[j].string, sizeof(string), va(" %s", string)); } params[j].count++; break; } } if(!params[j].cmd) { G_Printf("G_SpawnBot: unknown parameter: %s\nFor usage info, use \"spawnbot /?\"\n", cmd); return; } // Q_strncpyz(last_cmd, cmd, sizeof(last_cmd)); Q_strncpyz(cmd, cmd_var, sizeof(cmd)); } // if(strlen(pClass)) { pClassInt = 1 + G_ClassForString(pClass); } else { pClassInt = 0; } if(!Q_stricmp(team, "red") || !Q_stricmp(team, "r") || !Q_stricmp(team, "axis")) { teamNum = TEAM_AXIS; } else if(!Q_stricmp(team, "blue") || !Q_stricmp(team, "b") || !Q_stricmp(team, "allies")) { teamNum = TEAM_ALLIES; } else { // pick the team with the least number of players teamNum = PickTeam(-1); } G_BotParseCharacterParms(characterFile, &characterInt); // Gordon: 27/11/02 if(*pow && !Q_stricmp(pow, "yes")) { prisonerOfWar = qtrue; } else { prisonerOfWar = qfalse; } // START Mad Doc - TDF // special case: if "NONE" is specified, treat this differently if(!Q_stricmp(pWeapon, "NONE")) { weaponSpawnNumber = -1; } // END Mad Doc - TDF // START Mad Doctor I changes, 8/17/2002. // If we have a weapon specified, and we have a class specified else if(isdigit(pWeapon[0])) { // Just convert the string to a number weaponSpawnNumber = atoi(pWeapon); } // if (isdigit(pWeapon[0]))... // If we have a weapon specified as a string, and we have a class specified else if(pClassInt > 0) { // Translate the weapon name into a proper weapon index // Get the index for the weapon weaponSpawnNumber = Bot_GetWeaponForClassAndTeam(pClassInt - 1, teamNum, pWeapon); // Get default weapon if(weaponSpawnNumber == -1) { weaponSpawnNumber = BG_GetPlayerClassInfo(teamNum, pClassInt - 1)->classWeapons[0]; } } // if (Q_stricmp(pWeapon[MAX_TOKEN_CHARS], "0")... // Otherwise, no weapon is selected else { // Just use the default weaponSpawnNumber = BG_GetPlayerClassInfo(teamNum, pClassInt - 1)->classWeapons[0]; } // else... // START Mad Doc - TDF rankNum = atoi(rank); if(rankNum) { rankNum--; // people like to start with 1 // Gordon: coders are people too :( } if(botSkills[0]) { // parse the skills out int i; char *pString, *token; pString = botSkills; for(i = 0; i < SK_NUM_SKILLS; i++) { token = COM_ParseExt(&pString, qfalse); skills[i] = atoi(token); } } // {"/botskills", botSkills, qfalse, "[botskills]"}, // not really to be exposed to script // END Mad Doc - TDF G_AddBot(name, atoi(skill), team, spawnPoint, pClassInt, weaponSpawnNumber, characterInt, respawn, scriptName, rankNum, skills, prisonerOfWar); // END Mad Doctor I changes, 8/17/2002. }
void G_Say(Gentity *ent, Gentity *target, int mode, const char *chatText) { int j; Gentity *other; int color; char name[64]; /* don't let text be too long for malicious reasons */ char text[MAX_SAY_TEXT]; char location[64]; if(g_gametype.integer < GT_TEAM && mode == SAY_TEAM) mode = SAY_ALL; switch(mode){ default: case SAY_ALL: G_LogPrintf("say: %s: %s\n", ent->client->pers.netname, chatText); Q_sprintf (name, sizeof(name), "%s%c%c"EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); color = COLOR_GREEN; break; case SAY_TEAM: G_LogPrintf("sayteam: %s: %s\n", ent->client->pers.netname, chatText); if(Team_GetLocationMsg(ent, location, sizeof(location))) Q_sprintf (name, sizeof(name), EC "(%s%c%c"EC ") (%s)"EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location); else Q_sprintf (name, sizeof(name), EC "(%s%c%c"EC ")"EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); color = COLOR_CYAN; break; case SAY_TELL: if(target && g_gametype.integer >= GT_TEAM && target->client->sess.team == ent->client->sess.team && Team_GetLocationMsg(ent, location, sizeof(location))) Q_sprintf (name, sizeof(name), EC "[%s%c%c"EC "] (%s)"EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location); else Q_sprintf (name, sizeof(name), EC "[%s%c%c"EC "]"EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); color = COLOR_MAGENTA; break; } Q_strncpyz(text, chatText, sizeof(text)); if(target){ G_SayTo(ent, target, mode, color, name, text); return; } /* echo the text to the console */ if(g_dedicated.integer) G_Printf("%s%s\n", name, text); /* send it to all the apropriate clients */ for(j = 0; j < level.maxclients; j++){ other = &g_entities[j]; G_SayTo(ent, other, mode, color, name, text); } }
/* ================ FinishSpawningItem Traces down to find where an item should rest, instead of letting them free fall from their spawn points ================ */ void FinishSpawningItem(gentity_t *ent) { trace_t tr; vec3_t dest; vec3_t maxs; if (ent->spawnflags & 1) // suspended { VectorSet(ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS); VectorSet(ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS); VectorCopy(ent->r.maxs, maxs); } else { // had to modify this so that items would spawn in shelves VectorSet(ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0); VectorSet(ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS); VectorCopy(ent->r.maxs, maxs); maxs[2] /= 2; } ent->r.contents = CONTENTS_TRIGGER | CONTENTS_ITEM; ent->touch = Touch_Item_Auto; ent->s.eType = ET_ITEM; ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex ent->s.otherEntityNum2 = 0; // takes modelindex2's place in signaling a dropped item // we don't use this (yet, anyway) so I'm taking it so you can specify a model for treasure items and clipboards //ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item if (ent->model) { ent->s.modelindex2 = G_ModelIndex(ent->model); } // using an item causes it to respawn ent->use = Use_Item; // moved this up so it happens for suspended items too (and made it a function) G_SetAngle(ent, ent->s.angles); if (ent->spawnflags & 1) // suspended { G_SetOrigin(ent, ent->s.origin); } else { VectorSet(dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096); trap_Trace(&tr, ent->s.origin, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID); if (tr.startsolid) { vec3_t temp; VectorCopy(ent->s.origin, temp); temp[2] -= ITEM_RADIUS; VectorSet(dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096); trap_Trace(&tr, temp, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID); } if (tr.startsolid) { G_Printf("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin)); G_FreeEntity(ent); return; } // allow to ride movers ent->s.groundEntityNum = tr.entityNum; G_SetOrigin(ent, tr.endpos); } if (ent->spawnflags & 2) // spin { ent->s.eFlags |= EF_SPINNING; } // team slaves and targeted items aren't present at start if ((ent->flags & FL_TEAMSLAVE) || ent->targetname) { ent->flags |= FL_NODRAW; //ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; return; } // health/ammo can potentially be multi-stage (multiple use) if (ent->item->giType == IT_HEALTH || ent->item->giType == IT_AMMO) { int i; // having alternate models defined in bg_misc.c for a health or ammo item specify it as "multi-stage" // - left-hand operand of comma expression has no effect // initial line: for(i=0;i<4,ent->item->world_model[i];i++) {} for (i = 0; i < 4 && ent->item->world_model[i] ; i++) { } ent->s.density = i - 1; // store number of stages in 'density' for client (most will have '1') } trap_LinkEntity(ent); }
/* =============== G_ParseMapRotation Parse a map rotation section =============== */ static bool G_ParseMapRotation( mapRotation_t *mr, char **text_p ) { char *token; bool mnSet = false; mapRotationEntry_t *mre = NULL; mapRotationCondition_t *mrc; // read optional parameters while( 1 ) { token = COM_Parse( text_p ); if( !token ) break; if( !Q_stricmp( token, "" ) ) return false; if( !Q_stricmp( token, "{" ) ) { if( !mnSet ) { G_Printf( S_COLOR_RED "ERROR: map settings section with no name\n" ); return false; } if( !G_ParseMapCommandSection( mre, text_p ) ) { G_Printf( S_COLOR_RED "ERROR: failed to parse map command section\n" ); return false; } mnSet = false; continue; } else if( !Q_stricmp( token, "goto" ) ) { token = COM_Parse( text_p ); if( !token ) break; mrc = &mre->conditions[ mre->numConditions ]; mrc->unconditional = true; Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) ); if( mre->numConditions == MAX_MAP_ROTATION_CONDS ) { G_Printf( S_COLOR_RED "ERROR: maximum number of conditions for one map (%d) reached\n", MAX_MAP_ROTATION_CONDS ); return false; } else mre->numConditions++; continue; } else if( !Q_stricmp( token, "if" ) ) { token = COM_Parse( text_p ); if( !token ) break; mrc = &mre->conditions[ mre->numConditions ]; if( !Q_stricmp( token, "numClients" ) ) { mrc->lhs = MCV_NUMCLIENTS; token = COM_Parse( text_p ); if( !token ) break; if( !Q_stricmp( token, "<" ) ) mrc->op = MCO_LT; else if( !Q_stricmp( token, ">" ) ) mrc->op = MCO_GT; else if( !Q_stricmp( token, "=" ) ) mrc->op = MCO_EQ; else { G_Printf( S_COLOR_RED "ERROR: invalid operator in expression: %s\n", token ); return false; } token = COM_Parse( text_p ); if( !token ) break; mrc->numClients = atoi( token ); } else if( !Q_stricmp( token, "lastWin" ) ) { mrc->lhs = MCV_LASTWIN; token = COM_Parse( text_p ); if( !token ) break; if( !Q_stricmp( token, "aliens" ) ) mrc->lastWin = PTE_ALIENS; else if( !Q_stricmp( token, "humans" ) ) mrc->lastWin = PTE_HUMANS; else { G_Printf( S_COLOR_RED "ERROR: invalid right hand side in expression: %s\n", token ); return false; } } else if( !Q_stricmp( token, "random" ) ) mrc->lhs = MCV_RANDOM; else { G_Printf( S_COLOR_RED "ERROR: invalid left hand side in expression: %s\n", token ); return false; } token = COM_Parse( text_p ); if( !token ) break; mrc->unconditional = false; Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) ); if( mre->numConditions == MAX_MAP_ROTATION_CONDS ) { G_Printf( S_COLOR_RED "ERROR: maximum number of conditions for one map (%d) reached\n", MAX_MAP_ROTATION_CONDS ); return false; } else mre->numConditions++; continue; } else if( !Q_stricmp( token, "}" ) ) return true; //reached the end of this map rotation mre = &mr->maps[ mr->numMaps ]; if( mr->numMaps == MAX_MAP_ROTATION_MAPS ) { G_Printf( S_COLOR_RED "ERROR: maximum number of maps in one rotation (%d) reached\n", MAX_MAP_ROTATION_MAPS ); return false; } else mr->numMaps++; Q_strncpyz( mre->name, token, sizeof( mre->name ) ); mnSet = true; } return false; }
/* =================== Svcmd_EntityList_f =================== */ void Svcmd_EntityList_f( void ) { int e; gentity_t *check; check = g_entities + 1; for ( e = 1; e < level.num_entities ; e++, check++ ) { if ( !check->inuse ) { continue; } G_Printf( "%3i:", e ); switch ( check->s.eType ) { case ET_GENERAL: G_Printf( "ET_GENERAL " ); break; case ET_PLAYER: G_Printf( "ET_PLAYER " ); break; case ET_ITEM: G_Printf( "ET_ITEM " ); break; case ET_MISSILE: G_Printf( "ET_MISSILE " ); break; case ET_MOVER: G_Printf( "ET_MOVER " ); break; case ET_BEAM: G_Printf( "ET_BEAM " ); break; case ET_PORTAL: G_Printf( "ET_PORTAL " ); break; case ET_SPEAKER: G_Printf( "ET_SPEAKER " ); break; case ET_PUSH_TRIGGER: G_Printf( "ET_PUSH_TRIGGER " ); break; case ET_CONCUSSIVE_TRIGGER: G_Printf( "ET_CONCUSSIVE_TRIGGR" ); break; case ET_TELEPORT_TRIGGER: G_Printf( "ET_TELEPORT_TRIGGER " ); break; case ET_INVISIBLE: G_Printf( "ET_INVISIBLE " ); break; case ET_EXPLOSIVE: G_Printf( "ET_EXPLOSIVE " ); break; case ET_EF_SPOTLIGHT: G_Printf( "ET_EF_SPOTLIGHT " ); break; case ET_ALARMBOX: G_Printf( "ET_ALARMBOX " ); break; default: G_Printf( "%3i ", check->s.eType ); break; } if ( check->classname ) { G_Printf( "%s", check->classname ); } G_Printf( "\n" ); } }
/* =================== Svcmd_EntityList_f =================== */ void Svcmd_EntityList_f( void ) { int e; gentity_t *check; check = g_entities; for( e = 0; e < level.num_entities; e++, check++ ) { if( !check->inuse ) continue; G_Printf( "%3i:", e ); switch( check->s.eType ) { case ET_GENERAL: G_Printf( "ET_GENERAL " ); break; case ET_PLAYER: G_Printf( "ET_PLAYER " ); break; case ET_ITEM: G_Printf( "ET_ITEM " ); break; case ET_BUILDABLE: G_Printf( "ET_BUILDABLE " ); break; case ET_LOCATION: G_Printf( "ET_LOCATION " ); break; case ET_MISSILE: G_Printf( "ET_MISSILE " ); break; case ET_MOVER: G_Printf( "ET_MOVER " ); break; case ET_BEAM: G_Printf( "ET_BEAM " ); break; case ET_PORTAL: G_Printf( "ET_PORTAL " ); break; case ET_SPEAKER: G_Printf( "ET_SPEAKER " ); break; case ET_PUSH_TRIGGER: G_Printf( "ET_PUSH_TRIGGER " ); break; case ET_TELEPORT_TRIGGER: G_Printf( "ET_TELEPORT_TRIGGER " ); break; case ET_INVISIBLE: G_Printf( "ET_INVISIBLE " ); break; case ET_GRAPPLE: G_Printf( "ET_GRAPPLE " ); break; case ET_CORPSE: G_Printf( "ET_CORPSE " ); break; case ET_PARTICLE_SYSTEM: G_Printf( "ET_PARTICLE_SYSTEM " ); break; case ET_ANIMMAPOBJ: G_Printf( "ET_ANIMMAPOBJ " ); break; case ET_MODELDOOR: G_Printf( "ET_MODELDOOR " ); break; case ET_LIGHTFLARE: G_Printf( "ET_LIGHTFLARE " ); break; case ET_LEV2_ZAP_CHAIN: G_Printf( "ET_LEV2_ZAP_CHAIN " ); break; default: G_Printf( "%-3i ", check->s.eType ); break; } if( check->classname ) G_Printf( "%s", check->classname ); G_Printf( "\n" ); } }
static void Svcmd_Kick_f( void ) { gclient_t *cl; int i; int timeout = -1; char sTimeout[MAX_TOKEN_CHARS]; char name[MAX_TOKEN_CHARS]; // make sure server is running if ( !G_Is_SV_Running() ) { G_Printf( "Server is not running.\n" ); return; } if ( trap_Argc() < 2 || trap_Argc() > 3 ) { G_Printf( "Usage: kick <player name> [timeout]\n" ); return; } if ( trap_Argc() == 3 ) { trap_Argv( 2, sTimeout, sizeof( sTimeout ) ); timeout = atoi( sTimeout ); } else { timeout = 300; } trap_Argv( 1, name, sizeof( name ) ); cl = G_GetPlayerByName( name ); //ClientForString( name ); if ( !cl ) { if ( !Q_stricmp( name, "all" ) ) { for ( i = 0, cl = level.clients; i < level.numConnectedClients; i++, cl++ ) { // dont kick localclients ... if ( cl->pers.localClient ) { continue; } if ( timeout != -1 ) { const char *ip; char userinfo[MAX_INFO_STRING]; trap_GetUserinfo( cl->ps.clientNum, userinfo, sizeof( userinfo ) ); ip = Info_ValueForKey( userinfo, "ip" ); // use engine banning system, mods may choose to use their own banlist if ( USE_ENGINE_BANLIST ) { // kick but dont ban bots, they arent that lame if ( ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) ) { timeout = 0; } trap_DropClient( cl->ps.clientNum, "player kicked", timeout ); } else { trap_DropClient( cl->ps.clientNum, "player kicked", 0 ); // kick but dont ban bots, they arent that lame if ( !( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) ) { AddIPBan( ip ); } } } else { trap_DropClient( cl->ps.clientNum, "player kicked", 0 ); } } } else if ( !Q_stricmp( name, "allbots" ) ) { for ( i = 0, cl = level.clients; i < level.numConnectedClients; i++, cl++ ) { if ( !( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) ) { continue; } // kick but dont ban bots, they arent that lame trap_DropClient( cl->ps.clientNum, "player kicked", 0 ); } } return; } else { // dont kick localclients ... if ( cl->pers.localClient ) { G_Printf( "Cannot kick host player\n" ); return; } if ( timeout != -1 ) { const char *ip; char userinfo[MAX_INFO_STRING]; trap_GetUserinfo( cl->ps.clientNum, userinfo, sizeof( userinfo ) ); ip = Info_ValueForKey( userinfo, "ip" ); // use engine banning system, mods may choose to use their own banlist if ( USE_ENGINE_BANLIST ) { // kick but dont ban bots, they arent that lame if ( ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) ) { timeout = 0; } trap_DropClient( cl->ps.clientNum, "player kicked", timeout ); } else { trap_DropClient( cl->ps.clientNum, "player kicked", 0 ); // kick but dont ban bots, they arent that lame if ( !( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) ) { AddIPBan( ip ); } } } else { trap_DropClient( cl->ps.clientNum, "player kicked", 0 ); } } }
void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod ) { gclient_t *client; int take; int save; int asave; int knockback; int max; if (!targ->takedamage) { return; } //in entityplus bots cannot harm other bots (unless it's a telefrag) if ( IsBot( targ ) && attacker && IsBot( attacker ) && mod != MOD_TELEFRAG ) return; //if attacker is shooter which was configured not to harm target, do nothing if ( attacker && strstr(attacker->classname, "shooter_" )) { if ( (attacker->spawnflags & 2) && IsBot( targ ) ) //spawnflags 2 == NO_BOTS return; if ( (attacker->spawnflags & 4) && !IsBot( targ ) ) //spawnflag 4 = NO_HUMANS return; } // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued ) { return; } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER ) { if ( strcmp(targ->classname, "func_breakable") && targ->use && (targ->moverState == MOVER_POS1 || targ->moverState == ROTATOR_POS1) ) { targ->use( targ, inflictor, attacker ); } else { // entity is a func_breakable if ( (targ->spawnflags & 1024) && attacker == level.player ) return; if ( (targ->spawnflags & 2048) && IsBot(attacker) ) return; if ( (targ->spawnflags & 4096) && strstr(attacker->classname, "shooter_") ) return; if ( !strcmp(targ->classname, "func_breakable") ) { targ->health -= damage; if ( targ->health <= 0 ) Break_Breakable(targ, attacker); } } return; } // scale back damage from bots or shooters to humans in single player, based on skill level // "I can win" does 0.05 dmg // "Bring it on" does 0.15 dmg // "Hurt me plenty" does 0.25 dmg // "Hardcore" does 0.35 dmg // "Nightmare does 0.45 dmg if ( attacker && ( IsBot(attacker) || !strcmp(attacker->classname, "shooter_bfg") || !strcmp(attacker->classname, "shooter_grenade") || !strcmp(attacker->classname, "shooter_plasma") || !strcmp(attacker->classname, "shooter_rocket") ) ) { float skill = trap_Cvar_VariableValue( "g_spSkill" ); int orgdmg = damage; if ( attacker->parent && attacker->parent->skill ) skill += attacker->parent->skill; if (skill < 1) skill = 1; //relative skill level should not drop below 1 but is allowed to rise above 5 damage *= ( ( 0.1 * skill ) - 0.05 ); //damage is always rounded down. if ( damage < 1 ) damage = 1; //make sure bot does at least -some- damage //G_Printf("skill: %f -- mp: %f -- orgdmg: %i -- dmg: %i\n", skill, ( 0.1 * skill ) - 0.05, orgdmg, damage); } client = targ->client; if ( client ) { if ( client->noclip ) { return; } } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } knockback = damage; if ( knockback > 200 ) { knockback = 200; } if ( targ->flags & FL_NO_KNOCKBACK ) { knockback = 0; } if ( dflags & DAMAGE_NO_KNOCKBACK ) { knockback = 0; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; float mass; mass = 200; VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // check for completely getting out of the damage if ( !(dflags & DAMAGE_NO_PROTECTION) ) { // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team if ( targ != attacker && OnSameTeam (targ, attacker) ) { if ( !g_friendlyFire.integer ) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } } // battlesuit protects from all radius damage (but takes knockback) // and protects 50% against all damage if ( client && client->ps.powerups[PW_BATTLESUIT] ) { G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 ); if ( ( dflags & DAMAGE_RADIUS ) || ( mod == MOD_FALLING ) ) { return; } damage *= 0.5; } // add to the attacker's hit counter (if the target isn't a general entity like a prox mine) if ( attacker->client && targ != attacker && targ->health > 0 && targ->s.eType != ET_MISSILE && targ->s.eType != ET_GENERAL) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS]--; } else { attacker->client->ps.persistant[PERS_HITS]++; } attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health<<8)|(client->ps.stats[STAT_ARMOR]); } // always give half damage if hurting self // calculated after knockback, so rocket jumping works if ( targ == attacker) { damage *= 0.5; } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; // save some from armor asave = CheckArmor (targ, take, dflags); take -= asave; if ( g_debugDamage.integer ) { G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number, targ->health, take, asave ); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if (take) { if ( g_mutators.integer & MT_INSTAGIB && IsBot( targ ) ) targ->health = -999; else targ->health = targ->health - take; if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } if ( targ->health <= 0 ) { if ( client ) targ->flags |= FL_NO_KNOCKBACK; if (targ->health < -999) targ->health = -999; targ->enemy = attacker; targ->die (targ, inflictor, attacker, take, mod); return; } else if ( targ->pain ) { targ->pain (targ, attacker, take); } } }
static bool G_asInitializeGametypeScript( asIScriptModule *asModule ) { int error; asIScriptContext *ctx; int funcCount; const char *fdeclstr; // grab script function calls funcCount = 0; fdeclstr = "void GT_InitGametype()"; level.gametype.initFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.initFunc ) { G_Printf( "* The function '%s' was not found. Can not continue.\n", fdeclstr ); return false; } else { funcCount++; } fdeclstr = "void GT_SpawnGametype()"; level.gametype.spawnFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.spawnFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } fdeclstr = "void GT_MatchStateStarted()"; level.gametype.matchStateStartedFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.matchStateStartedFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } fdeclstr = "bool GT_MatchStateFinished( int incomingMatchState )"; level.gametype.matchStateFinishedFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.matchStateFinishedFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } fdeclstr = "void GT_ThinkRules()"; level.gametype.thinkRulesFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.thinkRulesFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } fdeclstr = "void GT_PlayerRespawn( Entity @ent, int old_team, int new_team )"; level.gametype.playerRespawnFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.playerRespawnFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } fdeclstr = "void GT_ScoreEvent( Client @client, const String &score_event, const String &args )"; level.gametype.scoreEventFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.scoreEventFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } fdeclstr = "String @GT_ScoreboardMessage( uint maxlen )"; level.gametype.scoreboardMessageFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.scoreboardMessageFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } fdeclstr = "Entity @GT_SelectSpawnPoint( Entity @ent )"; level.gametype.selectSpawnPointFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.selectSpawnPointFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } fdeclstr = "bool GT_Command( Client @client, const String &cmdString, const String &argsString, int argc )"; level.gametype.clientCommandFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.clientCommandFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } fdeclstr = "void GT_Shutdown()"; level.gametype.shutdownFunc = asModule->GetFunctionByDecl( fdeclstr ); if( !level.gametype.shutdownFunc ) { if( developer->integer || sv_cheats->integer ) { G_Printf( "* The function '%s' was not present in the script.\n", fdeclstr ); } } else { funcCount++; } // // Initialize AI gametype exports // AI_InitGametypeScript( asModule ); // // execute the GT_InitGametype function // ctx = game.asExport->asAcquireContext( GAME_AS_ENGINE() ); error = ctx->Prepare( static_cast<asIScriptFunction *>( level.gametype.initFunc ) ); if( error < 0 ) { return false; } error = ctx->Execute(); if( G_ExecutionErrorReport( error ) ) { return false; } return true; }
void GClip_LinkEntity( edict_t *ent ) { areanode_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j, k; int area; int topnode; if( ent->r.area.prev ) GClip_UnlinkEntity( ent ); // unlink from old position if( ent == game.edicts ) return; // don't add the world if( !ent->r.inuse ) return; // set the size VectorSubtract( ent->r.maxs, ent->r.mins, ent->r.size ); if( ent->r.solid == SOLID_NOT || ( ent->r.svflags & SVF_PROJECTILE ) ) { ent->s.solid = 0; } else if( ISBRUSHMODEL( ent->s.modelindex ) ) { // the only predicted SOLID_TRIGGER entity is ET_PUSH_TRIGGER if( ent->r.solid != SOLID_TRIGGER || ent->s.type == ET_PUSH_TRIGGER ) ent->s.solid = SOLID_BMODEL; else ent->s.solid = 0; } else // encode the size into the entity_state for client prediction { if( ent->r.solid == SOLID_TRIGGER ) { ent->s.solid = 0; } else { // assume that x/y are equal and symetric i = ent->r.maxs[0]/8; clamp( i, 1, 31 ); // z is not symetric j = ( -ent->r.mins[2] )/8; clamp( j, 1, 31 ); // and z maxs can be negative... k = ( ent->r.maxs[2]+32 )/8; clamp( k, 1, 63 ); ent->s.solid = ( k<<10 ) | ( j<<5 ) | i; } } // set the abs box if( ISBRUSHMODEL( ent->s.modelindex ) && ( ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2] ) ) { // expand for rotation float radius; radius = RadiusFromBounds( ent->r.mins, ent->r.maxs ); for( i = 0; i < 3; i++ ) { ent->r.absmin[i] = ent->s.origin[i] - radius; ent->r.absmax[i] = ent->s.origin[i] + radius; } } else // axis aligned { VectorAdd( ent->s.origin, ent->r.mins, ent->r.absmin ); VectorAdd( ent->s.origin, ent->r.maxs, ent->r.absmax ); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->r.absmin[0] -= 1; ent->r.absmin[1] -= 1; ent->r.absmin[2] -= 1; ent->r.absmax[0] += 1; ent->r.absmax[1] += 1; ent->r.absmax[2] += 1; // link to PVS leafs ent->r.num_clusters = 0; ent->r.areanum = ent->r.areanum2 = -1; // get all leafs, including solids num_leafs = trap_CM_BoxLeafnums( ent->r.absmin, ent->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode ); // set areas for( i = 0; i < num_leafs; i++ ) { clusters[i] = trap_CM_LeafCluster( leafs[i] ); area = trap_CM_LeafArea( leafs[i] ); if( area > -1 ) { // doors may legally straggle two areas, // but nothing should ever need more than that if( ent->r.areanum > -1 && ent->r.areanum != area ) { if( ent->r.areanum2 > -1 && ent->r.areanum2 != area ) { if( developer->integer ) G_Printf( "Object %s touching 3 areas at %f %f %f\n", ( ent->classname ? ent->classname : "" ), ent->r.absmin[0], ent->r.absmin[1], ent->r.absmin[2] ); } ent->r.areanum2 = area; } else ent->r.areanum = area; } } if( num_leafs >= MAX_TOTAL_ENT_LEAFS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; } else { ent->r.num_clusters = 0; for( i = 0; i < num_leafs; i++ ) { if( clusters[i] == -1 ) continue; // not a visible leaf for( j = 0; j < i; j++ ) if( clusters[j] == clusters[i] ) break; if( j == i ) { if( ent->r.num_clusters == MAX_ENT_CLUSTERS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; break; } ent->r.clusternums[ent->r.num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid if( !ent->r.linkcount && !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) ) { VectorCopy( ent->s.origin, ent->s.old_origin ); ent->olds = ent->s; } ent->r.linkcount++; ent->linked = qtrue; if( ent->r.solid == SOLID_NOT ) return; // find the first node that the ent's box crosses node = sv_areanodes; while( 1 ) { if( node->axis == -1 ) break; if( ent->r.absmin[node->axis] > node->dist ) node = node->children[0]; else if( ent->r.absmax[node->axis] < node->dist ) node = node->children[1]; else break; // crosses the node } // link it in if( ent->r.solid == SOLID_TRIGGER ) GClip_InsertLinkBefore( &ent->r.area, &node->trigger_edicts, NUM_FOR_EDICT( ent ) ); else GClip_InsertLinkBefore( &ent->r.area, &node->solid_edicts, NUM_FOR_EDICT( ent ) ); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, const char *pclass, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; char userinfo[MAX_INFO_STRING]; int preTeam = 0; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); /* if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } */ key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "munro/main/default"; //RPG-X MODEL SYSTEM } Info_SetValueForKey( userinfo, key, model ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( G_Client_PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); if (g_gametype.integer >= GT_TEAM) { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { bot->client->sess.sessionTeam = G_Client_PickTeam( -1 ); } } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( G_Client_Connect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { team = "Blue"; } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); G_Client_UserinfoChanged( clientNum ); } if( delay == 0 ) { G_Client_Begin( clientNum, qfalse, qfalse, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* * GClip_AreaEdicts * fills in a table of edict ids with edicts that have bounding boxes * that intersect the given area. It is possible for a non-axial bmodel * to be returned that doesn't actually intersect the area on an exact * test. * returns the number of pointers filled in * ??? does this always return the world? */ static int GClip_AreaEdicts( vec3_t mins, vec3_t maxs, int *list, int maxcount, int areatype, int timeDelta ) { link_t *l, *start; c4clipedict_t *clipEnt; int stackdepth = 0, count = 0; areanode_t *localstack[AREA_NODES], *node = sv_areanodes; while( 1 ) { // touch linked edicts if( areatype == AREA_SOLID ) start = &node->solid_edicts; else start = &node->trigger_edicts; for( l = start->next; l != start; l = l->next ) { clipEnt = GClip_GetClipEdictForDeltaTime( l->entNum, timeDelta ); if( clipEnt->r.solid == SOLID_NOT ) continue; // deactivated if( !BoundsIntersect( clipEnt->r.absmin, clipEnt->r.absmax, mins, maxs ) ) continue; // not touching if( count == maxcount ) { G_Printf( "G_AreaEdicts: MAXCOUNT\n" ); return count; } list[count++] = l->entNum; } if( node->axis == -1 ) goto checkstack; // terminal node // recurse down both sides if( maxs[node->axis] > node->dist ) { if( mins[node->axis] < node->dist ) { localstack[stackdepth++] = node->children[0]; node = node->children[1]; continue; } node = node->children[0]; continue; } if( mins[node->axis] < node->dist ) { node = node->children[1]; continue; } checkstack: if( !stackdepth ) return count; node = localstack[--stackdepth]; } return count; }
void Svcmd_GameMem_f( void ) { G_Printf( "Game memory status: %i out of %i bytes allocated\n", allocPoint, POOLSIZE ); }
qboolean G_Damage(gentity_t * targ, gentity_t * inflictor, gentity_t * attacker, const vec3_t dir, const vec3_t point, int damage, int dflags, int mod) { gclient_t *client; int take; int save; int asave; int knockback; int max; #ifdef MISSIONPACK vec3_t bouncedir, impactpoint; #endif if(!inflictor) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if(!attacker) { attacker = &g_entities[ENTITYNUM_WORLD]; } #ifdef G_LUA // Lua API callbacks if(targ->luaHurt && !targ->client) { G_LuaHook_EntityHurt(targ->luaHurt, targ->s.number, inflictor->s.number, attacker->s.number); } #endif if(!targ->takedamage) { return qfalse; } // the intermission has allready been qualified for, so don't // allow any extra scoring if(level.intermissionQueued) { return qfalse; } #ifdef MISSIONPACK if(targ->client && mod != MOD_JUICED) { if(targ->client->invulnerabilityTime > level.time) { if(dir && point) { G_InvulnerabilityEffect(targ, dir, point, impactpoint, bouncedir); } return qfalse; } } #endif // shootable doors / buttons don't actually have any health if(targ->s.eType == ET_MOVER) { if(targ->use && targ->moverState == MOVER_POS1) { targ->use(targ, inflictor, attacker); } return qfalse; } if(g_gametype.integer == GT_OBELISK && CheckObeliskAttack(targ, attacker)) { return qfalse; } // reduce damage by the attacker's handicap value // unless they are rocket jumping if(attacker->client && attacker != targ) { max = attacker->client->ps.stats[STAT_MAX_HEALTH]; #ifdef MISSIONPACK if(bg_itemlist[attacker->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD) { max /= 2; } #endif damage = damage * max / 100; } client = targ->client; if(client) { if(client->noclip) { return qfalse; } } if(!dir) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize((float *)dir); } knockback = damage; if(knockback > 200) { knockback = 200; } if(targ->flags & FL_NO_KNOCKBACK) { knockback = 0; } if(dflags & DAMAGE_NO_KNOCKBACK) { knockback = 0; } // figure momentum add, even if the damage won't be taken if(knockback && targ->client) { vec3_t kvel; float mass; mass = 200; VectorScale(dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd(targ->client->ps.velocity, kvel, targ->client->ps.velocity); // set the timer so that the other client can't cancel // out the movement immediately if(!targ->client->ps.pm_time) { int t; t = knockback * 2; if(t < 50) { t = 50; } if(t > 200) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } if(attacker->s.weapon == WP_RAILGUN && targ->s.eType == ET_PROJECTILE && targ->s.weapon == WP_RAILGUN) { // Tr3B: added this special case to shoot railgun spheres // railgun spheres can be shot by any raigun user } // check for completely getting out of the damage else if(!(dflags & DAMAGE_NO_PROTECTION)) { // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team #ifdef MISSIONPACK if(mod != MOD_JUICED && targ != attacker && !(dflags & DAMAGE_NO_TEAM_PROTECTION) && OnSameTeam(targ, attacker)) { #else if(targ != attacker && OnSameTeam(targ, attacker)) { #endif if(!g_friendlyFire.integer) { return qfalse; } } #ifdef MISSIONPACK if(mod == MOD_PROXIMITY_MINE) { if(inflictor && inflictor->parent && OnSameTeam(targ, inflictor->parent)) { return qfalse; } if(targ == attacker) { return qfalse; } } #endif // check for godmode if(targ->flags & FL_GODMODE) { return qfalse; } } // battlesuit protects from all radius damage (but takes knockback) // and protects 50% against all damage if(client && client->ps.powerups[PW_BATTLESUIT]) { G_AddEvent(targ, EV_POWERUP_BATTLESUIT, 0); if((dflags & DAMAGE_RADIUS) || (mod == MOD_FALLING)) { return qfalse; } damage *= 0.5; } // add to the attacker's hit counter (if the target isn't a general entity like a prox mine) if(attacker->client && targ->client && targ != attacker && targ->health > 0 && targ->s.eType != ET_PROJECTILE && targ->s.eType != ET_PROJECTILE2 && targ->s.eType != ET_GENERAL) { if(OnSameTeam(targ, attacker)) { attacker->client->ps.persistant[PERS_HITS]--; } else { attacker->client->ps.persistant[PERS_HITS]++; } attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health << 8) | (targ->client->ps.stats[STAT_ARMOR]); } // always give half damage if hurting self // calculated after knockback, so rocket jumping works if(targ == attacker) { damage *= 0.5; } if(damage < 1) { damage = 1; } take = damage; save = 0; // save some from armor asave = CheckArmor(targ, take, dflags); take -= asave; if(g_debugDamage.integer) { G_Printf("%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number, targ->health, take, asave); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if(client) { if(attacker) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; if(dir) { VectorCopy(dir, client->damage_from); client->damage_fromWorld = qfalse; } else { VectorCopy(targ->r.currentOrigin, client->damage_from); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier if(g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF) { Team_CheckHurtCarrier(targ, attacker); } if(targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if(take) { targ->health = targ->health - take; if(targ->client) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } if(targ->health <= 0) { if(client) targ->flags |= FL_NO_KNOCKBACK; if(targ->health < -999) targ->health = -999; targ->enemy = attacker; #ifdef G_LUA // Lua API callbacks if(targ->luaDie && !targ->client) { G_LuaHook_EntityDie(targ->luaDie, targ->s.number, inflictor->s.number, attacker->s.number, take, mod); } #endif targ->die(targ, inflictor, attacker, take, mod); return qfalse; } else if(targ->pain) { targ->pain(targ, attacker, take); } } return qfalse; } /* ============ CanDamage Returns qtrue if the inflictor can directly damage the target. Used for explosions and melee attacks. ============ */ qboolean CanDamage(gentity_t * targ, vec3_t origin) { vec3_t dest; trace_t tr; vec3_t midpoint; // use the midpoint of the bounds instead of the origin, because // bmodels may have their origin is 0,0,0 VectorAdd(targ->r.absmin, targ->r.absmax, midpoint); VectorScale(midpoint, 0.5, midpoint); VectorCopy(midpoint, dest); trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if(tr.fraction == 1.0 || tr.entityNum == targ->s.number) return qtrue; // this should probably check in the plane of projection, // rather than in world coordinate, and also include Z VectorCopy(midpoint, dest); dest[0] += 15.0; dest[1] += 15.0; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if(tr.fraction == 1.0) return qtrue; VectorCopy(midpoint, dest); dest[0] += 15.0; dest[1] -= 15.0; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if(tr.fraction == 1.0) return qtrue; VectorCopy(midpoint, dest); dest[0] -= 15.0; dest[1] += 15.0; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if(tr.fraction == 1.0) return qtrue; VectorCopy(midpoint, dest); dest[0] -= 15.0; dest[1] -= 15.0; trap_Trace(&tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if(tr.fraction == 1.0) return qtrue; return qfalse; }
/* ================ FinishSpawningItem Traces down to find where an item should rest, instead of letting them free fall from their spawn points ================ */ void FinishSpawningItem( gentity_t *ent ) { trace_t tr; vec3_t dest; vec3_t maxs; if ( ent->spawnflags & 1 ) { // suspended VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS ); VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS ); VectorCopy( ent->r.maxs, maxs ); } else { // Rafael // had to modify this so that items would spawn in shelves VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0 ); VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS ); VectorCopy( ent->r.maxs, maxs ); maxs[2] /= 2; } ent->r.contents = CONTENTS_TRIGGER | CONTENTS_ITEM; ent->touch = Touch_Item_Auto; ent->s.eType = ET_ITEM; ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex ent->s.otherEntityNum2 = 0; // DHM - Nerve :: takes modelindex2's place in signaling a dropped item //----(SA) we don't use this (yet, anyway) so I'm taking it so you can specify a model for treasure items and clipboards // ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item if ( ent->model ) { ent->s.modelindex2 = G_ModelIndex( ent->model ); } // if clipboard, add the menu name string to the client's configstrings if ( ent->item->giType == IT_CLIPBOARD ) { if ( !ent->message ) { ent->s.density = G_FindConfigstringIndex( "clip_test", CS_CLIPBOARDS, MAX_CLIPBOARD_CONFIGSTRINGS, qtrue ); } else { ent->s.density = G_FindConfigstringIndex( ent->message, CS_CLIPBOARDS, MAX_CLIPBOARD_CONFIGSTRINGS, qtrue ); } ent->touch = Touch_Item; // no auto-pickup, only activate } else if ( ent->item->giType == IT_HOLDABLE ) { if ( ent->item->giTag >= HI_BOOK1 && ent->item->giTag <= HI_BOOK3 ) { G_FindConfigstringIndex( va( "hbook%d", ent->item->giTag - HI_BOOK1 ), CS_CLIPBOARDS, MAX_CLIPBOARD_CONFIGSTRINGS, qtrue ); } ent->touch = Touch_Item; // no auto-pickup, only activate } //----(SA) added if ( ent->item->giType == IT_TREASURE ) { ent->touch = Touch_Item; // no auto-pickup, only activate } //----(SA) end // using an item causes it to respawn ent->use = Use_Item; //----(SA) moved this up so it happens for suspended items too (and made it a function) G_SetAngle( ent, ent->s.angles ); if ( ent->spawnflags & 1 ) { // suspended G_SetOrigin( ent, ent->s.origin ); } else { VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 ); trap_Trace( &tr, ent->s.origin, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID ); if ( tr.startsolid ) { vec3_t temp; VectorCopy( ent->s.origin, temp ); temp[2] -= ITEM_RADIUS; VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 ); trap_Trace( &tr, temp, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID ); } #if 0 // drop to floor VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 ); trap_Trace( &tr, ent->s.origin, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID ); #endif if ( tr.startsolid ) { G_Printf( "FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos( ent->s.origin ) ); G_FreeEntity( ent ); return; } // allow to ride movers ent->s.groundEntityNum = tr.entityNum; G_SetOrigin( ent, tr.endpos ); } if ( ent->spawnflags & 2 ) { // spin ent->s.eFlags |= EF_SPINNING; } // team slaves and targeted items aren't present at start if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) { ent->flags |= FL_NODRAW; //ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; return; } // health/ammo can potentially be multi-stage (multiple use) if ( ent->item->giType == IT_HEALTH || ent->item->giType == IT_AMMO || ent->item->giType == IT_POWERUP ) { int i; // having alternate models defined in bg_misc.c for a health or ammo item specify it as "multi-stage" // TTimo left-hand operand of comma expression has no effect // initial line: for(i=0;i<4,ent->item->world_model[i];i++) {} for ( i = 0; i < 4 && ent->item->world_model[i] ; i++ ) {} ent->s.density = i - 1; // store number of stages in 'density' for client (most will have '1') } // powerups don't spawn in for a while if ( ent->item->giType == IT_POWERUP && g_gametype.integer != GT_SINGLE_PLAYER ) { float respawn; respawn = 45 + crandom() * 15; ent->flags |= FL_NODRAW; //ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; ent->nextthink = level.time + respawn * 1000; ent->think = RespawnItem; return; } trap_LinkEntity( ent ); }
/* ================= G_UpdateCharacter ================= */ void G_UpdateCharacter(gclient_t *client) { char infostring[MAX_INFO_STRING]; char *s; int characterIndex; bg_character_t *character; trap_GetUserinfo(client->ps.clientNum, infostring, sizeof(infostring)); s = Info_ValueForKey(infostring, "ch"); if (*s) { characterIndex = atoi(s); if (characterIndex < 0 || characterIndex >= MAX_CHARACTERS) { goto set_default_character; } if (client->pers.characterIndex != characterIndex) { client->pers.characterIndex = characterIndex; trap_GetConfigstring(CS_CHARACTERS + characterIndex, infostring, MAX_INFO_STRING); if (!(client->pers.character = BG_FindCharacter(infostring))) { // not found - create it (this should never happen as we should have everything precached) client->pers.character = BG_FindFreeCharacter(infostring); if (!client->pers.character) { goto set_default_character; } Q_strncpyz(client->pers.character->characterFile, infostring, sizeof(client->pers.character->characterFile)); if (!G_RegisterCharacter(infostring, client->pers.character)) { G_Printf(S_COLOR_YELLOW "WARNING: G_UpdateCharacter: failed to load character file '%s' for %s\n", infostring, client->pers.netname); goto set_default_character; } } // reset anims so client's dont freak out // xkan: this can only be done if the model really changed - otherwise, the // animation may get screwed up if we are in the middle of some animation // and we come into this function; // plus, also reset the timer so we can properly start the next animation client->ps.legsAnim = 0; client->ps.torsoAnim = 0; client->ps.legsTimer = 0; client->ps.torsoTimer = 0; } return; } set_default_character: // set default character character = BG_GetCharacter(client->sess.sessionTeam, client->sess.playerType); if (client->pers.character != character) { client->pers.characterIndex = -1; client->pers.character = character; client->ps.legsAnim = 0; client->ps.torsoAnim = 0; client->ps.legsTimer = 0; client->ps.torsoTimer = 0; } }
STATIC_INLINE void PrintEntityOverviewLine( gentity_t *entity ) { G_Printf( "%3i: %15s/" S_COLOR_CYAN "%-24s" S_COLOR_WHITE "%s%s\n", entity->s.number, Com_EntityTypeName( entity->s.eType ), entity->classname, entity->names[0] ? entity->names[0] : "", entity->names[1] ? " …" : ""); }
/* ================= ConsoleCommand ================= */ qboolean ConsoleCommand( void ) { char cmd[MAX_TOKEN_CHARS]; trap_Argv( 0, cmd, sizeof( cmd ) ); #ifdef SAVEGAME_SUPPORT if ( Q_stricmp( cmd, "savegame" ) == 0 ) { if ( g_gametype.integer != GT_SINGLE_PLAYER ) { return qtrue; } // don't allow a manual savegame command while we are waiting for the game to start/exit if ( g_reloading.integer ) { return qtrue; } if ( saveGamePending ) { return qtrue; } trap_Argv( 1, cmd, sizeof( cmd ) ); if ( strlen( cmd ) > 0 ) { // strip the extension if provided if ( strrchr( cmd, '.' ) ) { cmd[strrchr( cmd,'.' ) - cmd] = '\0'; } if ( !Q_stricmp( cmd, "current" ) ) { // beginning of map Com_Printf( "sorry, '%s' is a reserved savegame name. please use another name.\n", cmd ); return qtrue; } if ( G_SaveGame( cmd ) ) { trap_SendServerCommand( -1, "cp \"Game Saved\n\"" ); // deletedgame } else { G_Printf( "Unable to save game.\n" ); } } else { // need a name G_Printf( "syntax: savegame <name>\n" ); } return qtrue; } #endif // SAVEGAME_SUPPORT if ( Q_stricmp( cmd, "entitylist" ) == 0 ) { Svcmd_EntityList_f(); return qtrue; } if ( Q_stricmp( cmd, "forceteam" ) == 0 ) { Svcmd_ForceTeam_f(); return qtrue; } if ( Q_stricmp( cmd, "game_memory" ) == 0 ) { Svcmd_GameMem_f(); return qtrue; } /*if (Q_stricmp (cmd, "addbot") == 0) { Svcmd_AddBot_f(); return qtrue; } if (Q_stricmp (cmd, "removebot") == 0) { Svcmd_AddBot_f(); return qtrue; }*/ if ( Q_stricmp( cmd, "addip" ) == 0 ) { Svcmd_AddIP_f(); return qtrue; } if ( Q_stricmp( cmd, "removeip" ) == 0 ) { Svcmd_RemoveIP_f(); return qtrue; } if ( Q_stricmp( cmd, "listip" ) == 0 ) { trap_SendConsoleCommand( EXEC_INSERT, "g_banIPs\n" ); return qtrue; } if ( Q_stricmp( cmd, "listmaxlivesip" ) == 0 ) { PrintMaxLivesGUID(); return qtrue; } // NERVE - SMF if ( Q_stricmp( cmd, "start_match" ) == 0 ) { Svcmd_StartMatch_f(); return qtrue; } if ( Q_stricmp( cmd, "reset_match" ) == 0 ) { Svcmd_ResetMatch_f( qtrue, qtrue ); return qtrue; } if ( Q_stricmp( cmd, "swap_teams" ) == 0 ) { Svcmd_SwapTeams_f(); return qtrue; } if ( Q_stricmp( cmd, "shuffle_teams" ) == 0 ) { Svcmd_ShuffleTeams_f(); return qtrue; } // -NERVE - SMF if ( Q_stricmp( cmd, "makeReferee" ) == 0 ) { G_MakeReferee(); return qtrue; } if ( Q_stricmp( cmd, "removeReferee" ) == 0 ) { G_RemoveReferee(); return qtrue; } /*if (Q_stricmp (cmd, "mute") == 0) { G_MuteClient(); return qtrue; } if (Q_stricmp (cmd, "unmute") == 0) { G_UnMuteClient(); return qtrue; }*/ if ( Q_stricmp( cmd, "ban" ) == 0 ) { G_PlayerBan(); return qtrue; } if ( Q_stricmp( cmd, "campaign" ) == 0 ) { Svcmd_Campaign_f(); return qtrue; } if ( Q_stricmp( cmd, "listcampaigns" ) == 0 ) { Svcmd_ListCampaigns_f(); return qtrue; } if ( Q_stricmp( cmd, "spawnbot" ) == 0 ) { Svcmd_SpawnBot(); return qtrue; } // START - Mad Doc - TDF if ( Q_stricmp( cmd, "revive" ) == 0 ) { trap_Argv( 1, cmd, sizeof( cmd ) ); Svcmd_RevivePlayer( cmd ); return qtrue; } // END - Mad Doc - TDF // fretn - moved from engine if ( !Q_stricmp( cmd, "kick" ) ) { Svcmd_Kick_f(); return qtrue; } if ( !Q_stricmp( cmd, "clientkick" ) ) { Svcmd_KickNum_f(); return qtrue; } // -fretn if ( g_dedicated.integer ) { if ( !Q_stricmp( cmd, "say" ) ) { trap_SendServerCommand( -1, va( "cpm \"server: %s\n\"", ConcatArgs( 1 ) ) ); return qtrue; } // OSP - console also gets ref commands if ( !level.fLocalHost && Q_stricmp( cmd, "ref" ) == 0 ) { if ( !G_refCommandCheck( NULL, cmd ) ) { G_refHelp_cmd( NULL ); } return( qtrue ); } // everything else will also be printed as a say command // trap_SendServerCommand( -1, va("cpm \"server: %s\n\"", ConcatArgs(0) ) ); // prints to the console instead now return qfalse; } return qfalse; }
static c4clipedict_t *GClip_GetClipEdictForDeltaTime( int entNum, int deltaTime ) { static int index = 0; static c4clipedict_t clipEnts[8]; static c4clipedict_t *clipent; static c4clipedict_t clipentNewer; // for interpolation c4frame_t *cframe = NULL; unsigned int backTime, cframenum, bf, i; edict_t *ent = game.edicts + entNum; // pick one of the 8 slots to prevent overwritings clipent = &clipEnts[index]; index = ( index + 1 )&7; if( !entNum || deltaTime >= 0 || !g_antilag->integer ) { // current time entity clipent->r = ent->r; clipent->s = ent->s; return clipent; } if( !ent->r.inuse || ent->r.solid == SOLID_NOT || ( ent->r.solid == SOLID_TRIGGER && !(entNum >= 1 && entNum <= gs.maxclients) ) ) { clipent->r = ent->r; clipent->s = ent->s; return clipent; } // clamp delta time inside the backed up limits backTime = abs( deltaTime ); if( g_antilag_maxtimedelta->integer ) { if( g_antilag_maxtimedelta->integer < 0 ) trap_Cvar_SetValue( "g_antilag_maxtimedelta", abs( g_antilag_maxtimedelta->integer ) ); if( backTime > (unsigned int)g_antilag_maxtimedelta->integer ) backTime = (unsigned int)g_antilag_maxtimedelta->integer; } // find the first snap with timestamp < than realtime - backtime cframenum = sv_collisionFrameNum; for( bf = 1; bf < CFRAME_UPDATE_BACKUP && bf < sv_collisionFrameNum; bf++ ) // never overpass limits { cframe = &sv_collisionframes[( cframenum-bf ) & CFRAME_UPDATE_MASK]; // if solid has changed, we can't keep moving backwards if( ent->r.solid != cframe->clipEdicts[entNum].r.solid || ent->r.inuse != cframe->clipEdicts[entNum].r.inuse ) { bf--; if( bf == 0 ) { // we can't step back from first cframe = NULL; } else { cframe = &sv_collisionframes[( cframenum-bf ) & CFRAME_UPDATE_MASK]; } break; } if( game.serverTime >= cframe->timestamp + backTime ) break; } if( !cframe ) { // current time entity clipent->r = ent->r; clipent->s = ent->s; return clipent; } // setup with older for the data that is not interpolated *clipent = cframe->clipEdicts[entNum]; // if we found an older than desired backtime frame, interpolate to find a more precise position. if( game.serverTime > cframe->timestamp+backTime ) { float lerpFrac; if( bf == 1 ) { // interpolate from 1st backed up to current lerpFrac = (float)( ( game.serverTime - backTime ) - cframe->timestamp ) / (float)( game.serverTime - cframe->timestamp ); clipentNewer.r = ent->r; clipentNewer.s = ent->s; } else { // interpolate between 2 backed up c4frame_t *cframeNewer = &sv_collisionframes[( cframenum-( bf-1 ) ) & CFRAME_UPDATE_MASK]; lerpFrac = (float)( ( game.serverTime - backTime ) - cframe->timestamp ) / (float)( cframeNewer->timestamp - cframe->timestamp ); clipentNewer = cframeNewer->clipEdicts[entNum]; } #if 0 G_Printf( "backTime:%i cframeBackTime:%i backFrames:%i lerfrac:%f\n", backTime, game.serverTime - cframe->timestamp, backframes, lerpFrac ); #endif // interpolate VectorLerp( clipent->s.origin, lerpFrac, clipentNewer.s.origin, clipent->s.origin ); VectorLerp( clipent->r.mins, lerpFrac, clipentNewer.r.mins, clipent->r.mins ); VectorLerp( clipent->r.maxs, lerpFrac, clipentNewer.r.maxs, clipent->r.maxs ); for( i = 0; i < 3; i++ ) clipent->s.angles[i] = LerpAngle( clipent->s.angles[i], clipentNewer.s.angles[i], lerpFrac ); } #if 0 G_Printf( "backTime:%i cframeBackTime:%i backFrames:%i\n", backTime, game.serverTime - cframe->timestamp, backframes ); #endif // back time entity return clipent; }
// ************** PLAYERS // // Show client info void G_players_cmd( gentity_t *ent, unsigned int dwCommand, qboolean fValue ) { int i, idnum, max_rate, cnt = 0, tteam; int user_rate, user_snaps; gclient_t *cl; gentity_t *cl_ent; char n2[MAX_NETNAME], ready[16], ref[16], rate[256]; const char *s; const char* tc; const char* coach; char userinfo[MAX_INFO_STRING]; if ( g_gamestate.integer == GS_PLAYING ) { if ( ent ) { CP( "print \"\n^3 ID^1 : ^3Player Nudge Rate MaxPkts Snaps\n\"" ); CP( "print \"^1-----------------------------------------------------------^7\n\"" ); } else { G_Printf( " ID : Player Nudge Rate MaxPkts Snaps\n" ); G_Printf( "-----------------------------------------------------------\n" ); } } else { if ( ent ) { CP( "print \"\n^3Status^1 : ^3ID^1 : ^3Player Nudge Rate MaxPkts Snaps\n\"" ); CP( "print \"^1---------------------------------------------------------------------^7\n\"" ); } else { G_Printf( "Status : ID : Player Nudge Rate MaxPkts Snaps\n" ); G_Printf( "---------------------------------------------------------------------\n" ); } } max_rate = trap_Cvar_VariableIntegerValue( "sv_maxrate" ); for ( i = 0; i < level.numConnectedClients; i++ ) { idnum = level.sortedClients[i]; //level.sortedNames[i]; cl = &level.clients[idnum]; cl_ent = g_entities + idnum; SanitizeString( cl->pers.netname, n2, qtrue ); n2[26] = 0; ref[0] = 0; ready[0] = 0; // Rate info if ( cl_ent->r.svFlags & SVF_BOT ) { strcpy( rate, va( "%s%s%s%s", "[BOT]", " -----", " --", " --" ) ); } else if ( cl->pers.connected == CON_CONNECTING ) { strcpy( rate, va( "%s", "^3>>> CONNECTING <<<" ) ); } else { trap_GetUserinfo( idnum, userinfo, sizeof( userinfo ) ); s = Info_ValueForKey( userinfo, "rate" ); user_rate = ( max_rate > 0 && atoi( s ) > max_rate ) ? max_rate : atoi( s ); s = Info_ValueForKey( userinfo, "snaps" ); user_snaps = atoi( s ); strcpy( rate, va( "%5d%6d%9d%7d", cl->pers.clientTimeNudge, user_rate, cl->pers.clientMaxPackets, user_snaps ) ); } if ( g_gamestate.integer != GS_PLAYING ) { if ( cl->sess.sessionTeam == TEAM_SPECTATOR || cl->pers.connected == CON_CONNECTING ) { strcpy( ready, ( ( ent ) ? "^5--------^1 :" : "-------- :" ) ); } else if ( cl->pers.ready || ( g_entities[idnum].r.svFlags & SVF_BOT ) ) { strcpy( ready, ( ( ent ) ? "^3(READY)^1 :" : "(READY) :" ) ); } else { strcpy( ready, ( ( ent ) ? "NOTREADY^1 :" : "NOTREADY :" ) ); } } if ( cl->sess.referee ) { strcpy( ref, "REF" ); } if ( cl->sess.coach_team ) { tteam = cl->sess.coach_team; coach = ( ent ) ? "^3C" : "C"; } else { tteam = cl->sess.sessionTeam; coach = " "; } tc = ( ent ) ? "^7 " : " "; if ( g_gametype.integer >= GT_WOLF ) { if ( tteam == TEAM_AXIS ) { tc = ( ent ) ? "^1X^7" : "X"; } if ( tteam == TEAM_ALLIES ) { tc = ( ent ) ? "^4L^7" : "L"; } } if ( ent ) { CP( va( "print \"%s%s%2d%s^1:%s %-26s^7%s ^3%s\n\"", ready, tc, idnum, coach, ( ( ref[0] ) ? "^3" : "^7" ), n2, rate, ref ) ); } else { G_Printf( "%s%s%2d%s: %-26s%s %s\n", ready, tc, idnum, coach, n2, rate, ref );} cnt++; } if ( ent ) { CP( va( "print \"\n^3%2d^7 total players\n\n\"", cnt ) ); } else { G_Printf( "\n%2d total players\n\n", cnt );} // Team speclock info if ( g_gametype.integer >= GT_WOLF ) { for ( i = TEAM_AXIS; i <= TEAM_ALLIES; i++ ) { if ( teamInfo[i].spec_lock ) { if ( ent ) { CP( va( "print \"** %s team is speclocked.\n\"", aTeams[i] ) ); } else { G_Printf( "** %s team is speclocked.\n", aTeams[i] );} } } } }