From ac4155327b0a0f8d0105a22c57d28ca0f7252e27 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 7 Jan 2019 02:21:48 -0600 Subject: [PATCH] Update to ioquake3 2018-12-21 part 4 (CGame/Game code) --- code/cgame/cg_draw.c | 17 +-- code/cgame/cg_drawtools.c | 2 +- code/cgame/cg_local.h | 10 +- code/cgame/cg_main.c | 2 +- code/cgame/cg_particles.c | 4 - code/cgame/cg_players.c | 18 +-- code/cgame/cg_playerstate.c | 3 + code/cgame/cg_predict.c | 2 + code/cgame/cg_public.h | 2 +- code/cgame/cg_scoreboard.c | 2 +- code/cgame/cg_servercmds.c | 24 ++-- code/cgame/cg_snapshot.c | 12 +- code/cgame/cg_view.c | 12 +- code/cgame/cg_weapons.c | 2 +- code/game/ai_chat.c | 48 ++----- code/game/ai_cmd.c | 20 +-- code/game/ai_dmnet.c | 5 +- code/game/ai_dmq3.c | 115 ++++++++------- code/game/ai_dmq3.h | 2 +- code/game/ai_main.c | 38 +++-- code/game/ai_team.c | 25 ++-- code/game/bg_lib.c | 8 +- code/game/bg_pmove.c | 4 +- code/game/bg_public.h | 2 +- code/game/bg_slidemove.c | 22 ++- code/game/g_active.c | 4 +- code/game/g_bot.c | 278 +++++++++++++++++++++--------------- code/game/g_client.c | 51 +++---- code/game/g_cmds.c | 261 +++++++++++++++++++++++---------- code/game/g_combat.c | 2 +- code/game/g_items.c | 2 +- code/game/g_local.h | 4 +- code/game/g_main.c | 34 ++++- code/game/g_mover.c | 8 +- code/game/g_session.c | 55 +++---- code/game/g_spawn.c | 2 +- code/game/g_svcmds.c | 4 +- code/game/g_target.c | 2 +- code/game/g_team.c | 27 +--- code/game/g_trigger.c | 4 +- code/game/g_utils.c | 10 +- code/game/g_weapon.c | 13 +- 42 files changed, 661 insertions(+), 501 deletions(-) diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c index 437f4653..e7470b35 100644 --- a/code/cgame/cg_draw.c +++ b/code/cgame/cg_draw.c @@ -1283,12 +1283,18 @@ static float CG_DrawPowerups(float y) if (!ps->powerups[i]) { continue; } - t = ps->powerups[i] - cg.time; - // ZOID--don't draw if the power up has unlimited time (999 seconds) + + // ZOID--don't draw if the power up has unlimited time // This is true of the CTF flags - if (t < 0 || t > 999000) { + if (ps->powerups[i] == INT_MAX) { continue; } + + t = ps->powerups[i] - cg.time; + if (t <= 0) { + continue; + } + // insert into the list for (j = 0; j < active; j++) { if (sortedTime[j] >= t) { @@ -2722,11 +2728,6 @@ static void CG_Draw2D(void) CG_DrawWeaponStats(); } -static void CG_DrawTourneyScoreboard( void ) -{ - CG_DrawOldTourneyScoreboard(); -} - /* ===================== CG_DrawDamageBlend diff --git a/code/cgame/cg_drawtools.c b/code/cgame/cg_drawtools.c index 1e5ec2ea..8f9e4a20 100644 --- a/code/cgame/cg_drawtools.c +++ b/code/cgame/cg_drawtools.c @@ -131,7 +131,7 @@ void CG_DrawRect(float x, float y, float width, float height, float size, const trap_R_SetColor(color); CG_DrawTopBottom(x, y, width, height, size); - CG_DrawSides(x, y, width, height, size); + CG_DrawSides(x, y + size, width, height - size * 2, size); trap_R_SetColor(NULL); } diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index f6ac3d6b..23e8f7a9 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -641,7 +641,7 @@ typedef struct centity_s { //====================================================================== // local entities are created as a result of events or predicted actions, -// and live independantly from all server transmitted entities +// and live independently from all server transmitted entities typedef struct markPoly_s { struct markPoly_s *prevMark, *nextMark; @@ -942,7 +942,7 @@ typedef struct { qboolean loading; // don't defer players at initial startup qboolean intermissionStarted; // don't play voice rewards, because game will end shortly - // there are only one or two snapshot_t that are relevent at a time + // there are only one or two snapshot_t that are relevant at a time int latestSnapshotNum; // the number of snapshots the client system has received int latestSnapshotTime; // the time from latestSnapshotNum, so we don't need to read the snapshot yet @@ -1101,7 +1101,7 @@ typedef struct { int itemPickup; int itemPickupTime; - int itemPickupBlendTime; // the pulse around the crosshair is timed seperately + int itemPickupBlendTime; // the pulse around the crosshair is timed separately int weaponSelectTime; int weaponAnimation; @@ -2385,7 +2385,7 @@ void CG_DrawInformation(void); // cg_scoreboard.c // qboolean CG_DrawOldScoreboard(void); -void CG_DrawOldTourneyScoreboard(void); +void CG_DrawTourneyScoreboard(void); // // cg_players.c @@ -2555,7 +2555,7 @@ void trap_GetGlconfig(glconfig_t * glconfig); void trap_GetGameState(gameState_t * gamestate); // cgame will poll each frame to see if a newer snapshot has arrived -// that it is interested in. The time is returned seperately so that +// that it is interested in. The time is returned separately so that // snapshot latency can be calculated. void trap_GetCurrentSnapshotNumber(int *snapshotNumber, int *serverTime); diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c index ff48b07a..337a6994 100644 --- a/code/cgame/cg_main.c +++ b/code/cgame/cg_main.c @@ -1372,7 +1372,7 @@ static void CG_RegisterItemSounds(int itemNum) if (item->pickup_sound) { trap_S_RegisterSound(item->pickup_sound, qfalse); } - // parse the space seperated precache string for other media + // parse the space separated precache string for other media s = item->sounds; if (!s || !s[0]) return; diff --git a/code/cgame/cg_particles.c b/code/cgame/cg_particles.c index b0eaebc1..4d3efa4c 100644 --- a/code/cgame/cg_particles.c +++ b/code/cgame/cg_particles.c @@ -947,10 +947,6 @@ void CG_ParticleSnowFlurry(qhandle_t pshader, centity_t * cent) VectorCopy(cent->currentState.origin, p->org); - p->org[0] = p->org[0]; - p->org[1] = p->org[1]; - p->org[2] = p->org[2]; - p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; diff --git a/code/cgame/cg_players.c b/code/cgame/cg_players.c index 082de6a9..bdb9b7c9 100644 --- a/code/cgame/cg_players.c +++ b/code/cgame/cg_players.c @@ -276,12 +276,12 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t * ci) while (1) { prev = text_p; // so we can unget token = COM_Parse(&text_p); - if (!token) { + if (!token[0]) { break; } if (!Q_stricmp(token, "footsteps")) { token = COM_Parse(&text_p); - if (!token) { + if (!token[0]) { break; } if (!Q_stricmp(token, "default") || !Q_stricmp(token, "normal")) { @@ -306,7 +306,7 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t * ci) } else if (!Q_stricmp(token, "headoffset")) { for (i = 0; i < 3; i++) { token = COM_Parse(&text_p); - if (!token) { + if (!token[0]) { break; } ci->headOffset[i] = atof(token); @@ -314,7 +314,7 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t * ci) continue; } else if (!Q_stricmp(token, "sex")) { token = COM_Parse(&text_p); - if (!token) { + if (!token[0]) { break; } if (token[0] == 'f' || token[0] == 'F') { @@ -344,7 +344,7 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t * ci) for (i = 0; i < MAX_ANIMATIONS; i++) { token = COM_Parse(&text_p); - if (!*token) { + if (!token[0]) { if (i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE) { animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame; animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp; @@ -367,7 +367,7 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t * ci) } token = COM_Parse(&text_p); - if (!*token) { + if (!token[0]) { break; } animations[i].numFrames = atoi(token); @@ -381,13 +381,13 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t * ci) } token = COM_Parse(&text_p); - if (!*token) { + if (!token[0]) { break; } animations[i].loopFrames = atoi(token); token = COM_Parse(&text_p); - if (!*token) { + if (!token[0]) { break; } fps = atof(token); @@ -1719,7 +1719,7 @@ static void CG_AddPainTwitch(centity_t * cent, vec3_t torsoAngles) =============== CG_PlayerAngles -Handles seperate torso motion +Handles separate torso motion legs pivot based on direction of movement diff --git a/code/cgame/cg_playerstate.c b/code/cgame/cg_playerstate.c index df9e69e5..7759f100 100644 --- a/code/cgame/cg_playerstate.c +++ b/code/cgame/cg_playerstate.c @@ -83,6 +83,9 @@ void CG_CheckAmmo(void) //if ( ! ( weapons & ( 1 << i ) ) ) { //continue; //} + //if ( cg.snap->ps.ammo[i] < 0 ) { + //continue; + //} //Blaze: Dont need this /* switch ( i ) { diff --git a/code/cgame/cg_predict.c b/code/cgame/cg_predict.c index f870d4f4..baea8b69 100644 --- a/code/cgame/cg_predict.c +++ b/code/cgame/cg_predict.c @@ -670,8 +670,10 @@ void CG_PredictPlayerState(void) if (pmove_msec.integer < 8) { trap_Cvar_Set("pmove_msec", "8"); + trap_Cvar_Update(&pmove_msec); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); + trap_Cvar_Update(&pmove_msec); } cg_pmove.pmove_fixed = pmove_fixed.integer; // | cg_pmove_fixed.integer; diff --git a/code/cgame/cg_public.h b/code/cgame/cg_public.h index 34510511..b4db1509 100644 --- a/code/cgame/cg_public.h +++ b/code/cgame/cg_public.h @@ -206,7 +206,7 @@ typedef enum { CG_SHUTDOWN, // void (*CG_Shutdown)( void ); - // oportunity to flush and close any open files + // opportunity to flush and close any open files CG_CONSOLE_COMMAND, // qboolean (*CG_ConsoleCommand)( void ); diff --git a/code/cgame/cg_scoreboard.c b/code/cgame/cg_scoreboard.c index 76c306fb..b37a338d 100644 --- a/code/cgame/cg_scoreboard.c +++ b/code/cgame/cg_scoreboard.c @@ -791,7 +791,7 @@ Draw the oversize scoreboard for tournements ================= */ -void CG_DrawOldTourneyScoreboard(void) +void CG_DrawTourneyScoreboard(void) { const char *s; vec4_t color; diff --git a/code/cgame/cg_servercmds.c b/code/cgame/cg_servercmds.c index f18e76f3..e3c7cec2 100644 --- a/code/cgame/cg_servercmds.c +++ b/code/cgame/cg_servercmds.c @@ -222,7 +222,7 @@ // Copyright (C) 1999-2000 Id Software, Inc. // // cg_servercmds.c -- reliably sequenced text commands sent by the server -// these are processed at snapshot transition time, so there will definately +// these are processed at snapshot transition time, so there will definitely // be a valid snapshot this frame #include "cg_local.h" @@ -899,7 +899,7 @@ int CG_ParseVoiceChats(const char *filename, voiceChatList_t * voiceChatList, in voiceChats[i].id[0] = 0; } token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if (!token[0]) { return qtrue; } if (!Q_stricmp(token, "female")) { @@ -916,7 +916,7 @@ int CG_ParseVoiceChats(const char *filename, voiceChatList_t * voiceChatList, in voiceChatList->numVoiceChats = 0; while (1) { token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if (!token[0]) { return qtrue; } Com_sprintf(voiceChats[voiceChatList->numVoiceChats].id, @@ -929,7 +929,7 @@ int CG_ParseVoiceChats(const char *filename, voiceChatList_t * voiceChatList, in voiceChats[voiceChatList->numVoiceChats].numSounds = 0; while (1) { token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if (!token[0]) { return qtrue; } if (!Q_stricmp(token, "}")) @@ -938,7 +938,7 @@ int CG_ParseVoiceChats(const char *filename, voiceChatList_t * voiceChatList, in voiceChats[voiceChatList->numVoiceChats].sounds[voiceChats[voiceChatList->numVoiceChats]. numSounds] = sound; token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if (!token[0]) { return qtrue; } Com_sprintf(voiceChats[voiceChatList->numVoiceChats]. @@ -1011,7 +1011,7 @@ int CG_HeadModelVoiceChats(char *filename) p = &ptr; token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if (!token[0]) { return -1; } @@ -1561,12 +1561,14 @@ static void CG_ServerCommand(void) } if (!strcmp(cmd, "chat")) { - if (!cg_teamChatsOnly.integer) { - trap_S_StartLocalSound(cgs.media.talkSound, CHAN_LOCAL_SOUND); - Q_strncpyz(text, CG_Argv(1), sizeof(text)); - CG_RemoveChatEscapeChar(text); - CG_AddMessage(text); + if (cgs.gametype >= GT_TEAM && cg_teamChatsOnly.integer) { + return; } + + trap_S_StartLocalSound(cgs.media.talkSound, CHAN_LOCAL_SOUND); + Q_strncpyz(text, CG_Argv(1), sizeof(text)); + CG_RemoveChatEscapeChar(text); + CG_AddMessage(text); return; } diff --git a/code/cgame/cg_snapshot.c b/code/cgame/cg_snapshot.c index 09b98dbb..1400da6b 100644 --- a/code/cgame/cg_snapshot.c +++ b/code/cgame/cg_snapshot.c @@ -121,11 +121,8 @@ static void CG_CheckVidRestart(void) ================== CG_SetInitialSnapshot -This will only happen on the very first snapshot, or -on tourney restarts. All other times will use -CG_TransitionSnapshot instead. - -FIXME: Also called by map_restart? +This will only happen on the very first snapshot. +All other times will use CG_TransitionSnapshot instead. ================== */ void CG_SetInitialSnapshot(snapshot_t * snap) @@ -189,9 +186,8 @@ static void CG_TransitionSnapshot(void) // execute any server string commands before transitioning entities CG_ExecuteNewServerCommands(cg.nextSnap->serverCommandSequence); - // if we had a map_restart, set everthing with initial - if (!cg.snap) { - return; + // if we had a map_restart, set everything with initial + if (cg.mapRestart) { } // clear the currentValid flag for all entities in the existing snapshot diff --git a/code/cgame/cg_view.c b/code/cgame/cg_view.c index e316b8a1..3a9ad0fd 100644 --- a/code/cgame/cg_view.c +++ b/code/cgame/cg_view.c @@ -155,7 +155,7 @@ Testmodel will create a fake entity 100 units in front of the current view position, directly facing the viewer. It will remain immobile, so you can move around it to view it from different angles. -Testgun will cause the model to follow the player around and supress the real +Testgun will cause the model to follow the player around and suppress the real view weapon model. The default frame 0 of most guns is completely off screen, so you will probably have to cycle a couple frames to see it. @@ -185,6 +185,7 @@ void CG_TestModel_f(void) { vec3_t angles; + cg.testGun = qfalse; memset(&cg.testModelEntity, 0, sizeof(cg.testModelEntity)); if (trap_Argc() < 2) { return; @@ -210,7 +211,6 @@ void CG_TestModel_f(void) angles[ROLL] = 0; AnglesToAxis(angles, cg.testModelEntity.axis); - cg.testGun = qfalse; } /* @@ -223,6 +223,11 @@ Replaces the current view weapon with the given model void CG_TestGun_f(void) { CG_TestModel_f(); + + if ( !cg.testModelEntity.hModel ) { + return; + } + cg.testGun = qtrue; cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON; } @@ -1341,7 +1346,8 @@ void CG_DrawActiveFrame(int serverTime, stereoFrame_t stereoView, qboolean demoP // decide on third person view // Elder: remove third-person death rendering - cg.renderingThirdPerson = cg_thirdPerson.integer; //|| (cg.snap->ps.stats[STAT_HEALTH] <= 0); + cg.renderingThirdPerson = cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR + && (cg_thirdPerson.integer); //|| (cg.snap->ps.stats[STAT_HEALTH] <= 0)); // build cg.refdef inwater = CG_CalcViewValues(); diff --git a/code/cgame/cg_weapons.c b/code/cgame/cg_weapons.c index 1ebcdbd9..6ca14bcb 100644 --- a/code/cgame/cg_weapons.c +++ b/code/cgame/cg_weapons.c @@ -1444,7 +1444,7 @@ void CG_AddPlayerWeapon( refEntity_t * parent, playerState_t * ps, centity_t * c nonPredictedCent = &cg_entities[ cent->currentState.clientNum ]; // if the index of the nonPredictedCent is not the same as the clientNum - // then this is a fake player (like on teh single player podiums), so + // then this is a fake player (like on the single player podiums), so // go ahead and use the cent if ((nonPredictedCent - cg_entities) != cent->currentState.clientNum) { nonPredictedCent = cent; diff --git a/code/game/ai_chat.c b/code/game/ai_chat.c index be584222..d7cd53bc 100644 --- a/code/game/ai_chat.c +++ b/code/game/ai_chat.c @@ -77,13 +77,9 @@ int BotNumActivePlayers(void) { int i, num; char buf[MAX_INFO_STRING]; - static int maxclients; - - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); num = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) @@ -106,14 +102,10 @@ int BotIsFirstInRankings(bot_state_t * bs) { int i, score; char buf[MAX_INFO_STRING]; - static int maxclients; playerState_t ps; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - score = bs->cur_ps.persistant[PERS_SCORE]; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) @@ -122,8 +114,7 @@ int BotIsFirstInRankings(bot_state_t * bs) if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // - BotAI_GetClientState(i, &ps); - if (score < ps.persistant[PERS_SCORE]) + if (BotAI_GetClientState(i, &ps) && score < ps.persistant[PERS_SCORE]) return qfalse; } return qtrue; @@ -138,14 +129,10 @@ int BotIsLastInRankings(bot_state_t * bs) { int i, score; char buf[MAX_INFO_STRING]; - static int maxclients; playerState_t ps; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - score = bs->cur_ps.persistant[PERS_SCORE]; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) @@ -154,8 +141,7 @@ int BotIsLastInRankings(bot_state_t * bs) if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // - BotAI_GetClientState(i, &ps); - if (score > ps.persistant[PERS_SCORE]) + if (BotAI_GetClientState(i, &ps) && score > ps.persistant[PERS_SCORE]) return qfalse; } return qtrue; @@ -171,15 +157,11 @@ char *BotFirstClientInRankings(void) int i, bestscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; - static int maxclients; playerState_t ps; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - bestscore = -999999; bestclient = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) @@ -188,8 +170,7 @@ char *BotFirstClientInRankings(void) if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // - BotAI_GetClientState(i, &ps); - if (ps.persistant[PERS_SCORE] > bestscore) { + if (BotAI_GetClientState(i, &ps) && ps.persistant[PERS_SCORE] > bestscore) { bestscore = ps.persistant[PERS_SCORE]; bestclient = i; } @@ -208,15 +189,11 @@ char *BotLastClientInRankings(void) int i, worstscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; - static int maxclients; playerState_t ps; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - worstscore = 999999; bestclient = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) @@ -225,8 +202,7 @@ char *BotLastClientInRankings(void) if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // - BotAI_GetClientState(i, &ps); - if (ps.persistant[PERS_SCORE] < worstscore) { + if (BotAI_GetClientState(i, &ps) && ps.persistant[PERS_SCORE] < worstscore) { worstscore = ps.persistant[PERS_SCORE]; bestclient = i; } @@ -245,15 +221,11 @@ char *BotRandomOpponentName(bot_state_t * bs) int i, count; char buf[MAX_INFO_STRING]; int opponents[MAX_CLIENTS], numopponents; - static int maxclients; static char name[32]; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - numopponents = 0; opponents[0] = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // diff --git a/code/game/ai_cmd.c b/code/game/ai_cmd.c index dfb36315..b04a6c53 100644 --- a/code/game/ai_cmd.c +++ b/code/game/ai_cmd.c @@ -256,16 +256,13 @@ int FindClientByName(char *name) { int i; char buf[MAX_INFO_STRING]; - static int maxclients; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { ClientName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) return i; } - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { ClientName(i, buf, sizeof(buf)); if (stristr(buf, name)) return i; @@ -282,18 +279,15 @@ int FindEnemyByName(bot_state_t * bs, char *name) { int i; char buf[MAX_INFO_STRING]; - static int maxclients; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (BotSameTeam(bs, i)) continue; ClientName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) return i; } - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (BotSameTeam(bs, i)) continue; ClientName(i, buf, sizeof(buf)); @@ -312,13 +306,9 @@ int NumPlayersOnSameTeam(bot_state_t * bs) { int i, num; char buf[MAX_INFO_STRING]; - static int maxclients; - - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); num = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, MAX_INFO_STRING); if (strlen(buf)) { if (BotSameTeam(bs, i + 1)) diff --git a/code/game/ai_dmnet.c b/code/game/ai_dmnet.c index e02560ef..6db01cae 100644 --- a/code/game/ai_dmnet.c +++ b/code/game/ai_dmnet.c @@ -657,7 +657,7 @@ int BotGetItemLongTermGoal(bot_state_t * bs, int tfl, bot_goal_t * goal) ================== BotGetLongTermGoal -we could also create a seperate AI node for every long term goal type +we could also create a separate AI node for every long term goal type however this saves us a lot of code ================== */ @@ -917,11 +917,10 @@ int BotGetLongTermGoal(bot_state_t * bs, int tfl, int retreat, bot_goal_t * goal bs->teammessage_time = 0; } // - if (bs->lastkilledplayer == bs->teamgoal.entitynum) { + if (bs->killedenemy_time > bs->teamgoal_time - TEAM_KILL_SOMEONE && bs->lastkilledplayer == bs->teamgoal.entitynum) { EasyClientName(bs->teamgoal.entitynum, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "kill_done", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); - bs->lastkilledplayer = -1; bs->ltgtype = 0; } // diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index b9b09ae1..0dda15fb 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -237,7 +237,6 @@ bot_waypoint_t *botai_freewaypoints; //NOTE: not using a cvars which can be updated because the game should be reloaded anyway int gametype; //game type -int maxclients; //maximum number of clients vmCvar_t bot_grapple; vmCvar_t bot_rocketjump; @@ -755,7 +754,9 @@ qboolean EntityIsDead(aas_entityinfo_t * entinfo) if (entinfo->number >= 0 && entinfo->number < MAX_CLIENTS) { //retrieve the current client state - BotAI_GetClientState(entinfo->number, &ps); + if (!BotAI_GetClientState(entinfo->number, &ps)) { + return qfalse; + } //Makro - added health check if (ps.pm_type != PM_NORMAL || ps.stats[STAT_HEALTH] <= 0) return qtrue; @@ -1393,11 +1394,8 @@ int ClientFromName(char *name) { int i; char buf[MAX_INFO_STRING]; - static int maxclients; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); Q_CleanStr(buf); if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) @@ -1415,11 +1413,8 @@ int ClientOnSameTeamFromName(bot_state_t * bs, char *name) { int i; char buf[MAX_INFO_STRING]; - static int maxclients; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (!BotSameTeam(bs, i)) continue; trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); @@ -2699,7 +2694,7 @@ bot_moveresult_t BotAttackMove(bot_state_t * bs, int tfl) bs->flags ^= BFL_STRAFERIGHT; bs->attackstrafe_time = 0; } - //bot couldn't do any usefull movement + //bot couldn't do any useful movement // bs->attackchase_time = AAS_Time() + 6; return moveresult; } @@ -2779,8 +2774,12 @@ float BotEntityVisible(int viewer, vec3_t eye, vec3_t viewangles, float fov, int aas_entityinfo_t entinfo; vec3_t dir, entangles, start, end, middle; - //calculate middle of bounding box BotEntityInfo(ent, &entinfo); + if (!entinfo.valid) { + return 0; + } + + //calculate middle of bounding box VectorAdd(entinfo.mins, entinfo.maxs, middle); VectorScale(middle, 0.5, middle); VectorAdd(entinfo.origin, middle, middle); @@ -2903,13 +2902,17 @@ int BotFindEnemy(bot_state_t * bs, int curenemy) } else { cursquaredist = 0; } - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; //if it's the current enemy if (i == curenemy) continue; + //if the enemy has targeting disabled + if (g_entities[i].flags & FL_NOTARGET) { + continue; + } // BotEntityInfo(i, &entinfo); // @@ -2922,10 +2925,6 @@ int BotFindEnemy(bot_state_t * bs, int curenemy) if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { continue; } -// JBravo: unlagged - if (g_entities[i].flags & FL_NOTARGET) { - continue; - } //if not an easy fragger don't shoot at chatting players if (easyfragger < 0.5 && EntityIsChatting(&entinfo)) @@ -2999,7 +2998,7 @@ int BotTeamFlagCarrierVisible(bot_state_t * bs) float vis; aas_entityinfo_t entinfo; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -3033,7 +3032,7 @@ int BotTeamFlagCarrier(bot_state_t * bs) int i; aas_entityinfo_t entinfo; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -3064,7 +3063,7 @@ int BotEnemyFlagCarrierVisible(bot_state_t * bs) float vis; aas_entityinfo_t entinfo; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -3104,7 +3103,7 @@ void BotVisibleTeamMatesAndEnemies(bot_state_t * bs, int *teammates, int *enemie *teammates = 0; if (enemies) *enemies = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -3591,24 +3590,29 @@ void BotMapScripts(bot_state_t * bs) strncpy(mapname, Info_ValueForKey(info, "mapname"), sizeof(mapname) - 1); mapname[sizeof(mapname) - 1] = '\0'; - if (!Q_stricmp(mapname, "q3tourney6")) { - vec3_t mins = { 700, 204, 672 }, maxs = { - 964, 468, 680}; + if (!Q_stricmp(mapname, "q3tourney6") || !Q_stricmp(mapname, "q3tourney6_ctf") || !Q_stricmp(mapname, "mpq3tourney6")) { + vec3_t mins = { 694, 200, 480 }, maxs = { + 968, 472, 680 }; vec3_t buttonorg = { 304, 352, 920 }; //NOTE: NEVER use the func_bobbing in q3tourney6 bs->tfl &= ~TFL_FUNCBOB; - //if the bot is below the bounding box + //crush area is higher in mpq3tourney6 + if (!Q_stricmp(mapname, "mpq3tourney6")) { + mins[2] += 64; + maxs[2] += 64; + } + //if the bot is in the bounding box of the crush area if (bs->origin[0] > mins[0] && bs->origin[0] < maxs[0]) { if (bs->origin[1] > mins[1] && bs->origin[1] < maxs[1]) { - if (bs->origin[2] < mins[2]) { + if (bs->origin[2] > mins[2] && bs->origin[2] < maxs[2]) { return; } } } shootbutton = qfalse; - //if an enemy is below this bounding box then shoot the button - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + //if an enemy is in the bounding box then shoot the button + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; @@ -3623,12 +3627,12 @@ void BotMapScripts(bot_state_t * bs) // if (entinfo.origin[0] > mins[0] && entinfo.origin[0] < maxs[0]) { if (entinfo.origin[1] > mins[1] && entinfo.origin[1] < maxs[1]) { - if (entinfo.origin[2] < mins[2]) { + if (entinfo.origin[2] > mins[2] && entinfo.origin[2] < maxs[2]) { //if there's a team mate below the crusher if (BotSameTeam(bs, i)) { shootbutton = qfalse; break; - } else { + } else if (bs->enemy == i) { shootbutton = qtrue; } } @@ -3651,9 +3655,6 @@ void BotMapScripts(bot_state_t * bs) BotAttack(bs); } } - } else if (!Q_stricmp(mapname, "mpq3tourney6")) { - //NOTE: NEVER use the func_bobbing in mpq3tourney6 - bs->tfl &= ~TFL_FUNCBOB; } } @@ -3744,7 +3745,6 @@ int BotFuncButtonActivateGoal(bot_state_t * bs, int bspent, bot_activategoal_t * modelindex = atoi(model + 1); if (!modelindex) return qfalse; - VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); //get the lip of the button trap_AAS_FloatForBSPEpairKey(bspent, "lip", &lip); @@ -3968,7 +3968,7 @@ int BotFuncDoorRotatingActivateGoal(bot_state_t * bs, int bspent, bot_activatego { int modelindex, entitynum; char model[MAX_INFO_STRING]; - vec3_t mins, maxs, origin, angles; + vec3_t mins, maxs, origin; trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); if (!*model) @@ -3976,7 +3976,6 @@ int BotFuncDoorRotatingActivateGoal(bot_state_t * bs, int bspent, bot_activatego modelindex = atoi(model + 1); if (!modelindex) return qfalse; - VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); //door origin VectorAdd(mins, maxs, origin); @@ -4003,7 +4002,7 @@ int BotTriggerMultipleActivateGoal(bot_state_t * bs, int bspent, bot_activategoa { int i, areas[10], numareas, modelindex, entitynum; char model[128]; - vec3_t start, end, mins, maxs, angles; + vec3_t start, end, mins, maxs; vec3_t origin, goalorigin; activategoal->shoot = qfalse; @@ -4015,7 +4014,6 @@ int BotTriggerMultipleActivateGoal(bot_state_t * bs, int bspent, bot_activategoa modelindex = atoi(model + 1); if (!modelindex) return qfalse; - VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, 0, CONTENTS_TRIGGER, mins, maxs); //trigger origin VectorAdd(mins, maxs, origin); @@ -4169,7 +4167,7 @@ int BotGetActivateGoal(bot_state_t * bs, int entitynum, bot_activategoal_t * act char targetname[10][128]; aas_entityinfo_t entinfo; aas_areainfo_t areainfo; - vec3_t origin, angles, absmins, absmaxs; + vec3_t origin, absmins, absmaxs; memset(activategoal, 0, sizeof(bot_activategoal_t)); BotEntityInfo(entitynum, &entinfo); @@ -4226,7 +4224,6 @@ int BotGetActivateGoal(bot_state_t * bs, int entitynum, bot_activategoal_t * act if (*model) { modelindex = atoi(model + 1); if (modelindex) { - VectorClear(angles); BotModelMinsMaxs(modelindex, ET_MOVER, 0, absmins, absmaxs); // numareas = trap_AAS_BBoxAreas(absmins, absmaxs, areas, MAX_ACTIVATEAREAS * 2); @@ -4621,7 +4618,7 @@ int BotAIPredictObstacles(bot_state_t * bs, bot_goal_t * goal) bs->predictobstacles_goalareanum = goal->areanum; bs->predictobstacles_time = FloatTime(); - // predict at most 100 areas or 10 seconds ahead + // predict at most 100 areas or 1 second ahead trap_AAS_PredictRoute(&route, bs->areanum, bs->origin, goal->areanum, bs->tfl, 100, 1000, RSE_USETRAVELTYPE | RSE_ENTERCONTENTS, AREACONTENTS_MOVER, TFL_BRIDGE, 0); @@ -5265,7 +5262,7 @@ BotDeathmatchAI */ void BotDeathmatchAI(bot_state_t * bs, float thinktime) { - char gender[144], name[144], buf[144]; + char gender[144], name[144]; char userinfo[MAX_INFO_STRING]; int i; @@ -5280,11 +5277,6 @@ void BotDeathmatchAI(bot_state_t * bs, float thinktime) trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "sex", gender); trap_SetUserinfo(bs->client, userinfo); - //set the team - if (!bs->map_restart && g_gametype.integer != GT_TOURNAMENT) { - Com_sprintf(buf, sizeof(buf), "team %s", bs->settings.team); - trap_EA_Command(bs->client, buf); - } //set the chat gender if (gender[0] == 'm') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); @@ -5406,7 +5398,33 @@ void BotSetEntityNumForGoal(bot_goal_t * goal, char *classname) if (!ent->inuse) { continue; } - if (!Q_stricmp(ent->classname, classname)) { + if (Q_stricmp(ent->classname, classname) != 0) { + continue; + } + VectorSubtract(goal->origin, ent->s.origin, dir); + if (VectorLengthSquared(dir) < Square(10)) { + goal->entitynum = i; + return; + } + } +} + +/* +================== +BotSetEntityNumForGoalWithActivator +================== +*/ +void BotSetEntityNumForGoalWithActivator(bot_goal_t *goal, char *classname) { + gentity_t *ent; + int i; + vec3_t dir; + + ent = &g_entities[0]; + for (i = 0; i < level.num_entities; i++, ent++) { + if (!ent->inuse || !ent->activator) { + continue; + } + if (Q_stricmp(ent->activator->classname, classname) != 0) { continue; } VectorSubtract(goal->origin, ent->s.origin, dir); @@ -5461,7 +5479,6 @@ void BotSetupDeathmatchAI(void) char model[128]; gametype = trap_Cvar_VariableIntegerValue("g_gametype"); - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); trap_Cvar_Register(&bot_rocketjump, "bot_rocketjump", "1", 0); trap_Cvar_Register(&bot_grapple, "bot_grapple", "0", 0); diff --git a/code/game/ai_dmq3.h b/code/game/ai_dmq3.h index d8783a3c..047550ec 100644 --- a/code/game/ai_dmq3.h +++ b/code/game/ai_dmq3.h @@ -201,7 +201,7 @@ void BotClearActivateGoalStack(bot_state_t * bs); //returns the team the bot is in int BotTeam(bot_state_t * bs); -//retuns the opposite team of the bot +//returns the opposite team of the bot int BotOppositeTeam(bot_state_t * bs); //returns the flag the bot is carrying (CTFFLAG_?) diff --git a/code/game/ai_main.c b/code/game/ai_main.c index 74d263b8..ce2612b9 100644 --- a/code/game/ai_main.c +++ b/code/game/ai_main.c @@ -76,8 +76,6 @@ #include "inv.h" #include "syn.h" -#define MAX_PATH 144 - //bot states bot_state_t *botstates[MAX_CLIENTS]; @@ -294,7 +292,7 @@ void BotTestAAS(vec3_t origin) return; areanum = BotPointAreaNum(origin); if (areanum) - BotAI_Print(PRT_MESSAGE, "\remtpy area"); + BotAI_Print(PRT_MESSAGE, "\rempty area"); else BotAI_Print(PRT_MESSAGE, "\r^1SOLID area"); } else if (bot_testclusters.integer) { @@ -423,7 +421,7 @@ void BotTeamplayReport(void) char buf[MAX_INFO_STRING]; BotAI_Print(PRT_MESSAGE, S_COLOR_RED "RED\n"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { // if (!botstates[i] || !botstates[i]->inuse) continue; @@ -438,7 +436,7 @@ void BotTeamplayReport(void) } } BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE "BLUE\n"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { // if (!botstates[i] || !botstates[i]->inuse) continue; @@ -570,7 +568,7 @@ void BotUpdateInfoConfigStrings(void) int i; char buf[MAX_INFO_STRING]; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { // if (!botstates[i] || !botstates[i]->inuse) continue; @@ -823,7 +821,7 @@ void BotChangeViewAngles(bot_state_t * bs, float thinktime) // if (bot_challenge.integer) { //smooth slowdown view model - diff = abs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i])); + diff = fabs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i])); anglespeed = diff * factor; if (anglespeed > maxchange) anglespeed = maxchange; @@ -939,7 +937,7 @@ void BotInputToUserCommand(bot_input_t * bi, usercmd_t * ucmd, int delta_angles[ //set the view independent movement f = DotProduct(forward, bi->dir); r = DotProduct(right, bi->dir); - u = abs(forward[2]) * bi->dir[2]; + u = fabs(forward[2]) * bi->dir[2]; m = fabs(f); if (fabs(r) > m) { @@ -1056,8 +1054,10 @@ int BotAI(int client, float thinktime) return qfalse; } //retrieve the current client state - BotAI_GetClientState(client, &bs->cur_ps); - + if (!BotAI_GetClientState(client, &bs->cur_ps)) { + BotAI_Print(PRT_FATAL, "BotAI: failed to get player state for player %d\n", client); + return qfalse; + } //retrieve any waiting server commands while (trap_BotGetServerCommand(client, buf, sizeof(buf))) { //have buf point to the command and args to the command arguments @@ -1225,7 +1225,7 @@ BotAISetupClient */ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart) { - char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH]; + char filename[144], name[144], gender[144]; bot_state_t *bs; int errnum; @@ -1257,7 +1257,7 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta //allocate a goal state bs->gs = trap_BotAllocGoalState(client); //load the item weights - trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, MAX_PATH); + trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, sizeof(filename)); errnum = trap_BotLoadItemWeights(bs->gs, filename); if (errnum != BLERR_NOERROR) { trap_BotFreeGoalState(bs->gs); @@ -1266,7 +1266,7 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta //allocate a weapon state bs->ws = trap_BotAllocWeaponState(); //load the weapon weights - trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, MAX_PATH); + trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, sizeof(filename)); errnum = trap_BotLoadWeaponWeights(bs->ws, filename); if (errnum != BLERR_NOERROR) { trap_BotFreeGoalState(bs->gs); @@ -1276,8 +1276,8 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta //allocate a chat state bs->cs = trap_BotAllocChatState(); //load the chat file - trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, MAX_PATH); - trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, MAX_PATH); + trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, sizeof(filename)); + trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, sizeof(name)); errnum = trap_BotLoadChatFile(bs->cs, filename, name); if (errnum != BLERR_NOERROR) { trap_BotFreeChatState(bs->cs); @@ -1286,7 +1286,7 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta return qfalse; } //get the gender characteristic - trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH); + trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, sizeof(gender)); //set the chat gender if (*gender == 'f' || *gender == 'F') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); @@ -1318,7 +1318,7 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta if (restart) { BotReadSessionData(bs); } - //bot has been setup succesfully + //bot has been setup successfully return qtrue; } @@ -1663,9 +1663,7 @@ int BotInitLibrary(void) char buf[144]; //set the maxclients and maxentities library variables before calling BotSetupLibrary - trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf)); - if (!strlen(buf)) - strcpy(buf, "8"); + Com_sprintf(buf, sizeof(buf), "%d", level.maxclients); trap_BotLibVarSet("maxclients", buf); Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES); trap_BotLibVarSet("maxentities", buf); diff --git a/code/game/ai_team.c b/code/game/ai_team.c index 0aa39d23..9f07b7c5 100644 --- a/code/game/ai_team.c +++ b/code/game/ai_team.c @@ -105,13 +105,9 @@ int BotNumTeamMates(bot_state_t * bs) { int i, numplayers; char buf[MAX_INFO_STRING]; - static int maxclients; - - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numplayers = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) @@ -137,8 +133,12 @@ int BotClientTravelTimeToGoal(int client, bot_goal_t * goal) playerState_t ps; int areanum; - BotAI_GetClientState(client, &ps); - areanum = BotPointAreaNum(ps.origin); + if (BotAI_GetClientState(client, &ps)) { + areanum = BotPointAreaNum(ps.origin); + } else { + areanum = 0; + } + if (!areanum) return 1; return trap_AAS_AreaTravelTimeToGoalArea(areanum, ps.origin, goal->areanum, TFL_DEFAULT); @@ -154,7 +154,6 @@ int BotSortTeamMatesByBaseTravelTime(bot_state_t * bs, int *teammates, int maxte int i, j, k, numteammates, traveltime; char buf[MAX_INFO_STRING]; - static int maxclients; int traveltimes[MAX_CLIENTS]; bot_goal_t *goal = NULL; @@ -164,11 +163,9 @@ int BotSortTeamMatesByBaseTravelTime(bot_state_t * bs, int *teammates, int maxte else goal = &ctf_blueflag; } - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numteammates = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) @@ -932,13 +929,9 @@ void BotTeamOrders(bot_state_t * bs) int teammates[MAX_CLIENTS]; int numteammates, i; char buf[MAX_INFO_STRING]; - static int maxclients; - - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numteammates = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) diff --git a/code/game/bg_lib.c b/code/game/bg_lib.c index f0c05327..ac4bcf07 100644 --- a/code/game/bg_lib.c +++ b/code/game/bg_lib.c @@ -57,10 +57,10 @@ static void swapfunc(char *, char *, int, int); */ #define swapcode(TYPE, parmi, parmj, n) { \ long i = (n) / sizeof (TYPE); \ - register TYPE *pi = (TYPE *) (parmi); \ - register TYPE *pj = (TYPE *) (parmj); \ + TYPE *pi = (TYPE *) (parmi); \ + TYPE *pj = (TYPE *) (parmj); \ do { \ - register TYPE t = *pi; \ + TYPE t = *pi; \ *pi++ = *pj; \ *pj++ = t; \ } while (--i > 0); \ @@ -1473,7 +1473,7 @@ double fabs( double x ) { * probably requires libm on most operating systems. Don't yet * support the exponent (e,E) and sigfig (g,G). Also, fmtint() * was pretty badly broken, it just wasn't being exercised in ways - * which showed it, so that's been fixed. Also, formated the code + * which showed it, so that's been fixed. Also, formatted the code * to mutt conventions, and removed dead code left over from the * original. Also, there is now a builtin-test, just compile with: * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm diff --git a/code/game/bg_pmove.c b/code/game/bg_pmove.c index fff3b8fe..d6033061 100644 --- a/code/game/bg_pmove.c +++ b/code/game/bg_pmove.c @@ -1047,7 +1047,7 @@ static void PM_NoclipMove(void) ================ PM_FootstepForSurface -Returns an event number apropriate for the groundsurface +Returns an event number appropriate for the groundsurface Makro - changed prototype so that we can use it for other surfaces, too (ladder footsteps) ================ @@ -1614,7 +1614,7 @@ static void PM_Footsteps(void) old = pm->ps->bobCycle; pm->ps->bobCycle = (int) (old + bobmove * pml.msec) & 255; - // if we just crossed a cycle boundary, play an apropriate footstep event + // if we just crossed a cycle boundary, play an appropriate footstep event if (((old + 64) ^ (pm->ps->bobCycle + 64)) & 128) { if (pm->waterlevel == 0) { //Elder: we can check for slippers here! diff --git a/code/game/bg_public.h b/code/game/bg_public.h index 4fd1210b..9ed266dc 100644 --- a/code/game/bg_public.h +++ b/code/game/bg_public.h @@ -813,7 +813,7 @@ typedef enum { STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?) STAT_CLIENTS_READY, // bit mask of clients wishing to exit the intermission (FIXME: configstring?) - //STAT_MAX_HEALTH, // health / armor limit, changable by handicap + //STAT_MAX_HEALTH, // health / armor limit, changeable by handicap //These are RQ3-related specific stats STAT_CLIPS, // Num Clips player currently has diff --git a/code/game/bg_slidemove.c b/code/game/bg_slidemove.c index c81144b3..7f70c9bb 100644 --- a/code/game/bg_slidemove.c +++ b/code/game/bg_slidemove.c @@ -465,8 +465,10 @@ qboolean PM_SlideMove(qboolean gravity) // slide along the plane PM_ClipVelocity(pm->ps->velocity, planes[i], clipVelocity, OVERCLIP); - // slide along the plane - PM_ClipVelocity(endVelocity, planes[i], endClipVelocity, OVERCLIP); + if (gravity) { + // slide along the plane + PM_ClipVelocity(endVelocity, planes[i], endClipVelocity, OVERCLIP); + } // see if there is a second plane that the new move enters for (j = 0; j < numplanes; j++) { @@ -490,10 +492,12 @@ qboolean PM_SlideMove(qboolean gravity) d = DotProduct(dir, pm->ps->velocity); VectorScale(dir, d, clipVelocity); - CrossProduct(planes[i], planes[j], dir); - VectorNormalize(dir); - d = DotProduct(dir, endVelocity); - VectorScale(dir, d, endClipVelocity); + if (gravity) { + CrossProduct(planes[i], planes[j], dir); + VectorNormalize(dir); + d = DotProduct(dir, endVelocity); + VectorScale(dir, d, endClipVelocity); + } // see if there is a third plane the the new move enters for (k = 0; k < numplanes; k++) { @@ -511,7 +515,11 @@ qboolean PM_SlideMove(qboolean gravity) // if we have fixed all interactions, try another move VectorCopy(clipVelocity, pm->ps->velocity); - VectorCopy(endClipVelocity, endVelocity); + + if (gravity) { + VectorCopy(endClipVelocity, endVelocity); + } + break; } } diff --git a/code/game/g_active.c b/code/game/g_active.c index 8ebb9bd2..0081844f 100644 --- a/code/game/g_active.c +++ b/code/game/g_active.c @@ -483,7 +483,7 @@ void G_TouchTriggers(gentity_t * ent) continue; } } - // use seperate code for determining if an item is picked up + // use separate code for determining if an item is picked up // so you don't have to actually contact its bounding box if (hit->s.eType == ET_ITEM) { if (!BG_PlayerTouchesItem(&ent->client->ps, &hit->s, level.time)) { @@ -1127,8 +1127,10 @@ void ClientThink_real(gentity_t * ent) if (pmove_msec.integer < 8) { trap_Cvar_Set("pmove_msec", "8"); + trap_Cvar_Update(&pmove_msec); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); + trap_Cvar_Update(&pmove_msec); } if (pmove_fixed.integer || client->pers.pmoveFixed) { diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 9df6dd4e..ecd11277 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -250,78 +250,103 @@ static void PlayerIntroSound(const char *modelAndSkin) trap_SendConsoleCommand(EXEC_APPEND, va("play sound/player/announce/%s.wav\n", skin)); } +/* +=============== +G_CountBotPlayersByName + +Check connected and connecting (delay join) bots. + +Returns number of bots with name on specified team or whole server if team is -1. +=============== +*/ +int G_CountBotPlayersByName( const char *name, int team ) { + int i, num; + gclient_t *cl; + + num = 0; + for ( i=0 ; i< g_maxclients.integer ; i++ ) { + cl = level.clients + i; + if ( cl->pers.connected == CON_DISCONNECTED ) { + continue; + } + if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { + continue; + } + if ( team >= 0 && cl->sess.sessionTeam != team ) { + continue; + } + if ( name && Q_stricmp( name, cl->pers.netname ) ) { + continue; + } + num++; + } + return num; +} + +/* +=============== +G_SelectRandomBotInfo + +Get random least used bot info on team or whole server if team is -1. +=============== +*/ +int G_SelectRandomBotInfo( int team ) { + int selection[MAX_BOTS]; + int n, num; + int count, bestCount; + char *value; + + // don't add duplicate bots to the server if there are less bots than bot types + if ( team != -1 && G_CountBotPlayersByName( NULL, -1 ) < g_numBots ) { + team = -1; + } + + num = 0; + bestCount = MAX_CLIENTS; + for ( n = 0; n < g_numBots ; n++ ) { + value = Info_ValueForKey( g_botInfos[n], "funname" ); + if ( !value[0] ) { + value = Info_ValueForKey( g_botInfos[n], "name" ); + } + // + count = G_CountBotPlayersByName( value, team ); + + if ( count < bestCount ) { + bestCount = count; + num = 0; + } + + if ( count == bestCount ) { + selection[num++] = n; + + if ( num == MAX_BOTS ) { + break; + } + } + } + + if ( num > 0 ) { + num = random() * ( num - 1 ); + return selection[num]; + } + + return -1; +} + /* =============== G_AddRandomBot =============== */ -void G_AddRandomBot(int team) -{ - int i, n, num; - float skill; - char *value, netname[36], *teamstr; - gclient_t *cl; +void G_AddRandomBot( int team ) { + char *teamstr; + float skill; - num = 0; - for (n = 0; n < g_numBots; n++) { - value = Info_ValueForKey(g_botInfos[n], "name"); - // - for (i = 0; i < g_maxclients.integer; i++) { - cl = level.clients + i; - if (cl->pers.connected != CON_CONNECTED) { - continue; - } - if (!(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT)) { - continue; - } - if (team >= 0 && cl->sess.sessionTeam != team) { - continue; - } - if (!Q_stricmp(value, cl->pers.netname)) { - break; - } - } - if (i >= g_maxclients.integer) { - num++; - } - } - num = random() * num; - for (n = 0; n < g_numBots; n++) { - value = Info_ValueForKey(g_botInfos[n], "name"); - // - for (i = 0; i < g_maxclients.integer; i++) { - cl = level.clients + i; - if (cl->pers.connected != CON_CONNECTED) { - continue; - } - if (!(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT)) { - continue; - } - if (team >= 0 && cl->sess.sessionTeam != team) { - continue; - } - if (!Q_stricmp(value, cl->pers.netname)) { - break; - } - } - if (i >= g_maxclients.integer) { - num--; - if (num <= 0) { - skill = trap_Cvar_VariableValue("g_spSkill"); - if (team == TEAM_RED) - teamstr = "red"; - else if (team == TEAM_BLUE) - teamstr = "blue"; - else - teamstr = ""; - Q_strncpyz(netname, value, sizeof(netname)); - Q_CleanStr(netname); - trap_SendConsoleCommand(EXEC_INSERT, - va("addbot %s %f %s %i\n", netname, skill, teamstr, 0)); - return; - } - } - } + skill = trap_Cvar_VariableValue( "g_spSkill" ); + if (team == TEAM_RED) teamstr = "red"; + else if (team == TEAM_BLUE) teamstr = "blue"; + else teamstr = "free"; + trap_SendConsoleCommand( EXEC_INSERT, va("addbot random %f %s %i\n", skill, teamstr, 0) ); } /* @@ -339,13 +364,13 @@ int G_RemoveRandomBot(int team) if (cl->pers.connected != CON_CONNECTED) { continue; } - if (!(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT)) { + if (!(g_entities[i].r.svFlags & SVF_BOT)) { continue; } if (team >= 0 && cl->sess.sessionTeam != team) { continue; } - trap_SendConsoleCommand( EXEC_INSERT, va("clientkick %d\n", cl->ps.clientNum) ); + trap_SendConsoleCommand( EXEC_INSERT, va("clientkick %d\n", i) ); return qtrue; } return qfalse; @@ -367,7 +392,7 @@ int G_CountHumanPlayers(int team) if (cl->pers.connected != CON_CONNECTED) { continue; } - if (g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) { + if (g_entities[i].r.svFlags & SVF_BOT) { continue; } if (g_gametype.integer >= GT_TEAM) { @@ -385,20 +410,22 @@ int G_CountHumanPlayers(int team) /* =============== G_CountBotPlayers + +Check connected and connecting (delay join) bots. =============== */ int G_CountBotPlayers(int team) { - int i, n, num; + int i, num; gclient_t *cl; num = 0; for (i = 0; i < g_maxclients.integer; i++) { cl = level.clients + i; - if (cl->pers.connected != CON_CONNECTED) { + if (cl->pers.connected == CON_DISCONNECTED) { continue; } - if (!(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT)) { + if (!(g_entities[i].r.svFlags & SVF_BOT)) { continue; } if (g_gametype.integer >= GT_TEAM) { @@ -410,15 +437,6 @@ int G_CountBotPlayers(int team) } num++; } - for (n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++) { - if (!botSpawnQueue[n].spawnTime) { - continue; - } - if (botSpawnQueue[n].spawnTime > level.time) { - continue; - } - num++; - } return num; } @@ -582,7 +600,6 @@ qboolean G_BotConnect(int clientNum, qboolean restart) Q_strncpyz(settings.characterfile, Info_ValueForKey(userinfo, "characterfile"), sizeof(settings.characterfile)); settings.skill = atof(Info_ValueForKey(userinfo, "skill")); - Q_strncpyz(settings.team, Info_ValueForKey(userinfo, "team"), sizeof(settings.team)); if (!BotAISetupClient(clientNum, &settings, restart)) { trap_DropClient(clientNum, "BotAISetupClient failed"); @@ -600,6 +617,8 @@ G_AddBot static void G_AddBot(const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; + int teamNum; + int botinfoNum; char *botinfo; gentity_t *bot; char *s; @@ -610,12 +629,59 @@ static void G_AddBot(const char *name, float skill, const char *team, int delay, weapon_t tpWeapon = WP_M4; holdable_t tpItem = HI_LASER; - // get the botinfo from bots.txt - botinfo = G_GetBotInfoByName(name); - if (!botinfo) { - G_Printf(S_COLOR_RED "Error: Bot '%s' not defined\n", name); + // 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; } + + // set default team + if (!team || !*team) { + if (g_gametype.integer >= GT_TEAM) { + if (PickTeam(clientNum) == TEAM_RED) { + team = "red"; + } else { + team = "blue"; + } + } else { + team = "free"; + } + } + + // get the botinfo from bots.txt + if ( Q_stricmp( name, "random" ) == 0 ) { + if (Q_stricmp(team, "red") == 0 || Q_stricmp(team, "r") == 0) { + teamNum = TEAM_RED; + } else if (Q_stricmp(team, "blue") == 0 || Q_stricmp(team, "b") == 0) { + teamNum = TEAM_BLUE; + } else if (!Q_stricmp(team, "spectator") || !Q_stricmp(team, "s")) { + teamNum = TEAM_SPECTATOR; + } else { + teamNum = TEAM_FREE; + } + + botinfoNum = G_SelectRandomBotInfo(teamNum); + + if (botinfoNum < 0) { + G_Printf(S_COLOR_RED "Error: Cannot add random bot, no bot info available.\n" ); + trap_BotFreeClient(clientNum); + return; + } + + botinfo = G_GetBotInfoByNumber(botinfoNum); + } + else { + 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'; @@ -630,7 +696,8 @@ static void G_AddBot(const char *name, float skill, const char *team, int delay, Info_SetValueForKey(userinfo, "name", botname); Info_SetValueForKey(userinfo, "rate", "25000"); Info_SetValueForKey(userinfo, "snaps", "20"); - Info_SetValueForKey(userinfo, "skill", va("%1.2f", skill)); + Info_SetValueForKey(userinfo, "skill", va("%.2f", skill)); + Info_SetValueForKey(userinfo, "teampref", team); if (skill >= 1 && skill < 2) { Info_SetValueForKey(userinfo, "handicap", "50"); @@ -679,30 +746,13 @@ static void G_AddBot(const char *name, float skill, const char *team, int delay, s = Info_ValueForKey(botinfo, "aifile"); if (!*s) { trap_Printf(S_COLOR_RED "Error: bot has no aifile specified\n"); + trap_BotFreeClient( clientNum ); 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 (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); + Info_SetValueForKey(userinfo, "characterfile", s); + + // don't send tinfo to bots, they don't parse it + Info_SetValueForKey( userinfo, "teamoverlay", "0" ); if (g_gametype.integer == GT_TEAMPLAY) { //Makro - load custom weapon/item from bot file @@ -772,7 +822,7 @@ void Svcmd_AddBot_f(void) if (!string[0]) { skill = 4; } else { - skill = atof(string); + skill = Com_Clamp(1, 5, atof(string)); } // team @@ -815,19 +865,19 @@ void Svcmd_BotList_f(void) trap_Printf("^1name model aifile funname\n"); for (i = 0; i < g_numBots; i++) { - strcpy(name, Info_ValueForKey(g_botInfos[i], "name")); + Q_strncpyz(name, Info_ValueForKey(g_botInfos[i], "name"), sizeof(name)); if (!*name) { strcpy(name, "UnnamedPlayer"); } - strcpy(funname, Info_ValueForKey(g_botInfos[i], "funname")); + Q_strncpyz(funname, Info_ValueForKey(g_botInfos[i], "funname"), sizeof(funname)); if (!*funname) { strcpy(funname, ""); } - strcpy(model, Info_ValueForKey(g_botInfos[i], "model")); + Q_strncpyz(model, Info_ValueForKey(g_botInfos[i], "model"), sizeof(model)); if (!*model) { strcpy(model, "visor/default"); } - strcpy(aifile, Info_ValueForKey(g_botInfos[i], "aifile")); + Q_strncpyz(aifile, Info_ValueForKey(g_botInfos[i], "aifile"), sizeof(aifile)); if (!*aifile) { strcpy(aifile, "bots/default_c.c"); } diff --git a/code/game/g_client.c b/code/game/g_client.c index c0cf9fb7..6ad93200 100644 --- a/code/game/g_client.c +++ b/code/game/g_client.c @@ -402,7 +402,7 @@ void SP_info_player_deathmatch(gentity_t * ent) } /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) -equivelant to info_player_deathmatch +equivalent to info_player_deathmatch */ void SP_info_player_start(gentity_t * ent) { @@ -703,7 +703,7 @@ void ClearBodyQue(void) ============= BodySink -After sitting around for five seconds, fall into the ground and dissapear +After sitting around for five seconds, fall into the ground and disappear ============= */ void BodySink(gentity_t * ent) @@ -994,7 +994,7 @@ void ClientUserinfoChanged(int clientNum) { gentity_t *ent; gclient_t *client; - int teamTask, teamLeader, team, health, gender; + int teamTask, teamLeader, health, gender; char *s, model[MAX_QPATH], headModel[MAX_QPATH], oldname[MAX_STRING_CHARS]; char c1[MAX_INFO_STRING], c2[MAX_INFO_STRING], redTeam[MAX_INFO_STRING]; char blueTeam[MAX_INFO_STRING], userinfo[MAX_INFO_STRING]; @@ -1011,11 +1011,7 @@ void ClientUserinfoChanged(int clientNum) // don't keep those clients and userinfo trap_DropClient(clientNum, "Invalid userinfo"); } - // check for local client - s = Info_ValueForKey(userinfo, "ip"); - if (!strcmp(s, "localhost")) { - client->pers.localClient = qtrue; - } + // check the item prediction s = Info_ValueForKey(userinfo, "cg_predictItems"); if (!atoi(s)) { @@ -1144,20 +1140,6 @@ void ClientUserinfoChanged(int clientNum) } else if (gender != GENDER_NEUTER) client->radioGender = gender; } - // bots set their team a few frames later - if (g_gametype.integer >= GT_TEAM && g_entities[clientNum].r.svFlags & SVF_BOT) { - s = Info_ValueForKey(userinfo, "team"); - if (!Q_stricmp(s, "red") || !Q_stricmp(s, "r") || !Q_stricmp(s, "1")) { - team = TEAM_RED; - } else if (!Q_stricmp(s, "blue") || !Q_stricmp(s, "b") || !Q_stricmp(s, "2")) { - team = TEAM_BLUE; - } else { - // pick the team with the least number of players - team = PickTeam(clientNum); - } - } else { - team = client->sess.sessionTeam; - } // teamInfo s = Info_ValueForKey(userinfo, "teamoverlay"); @@ -1173,11 +1155,11 @@ void ClientUserinfoChanged(int clientNum) teamLeader = client->sess.teamLeader; // colors - strcpy(c1, Info_ValueForKey(userinfo, "color1")); - strcpy(c2, Info_ValueForKey(userinfo, "color2")); + Q_strncpyz(c1, Info_ValueForKey(userinfo, "color1"), sizeof(c1)); + Q_strncpyz(c2, Info_ValueForKey(userinfo, "color2"), sizeof(c2)); - strcpy(redTeam, Info_ValueForKey(userinfo, "g_redteam")); - strcpy(blueTeam, Info_ValueForKey(userinfo, "g_blueteam")); + Q_strncpyz(redTeam, Info_ValueForKey(userinfo, "g_redteam"), sizeof(redTeam)); + Q_strncpyz(blueTeam, Info_ValueForKey(userinfo, "g_blueteam"), sizeof(blueTeam)); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds @@ -1185,7 +1167,7 @@ void ClientUserinfoChanged(int clientNum) //Makro - adding teamplay weapon/item info for bots s = va ("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d\\tpw\\%s\\tpi\\%s", - client->pers.netname, team, model, headModel, c1, c2, client->pers.maxHealth, client->sess.wins, + client->pers.netname, client->sess.sessionTeam, model, headModel, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, //Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader ); Info_ValueForKey(userinfo, "skill"), teamTask, teamLeader, Info_ValueForKey(userinfo, "tpw"), @@ -1339,11 +1321,11 @@ char *ClientConnect(int clientNum, qboolean firstTime, qboolean isBot) // JBravo: Clear zcam flag for cgame client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; - // read or initialize the session data - if (firstTime || level.newSession) { - G_InitSessionData(client, userinfo); + // check for local client + value = Info_ValueForKey( userinfo, "ip" ); + if ( !strcmp( value, "localhost" ) ) { + client->pers.localClient = qtrue; } - G_ReadSessionData(client); if (isBot) { ent->r.svFlags |= SVF_BOT; @@ -1352,6 +1334,13 @@ char *ClientConnect(int clientNum, qboolean firstTime, qboolean isBot) return "BotConnectfailed"; } } + + // read or initialize the session data + if (firstTime || level.newSession) { + G_InitSessionData(client, userinfo); + } + G_ReadSessionData(client); + // slicer : make sessionTeam = to savedTeam for scoreboard on cgame // JBravo: only for teambased games. Could break DM if (g_gametype.integer >= GT_TEAM) { diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index d6e9818c..64153a5e 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -505,8 +505,6 @@ #include "../ui/menudef.h" // for the voice chats //Blaze for door code void Use_BinaryMover(gentity_t * ent, gentity_t * other, gentity_t * activator); -// JBravo: for kicking code -gentity_t *getEntByName(char *name); /* ================== @@ -516,12 +514,17 @@ DeathmatchScoreboardMessage */ void DeathmatchScoreboardMessage(gentity_t * ent) { - char entry[1024], string[1400]; + char entry[1024], string[1000]; int stringlength, i, j; gclient_t *cl; int numSorted, scoreFlags, accuracy; int alive; + // don't send scores to bots, they don't parse it + if ( ent->r.svFlags & SVF_BOT ) { + return; + } + // send the latest information on all clients string[0] = 0; stringlength = 0; @@ -705,6 +708,33 @@ char *ConcatArgs(int start) return line; } + +/* +================== +StringIsInteger +================== +*/ +qboolean StringIsInteger(const char * s) +{ + int i; + int len; + qboolean foundDigit; + + len = strlen(s); + foundDigit = qfalse; + + for (i=0 ; i < len ; i++) { + if (!isdigit(s[i])) { + return qfalse; + } + + foundDigit = qtrue; + } + + return foundDigit; +} + + /* ================== ClientNumberFromString @@ -713,37 +743,36 @@ Returns a player number for either a number or name string Returns -1 if invalid ================== */ -int ClientNumberFromString(gentity_t * to, char *s) +int ClientNumberFromString(gentity_t *to, char *s, qboolean checkNums, qboolean checkNames) { gclient_t *cl; int idnum; - char s2[MAX_STRING_CHARS]; - char n2[MAX_STRING_CHARS]; + char cleanName[MAX_STRING_CHARS]; - // numeric values are just slot numbers - if (s[0] >= '0' && s[0] <= '9') { - idnum = atoi(s); - if (idnum < 0 || idnum >= level.maxclients) { - trap_SendServerCommand(to - g_entities, va("print \"^1Bad client slot: %i\n\"", idnum)); - return -1; + if ( checkNums ) { + // numeric values could be slot numbers + if (StringIsInteger(s)) { + idnum = atoi(s); + if (idnum >= 0 && idnum < level.maxclients) { + cl = &level.clients[idnum]; + if (cl->pers.connected == CON_CONNECTED) { + return idnum; + } + } } - - cl = &level.clients[idnum]; - if (cl->pers.connected != CON_CONNECTED) { - trap_SendServerCommand(to - g_entities, va("print \"^1Client %i is not active\n\"", idnum)); - return -1; - } - return idnum; } - // check for a name match - SanitizeString(s, s2); - for (idnum = 0, cl = level.clients; idnum < level.maxclients; idnum++, cl++) { - if (cl->pers.connected != CON_CONNECTED) { - continue; - } - SanitizeString(cl->pers.netname, n2); - if (!strcmp(n2, s2)) { - return idnum; + + if ( checkNames ) { + // check for a name match + for (idnum = 0, cl = level.clients; idnum < level.maxclients; idnum++, cl++) { + if (cl->pers.connected != CON_CONNECTED) { + continue; + } + Q_strncpyz(cleanName, cl->pers.netname, sizeof(cleanName)); + Q_CleanStr(cleanName); + if (!Q_stricmp(cleanName, s)) { + return idnum; + } } } @@ -1079,7 +1108,7 @@ void BroadcastTeamChange(gclient_t * client, int oldTeam) SetTeam ================= */ -void SetTeam(gentity_t * ent, char *s) +void SetTeam(gentity_t * ent, const char *s) { int team, oldTeam, clientNum; gclient_t *client; @@ -1131,7 +1160,7 @@ void SetTeam(gentity_t * ent, char *s) team = PickTeam(clientNum); } - if (g_teamForceBalance.integer) { + if (g_teamForceBalance.integer && !client->pers.localClient && !(ent->r.svFlags & SVF_BOT)) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( clientNum, TEAM_BLUE ); @@ -1185,10 +1214,11 @@ void SetTeam(gentity_t * ent, char *s) if(g_gametype.integer >= GT_TEAM && (oldTeam == TEAM_FREE || oldTeam == TEAM_SPECTATOR) && (team == TEAM_RED || team == TEAM_BLUE)) ent->client->sess.refHear = qfalse; - // if the player was dead leave the body - if (client->ps.stats[STAT_HEALTH] <= 0) { + // if the player was dead leave the body, but only if they're actually in game + if (client->ps.stats[STAT_HEALTH] <= 0 && client->pers.connected == CON_CONNECTED) { CopyToBodyQue(ent); } + // he starts at 'base' client->pers.teamState.state = TEAM_BEGIN; @@ -1242,9 +1272,8 @@ void SetTeam(gentity_t * ent, char *s) } client->sess.teamLeader = qfalse; - BroadcastTeamChange(client, oldTeam); - // get and distribute relevent paramters + // get and distribute relevant parameters // JBravo: save sessionTeam and then set it correctly for the call to ClientUserinfoChanged // so the scoreboard will be correct. Also check for uneven teams. @@ -1269,8 +1298,19 @@ void SetTeam(gentity_t * ent, char *s) client->radioGender = level.team2gender; } else { ClientUserinfoChanged(clientNum); - ClientBegin(clientNum); + + if (client->pers.connected == CON_CONNECTED) { + ClientBegin(clientNum); + } } + + BroadcastTeamChange(client, oldTeam); + + // client hasn't spawned yet, they sent an early team command, teampref userinfo, or g_teamAutoJoin is enabled + if (client->pers.connected != CON_CONNECTED) { + return; + } + if (g_gametype.integer == GT_CTF || (g_gametype.integer == GT_TEAM && client->sess.savedTeam == TEAM_SPECTATOR)) MakeSpectator (ent); // JBravo: If the game is in progress, lets spawn players joining. @@ -1423,7 +1463,7 @@ void Cmd_Follow_f(gentity_t * ent) } trap_Argv(1, arg, sizeof(arg)); - i = ClientNumberFromString(ent, arg); + i = ClientNumberFromString(ent, arg, qtrue, qtrue); if (i == -1) { return; } @@ -1685,7 +1725,7 @@ void G_Say(gentity_t * ent, gentity_t * target, int mode, const char *chatText) // JBravo: Log it like AQ does G_LogPrintf("%s%s\n", name, text); - // send it to all the apropriate clients + // send it to all the appropriate clients for (j = 0; j < level.maxclients; j++) { //Blaze: Prit out some Debug info if (&g_entities[j] == NULL) G_Printf("Ln 1532\n"); @@ -1695,6 +1735,18 @@ void G_Say(gentity_t * ent, gentity_t * target, int mode, const char *chatText) } } +static void SanitizeChatText(char *text) +{ + int i; + + for (i = 0; text[i]; i++) { + if (text[i] == '\n' || text[i] == '\r') { + text[i] = ' '; + } + } +} + + /* ================== Cmd_ay_f @@ -1732,6 +1784,8 @@ static void Cmd_Say_f(gentity_t * ent, int mode, qboolean arg0) } } + SanitizeChatText( p ); + G_Say(ent, NULL, mode, p); } @@ -1747,7 +1801,8 @@ static void Cmd_Tell_f(gentity_t * ent) char *p; char arg[MAX_TOKEN_CHARS]; - if (trap_Argc() < 2) { + if (trap_Argc () < 3) { + trap_SendServerCommand( ent-g_entities, "print \"Usage: tell \n\"" ); return; } @@ -1756,20 +1811,22 @@ static void Cmd_Tell_f(gentity_t * ent) return; trap_Argv(1, arg, sizeof(arg)); - targetNum = atoi(arg); - if (targetNum < 0 || targetNum >= level.maxclients) { + targetNum = ClientNumberFromString(ent, arg, qtrue, qtrue); + if (targetNum == -1) { return; } //Blaze: Prit out some Debug info if (&g_entities[targetNum] == NULL) G_Printf("Ln 1608\n"); target = &g_entities[targetNum]; - if (!target || !target->inuse || !target->client) { + if (!target->inuse || !target->client) { return; } p = ConcatArgs(2); + SanitizeChatText(p); + G_LogPrintf("tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p); G_Say(ent, target, SAY_TELL, p); // don't tell to the player self if it was already directed to this player @@ -1832,7 +1889,7 @@ void G_Voice(gentity_t * ent, gentity_t * target, int mode, const char *id, qboo if (g_dedicated.integer) { G_Printf("voice: %s %s\n", ent->client->pers.netname, id); } - // send it to all the apropriate clients + // send it to all the appropriate clients for (j = 0; j < level.maxclients; j++) { //Blaze: Prit out some Debug info if (&g_entities[j] == NULL) G_Printf("Ln 1682\n"); @@ -1861,6 +1918,8 @@ static void Cmd_Voice_f(gentity_t * ent, int mode, qboolean arg0, qboolean voice p = ConcatArgs(1); } + SanitizeChatText(p); + G_Voice(ent, NULL, mode, p, voiceonly); } @@ -1876,25 +1935,28 @@ static void Cmd_VoiceTell_f(gentity_t * ent, qboolean voiceonly) char *id; char arg[MAX_TOKEN_CHARS]; - if (trap_Argc() < 2) { + if (trap_Argc () < 3) { + trap_SendServerCommand(ent-g_entities, va("print \"Usage: %s \n\"", voiceonly ? "votell" : "vtell")); return; } trap_Argv(1, arg, sizeof(arg)); - targetNum = atoi(arg); - if (targetNum < 0 || targetNum >= level.maxclients) { + targetNum = ClientNumberFromString(ent, arg, qtrue, qtrue); + if (targetNum == -1) { return; } //Blaze: Prit out some Debug info if (&g_entities[targetNum] == NULL) G_Printf("Ln 1733\n"); target = &g_entities[targetNum]; - if (!target || !target->inuse || !target->client) { + if (!target->inuse || !target->client) { return; } id = ConcatArgs(2); + SanitizeChatText(id); + G_LogPrintf("vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id); G_Voice(ent, target, SAY_TELL, id, voiceonly); // don't tell to the player self if it was already directed to this player @@ -1989,28 +2051,49 @@ static char *gc_orders[] = { "report" }; -void Cmd_GameCommand_f(gentity_t * ent) +static const int numgc_orders = ARRAY_LEN(gc_orders); + +void Cmd_GameCommand_f(gentity_t *ent) { - int player; - int order; - char str[MAX_TOKEN_CHARS]; + int targetNum; + gentity_t *target; + int order; + char arg[MAX_TOKEN_CHARS]; - trap_Argv(1, str, sizeof(str)); - player = atoi(str); - trap_Argv(2, str, sizeof(str)); - order = atoi(str); - - if (player < 0 || player >= MAX_CLIENTS) { + if (trap_Argc() != 3) { + trap_SendServerCommand(ent-g_entities, va("print \"Usage: gc \n\"", numgc_orders - 1)); return; } - if (order < 0 || order > ARRAY_LEN(gc_orders)) { + + trap_Argv(2, arg, sizeof(arg)); + order = atoi(arg); + + if (order < 0 || order >= numgc_orders) { + trap_SendServerCommand(ent-g_entities, va("print \"Bad order: %i\n\"", order)); return; } + + trap_Argv(1, arg, sizeof(arg)); + targetNum = ClientNumberFromString(ent, arg, qtrue, qtrue); + if (targetNum == -1) { + return; + } + //Blaze: Prit out some Debug info - if (&g_entities[player] == NULL) G_Printf("Ln 1854\n"); + if (&g_entities[targetNum] == NULL) G_Printf("Ln 1854\n"); - G_Say(ent, &g_entities[player], SAY_TELL, gc_orders[order]); - G_Say(ent, ent, SAY_TELL, gc_orders[order]); + target = &g_entities[targetNum]; + if (!target->inuse || !target->client) { + return; + } + + G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, gc_orders[order]); + G_Say(ent, target, SAY_TELL, gc_orders[order]); + // don't tell to the player self if it was already directed to this player + // also don't send the chat back to a bot + if (ent != target && !(ent->r.svFlags & SVF_BOT)) { + G_Say(ent, ent, SAY_TELL, gc_orders[order]); + } } /* @@ -2044,12 +2127,12 @@ Cmd_CallVote_f */ void Cmd_CallVote_f(gentity_t * ent) { - int i, kickNum; + char *c; + int i; float delay; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; char *v_gametype, *v_map; - gentity_t *kicked; if (!g_allowVote.integer) { trap_SendServerCommand(ent - g_entities, "print \"^1Voting not allowed here.\n\""); @@ -2090,9 +2173,16 @@ void Cmd_CallVote_f(gentity_t * ent) trap_Argv(1, arg1, sizeof(arg1)); trap_Argv(2, arg2, sizeof(arg2)); - if (strchr(arg1, ';') || strchr(arg2, ';')) { - trap_SendServerCommand(ent - g_entities, "print \"^1Invalid vote string.\n\""); - return; + // check for command separators in arg2 + for(c = arg2; *c; ++c) { + switch(*c) { + case '\n': + case '\r': + case ';': + trap_SendServerCommand(ent-g_entities, "print \"^1Invalid vote string.\n\""); + return; + break; + } } if (!Q_stricmp(arg1, "cyclemap")) { @@ -2244,16 +2334,19 @@ void Cmd_CallVote_f(gentity_t * ent) } else if (!Q_stricmp(arg1, "cyclemap")) { Com_sprintf(level.voteString, sizeof(level.voteString), "cyclemap"); Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s", level.voteString); - } else if (!Q_strncmp(arg1, "kick", 4)) { - kicked = getEntByName(arg2); - if (kicked && kicked->client) { - kickNum = kicked->client - level.clients; - Com_sprintf(level.voteString, sizeof(level.voteString), "clientkick \"%i\"", kickNum); - Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s \"%s\"", arg1, arg2); - } else { - Com_sprintf(level.voteString, sizeof(level.voteString), "%s \"%s\"", arg1, arg2); - Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s", level.voteString); + } else if (!Q_stricmp(arg1, "clientkick") || !Q_stricmp(arg1, "kick")) { + i = ClientNumberFromString(ent, arg2, !Q_stricmp(arg1, "clientkick"), !Q_stricmp(arg1, "kick")); + if (i == -1) { + return; } + + if (level.clients[i].pers.localClient) { + trap_SendServerCommand(ent - g_entities, "print \"Cannot kick host player.\n\""); + return; + } + + Com_sprintf(level.voteString, sizeof(level.voteString), "clientkick %d", i); + Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", level.clients[i].pers.netname); } else { Com_sprintf(level.voteString, sizeof(level.voteString), "%s \"%s\"", arg1, arg2); Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s", level.voteString); @@ -2331,6 +2424,7 @@ Cmd_CallTeamVote_f */ void Cmd_CallTeamVote_f(gentity_t * ent) { + char *c; int i, team, cs_offset; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; @@ -2376,9 +2470,16 @@ void Cmd_CallTeamVote_f(gentity_t * ent) trap_Argv(i, &arg2[strlen(arg2)], sizeof(arg2) - strlen(arg2)); } - if (strchr(arg1, ';') || strchr(arg2, ';')) { - trap_SendServerCommand(ent - g_entities, "print \"^1Invalid vote string.\n\""); + // check for command separators in arg2 + for(c = arg2; *c; ++c) { + switch(*c) { + case '\n': + case '\r': + case ';': + trap_SendServerCommand(ent - g_entities, "print \"^1Invalid vote string.\n\""); return; + break; + } } if (!Q_stricmp(arg1, "leader")) { @@ -3004,6 +3105,14 @@ void ClientCommand(int clientNum) ent = g_entities + clientNum; if (!ent->client || ent->client->pers.connected != CON_CONNECTED) { + if (ent->client && ent->client->pers.localClient) { + // Handle early team command sent by UI when starting a local + // team play game. + trap_Argv( 0, cmd, sizeof( cmd ) ); + if (Q_stricmp (cmd, "team") == 0) { + Cmd_Team_f (ent); + } + } return; // not fully in game yet } diff --git a/code/game/g_combat.c b/code/game/g_combat.c index 0c37f111..1ab4c2a8 100644 --- a/code/game/g_combat.c +++ b/code/game/g_combat.c @@ -1806,7 +1806,7 @@ void G_Damage(gentity_t * targ, gentity_t * inflictor, gentity_t * attacker, } } - // the intermission has allready been qualified for, so don't + // the intermission has already been qualified for, so don't // allow any extra scoring if (level.intermissionQueued) { return; diff --git a/code/game/g_items.c b/code/game/g_items.c index 846ed592..91431c60 100644 --- a/code/game/g_items.c +++ b/code/game/g_items.c @@ -119,7 +119,7 @@ Respawnable items don't actually go away when picked up, they are just made invisible and untouchable. This allows them to ride - movers and respawn apropriately. + movers and respawn appropriately. */ #define RESPAWN_ARMOR 25 diff --git a/code/game/g_local.h b/code/game/g_local.h index be8a855f..5b0c52fc 100644 --- a/code/game/g_local.h +++ b/code/game/g_local.h @@ -1032,7 +1032,7 @@ char *ConcatArgs(int start); void Cmd_Score_f(gentity_t * ent); void StopFollowing(gentity_t * ent); void BroadcastTeamChange(gclient_t * client, int oldTeam); -void SetTeam(gentity_t * ent, char *s); +void SetTeam(gentity_t * ent, const char *s); void Cmd_FollowCycle_f(gentity_t * ent, int dir); void Cmd_Unzoom(gentity_t * ent); void Cmd_OpenDoor(gentity_t * ent); @@ -1389,7 +1389,6 @@ void BotInterbreedEndMatch(void); typedef struct bot_settings_s { char characterfile[MAX_FILEPATH]; float skill; - char team[MAX_FILEPATH]; } bot_settings_t; int BotAISetup(int restart); @@ -1462,6 +1461,7 @@ extern vmCvar_t g_enableBreath; extern vmCvar_t g_enableFogLaser; extern vmCvar_t g_singlePlayer; extern vmCvar_t g_proxMineTimeout; +extern vmCvar_t g_localTeamPref; // JBravo: unlagged extern vmCvar_t g_delagHitscan; diff --git a/code/game/g_main.c b/code/game/g_main.c index 76fe119b..fd498e76 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -514,6 +514,7 @@ vmCvar_t pmove_fixed; vmCvar_t pmove_msec; vmCvar_t g_rankings; vmCvar_t g_listEntity; +vmCvar_t g_localTeamPref; //Slicer: Matchmode vmCvar_t g_RQ3_matchmode; @@ -614,7 +615,7 @@ static cvarTable_t gameCvarTable[] = { // noset vars {NULL, "gamename", GAMEVERSION, CVAR_SERVERINFO | CVAR_ROM, 0, qfalse}, - {NULL, "gamedate", __DATE__, CVAR_ROM, 0, qfalse}, + {NULL, "gamedate", PRODUCT_DATE, CVAR_ROM, 0, qfalse}, {&g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse}, // latched vars @@ -674,6 +675,7 @@ static cvarTable_t gameCvarTable[] = { {&g_unlaggedVersion, "g_unlaggedVersion", "2.0", CVAR_ROM | CVAR_SERVERINFO, 0, qfalse}, {&sv_fps, "sv_fps", "20", CVAR_SYSTEMINFO | CVAR_ARCHIVE, 0, qfalse}, {&g_rankings, "g_rankings", "0", 0, 0, qfalse}, + {&g_localTeamPref, "g_localTeamPref", "", 0, 0, qfalse}, //Slicer: Matchmode {&g_RQ3_matchmode, "g_RQ3_matchmode", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_SYSTEMINFO, 0, qfalse}, {&g_RQ3_forceteamtalk, "g_RQ3_forceteamtalk", "0", 0, 0, qtrue}, @@ -847,7 +849,7 @@ void G_FindTeams(void) c = 0; c2 = 0; - for (i = 1, e = g_entities + i; i < level.num_entities; i++, e++) { + for (i = MAX_CLIENTS, e = g_entities + i; i < level.num_entities; i++, e++) { if (!e->inuse) continue; if (!e->team) @@ -1251,7 +1253,7 @@ void G_InitGame(int levelTime, int randomSeed, int restart) G_Printf("------- Game Initialization -------\n"); G_Printf("gamename: %s\n", GAMEVERSION); - G_Printf("gamedate: %s\n", __DATE__); + G_Printf("gamedate: %s\n", PRODUCT_DATE); srand(randomSeed); @@ -2284,6 +2286,12 @@ void CheckExitRules(void) //Slicer if (g_gametype.integer >= GT_TEAM) { //Let's check fraglimit here, everything else is on teamplay.c + if ( g_fraglimit.integer < 0 ) { + G_Printf( "fraglimit %i is out of range, defaulting to 0\n", g_fraglimit.integer ); + trap_Cvar_Set( "fraglimit", "0" ); + trap_Cvar_Update( &g_fraglimit ); + } + if (g_fraglimit.integer > 0) { for (i = 0; i < g_maxclients.integer; i++) { cl = level.clients + i; @@ -2315,6 +2323,12 @@ void CheckExitRules(void) return; } + if ( g_timelimit.integer < 0 || g_timelimit.integer > INT_MAX / 60000 ) { + G_Printf( "timelimit %i is out of range, defaulting to 0\n", g_timelimit.integer ); + trap_Cvar_Set( "timelimit", "0" ); + trap_Cvar_Update( &g_timelimit ); + } + if (g_timelimit.integer && !level.warmupTime) { if (level.time - level.startTime >= g_timelimit.integer * 60000) { trap_SendServerCommand(-1, "print \"Timelimit hit.\n\""); @@ -2327,6 +2341,12 @@ void CheckExitRules(void) return; } */ + if ( g_fraglimit.integer < 0 ) { + G_Printf( "fraglimit %i is out of range, defaulting to 0\n", g_fraglimit.integer ); + trap_Cvar_Set( "fraglimit", "0" ); + trap_Cvar_Update( &g_fraglimit ); + } + if (g_gametype.integer < GT_CTF && g_fraglimit.integer) { if (level.teamScores[TEAM_RED] >= g_fraglimit.integer) { trap_SendServerCommand(-1, "print \"Red hit the fraglimit.\n\""); @@ -2358,6 +2378,12 @@ void CheckExitRules(void) } } + if ( g_capturelimit.integer < 0 ) { + G_Printf( "capturelimit %i is out of range, defaulting to 0\n", g_capturelimit.integer ); + trap_Cvar_Set( "capturelimit", "0" ); + trap_Cvar_Update( &g_capturelimit ); + } + if (g_gametype.integer >= GT_CTF && g_capturelimit.integer) { if (level.teamScores[TEAM_RED] >= g_capturelimit.integer) { @@ -2442,7 +2468,7 @@ void CheckTournament(void) int counts[TEAM_NUM_TEAMS]; qboolean notEnough = qfalse; - if (g_gametype.integer > GT_TEAM) { + if (g_gametype.integer >= GT_TEAM) { counts[TEAM_BLUE] = TeamCount(-1, TEAM_BLUE); counts[TEAM_RED] = TeamCount(-1, TEAM_RED); diff --git a/code/game/g_mover.c b/code/game/g_mover.c index bc1eda0c..7748d5c8 100644 --- a/code/game/g_mover.c +++ b/code/game/g_mover.c @@ -372,7 +372,7 @@ qboolean G_TryPushingEntity(gentity_t * check, gentity_t * pusher, vec3_t move, return qtrue; } // if it is ok to leave in the old position, do it - // this is only relevent for riding entities, not pushed + // this is only relevant for riding entities, not pushed // Sliding trapdoors can cause this. VectorCopy((pushed_p - 1)->origin, check->s.pos.trBase); if (check->client) { @@ -626,7 +626,7 @@ void G_MoverTeam(gentity_t * ent) obstacle = NULL; - // make sure all team slaves can move before commiting + // make sure all team slaves can move before committing // any moves or calling any think functions // if the move is blocked, all moved objects will be backed out pushed_p = pushed; @@ -1214,7 +1214,7 @@ void InitMover(gentity_t * ent) qboolean lightSet, colorSet; char *sound; - // if the "model2" key is set, use a seperate model + // if the "model2" key is set, use a separate model // for drawing, but clip against the brushes if (ent->model2) { ent->s.modelindex2 = G_ModelIndex(ent->model2); @@ -2276,7 +2276,7 @@ void Reached_Train(gentity_t * ent) vec3_t move; float length; - // copy the apropriate values + // copy the appropriate values next = ent->nextTrain; if (!next || !next->nextTrain) { return; // just stop diff --git a/code/game/g_session.c b/code/game/g_session.c index d5378a3d..79be0863 100644 --- a/code/game/g_session.c +++ b/code/game/g_session.c @@ -97,10 +97,6 @@ void G_WriteClientSessionData(gclient_t * client) //Slicer how about savedTeam ?! - if (!g_RQ3_matchmode.integer && g_gametype.integer >= GT_TEAM) { - //Reset teams on map changes / map_restarts, except on matchmode - client->sess.savedTeam = TEAM_SPECTATOR; - } s = va("%i %i %i %i %i %i %i %i %i %i %i", client->sess.sessionTeam, client->sess.spectatorTime, @@ -164,7 +160,9 @@ void G_ReadSessionData(gclient_t * client) if (g_gametype.integer == GT_CTF) { client->sess.sessionTeam = TEAM_SPECTATOR; - client->sess.savedTeam = TEAM_SPECTATOR; + if (!(g_entities[(int)(client-level.clients)].r.svFlags & SVF_BOT)) { + client->sess.savedTeam = TEAM_SPECTATOR; + } client->sess.captain = TEAM_FREE; client->sess.sub = TEAM_FREE; } @@ -201,25 +199,29 @@ void G_InitSessionData(gclient_t * client, char *userinfo) // JBravo: adding PERS_SAVEDTEAM client->ps.persistant[PERS_SAVEDTEAM] = TEAM_SPECTATOR; + // check for team preference, mainly for bots + value = Info_ValueForKey( userinfo, "teampref" ); + + // check for human's team preference set by start server menu + if ( !value[0] && g_localTeamPref.string[0] && client->pers.localClient ) { + value = g_localTeamPref.string; + + // clear team so it's only used once + trap_Cvar_Set( "g_localTeamPref", "" ); + } + // initial team determination if (g_gametype.integer >= GT_TEAM) { - if ( g_teamAutoJoin.integer && !(g_entities[ client - level.clients ].r.svFlags & SVF_BOT) ) { - if (g_gametype.integer == GT_TEAMPLAY) { - sess->savedTeam = PickTeam(-1); - client->ps.persistant[PERS_SAVEDTEAM] = sess->savedTeam; - } else if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_TEAM) { - sess->savedTeam = PickTeam(-1); - client->ps.persistant[PERS_SAVEDTEAM] = sess->savedTeam; - sess->sessionTeam = sess->savedTeam; - } else - sess->sessionTeam = PickTeam(-1); - BroadcastTeamChange(client, -1); - } else { - // always spawn as spectator in team games - sess->sessionTeam = TEAM_SPECTATOR; + // always spawn as spectator in team games + sess->sessionTeam = TEAM_SPECTATOR; + sess->spectatorState = SPECTATOR_FREE; + client->specMode = SPECTATOR_FREE; + sess->spectatorTime = level.time; + + if (value[0] || g_teamAutoJoin.integer) { + SetTeam(&g_entities[client - level.clients], value[0] ? value : "auto"); } } else { - value = Info_ValueForKey(userinfo, "team"); if (value[0] == 's') { // a willing spectator, not a waiting-in-line sess->sessionTeam = TEAM_SPECTATOR; @@ -245,11 +247,11 @@ void G_InitSessionData(gclient_t * client, char *userinfo) break; } } - } - sess->spectatorState = SPECTATOR_FREE; - client->specMode = SPECTATOR_FREE; - sess->spectatorTime = level.time; + sess->spectatorState = SPECTATOR_FREE; + client->specMode = SPECTATOR_FREE; + sess->spectatorTime = level.time; + } G_WriteClientSessionData(client); } @@ -290,6 +292,11 @@ void G_WriteSessionData(void) for (i = 0; i < level.maxclients; i++) { if (level.clients[i].pers.connected == CON_CONNECTED) { + if (!g_RQ3_matchmode.integer && g_gametype.integer >= GT_TEAM && !(g_entities[i].r.svFlags & SVF_BOT)) { + //Slicer: reset teams on map changes / map_restarts, except on matchmode + level.clients[i].sess.savedTeam = TEAM_SPECTATOR; + } + G_WriteClientSessionData(&level.clients[i]); } } diff --git a/code/game/g_spawn.c b/code/game/g_spawn.c index 07e2fc2f..db822b03 100644 --- a/code/game/g_spawn.c +++ b/code/game/g_spawn.c @@ -681,7 +681,7 @@ void G_ParseField(const char *key, const char *value, gentity_t * ent) G_SpawnGEntityFromSpawnVars Spawn an entity and fill in all of the level fields from -level.spawnVars[], then call the class specfic spawn function +level.spawnVars[], then call the class specific spawn function =================== */ void G_SpawnGEntityFromSpawnVars(void) diff --git a/code/game/g_svcmds.c b/code/game/g_svcmds.c index 953bb40c..090e235d 100644 --- a/code/game/g_svcmds.c +++ b/code/game/g_svcmds.c @@ -328,8 +328,8 @@ void Svcmd_EntityList_f(void) int e; gentity_t *check; - check = g_entities + 1; - for (e = 1; e < level.num_entities; e++, check++) { + check = g_entities; + for (e = 0; e < level.num_entities; e++, check++) { if (!check->inuse) { continue; } diff --git a/code/game/g_target.c b/code/game/g_target.c index 006c0a36..a0fd93f8 100644 --- a/code/game/g_target.c +++ b/code/game/g_target.c @@ -139,7 +139,7 @@ void Use_Target_Delay(gentity_t * ent, gentity_t * other, gentity_t * activator) void SP_target_delay(gentity_t * ent) { - // check delay for backwards compatability + // check delay for backwards compatibility if (!G_SpawnFloat("delay", "0", &ent->wait)) { G_SpawnFloat("wait", "1", &ent->wait); } diff --git a/code/game/g_team.c b/code/game/g_team.c index c73bc4c4..3e87cc9d 100644 --- a/code/game/g_team.c +++ b/code/game/g_team.c @@ -344,6 +344,7 @@ void Team_FragBonuses(gentity_t * targ, gentity_t * inflictor, gentity_t * attac } if (g_gametype.integer == GT_1FCTF) { + flag_pw = PW_NEUTRALFLAG; enemy_flag_pw = PW_NEUTRALFLAG; } // did the attacker frag the flag carrier? @@ -405,26 +406,6 @@ void Team_FragBonuses(gentity_t * targ, gentity_t * inflictor, gentity_t * attac return; } - if (targ->client->pers.teamState.lasthurtcarrier && - level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT) { - // attacker is on the same team as the skull carrier and - AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS); - - attacker->client->pers.teamState.carrierdefense++; - targ->client->pers.teamState.lasthurtcarrier = 0; - - //Blaze: Removed because it uses the persistant stats stuff - //attacker->client->ps.persistant[PERS_DEFEND_COUNT]++; - team = attacker->client->sess.sessionTeam; - // add the sprite over the player's head -/* attacker->client->ps.eFlags &= - ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | - EF_AWARD_CAP); - attacker->client->ps.eFlags |= EF_AWARD_DEFEND; - attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; */ - - return; - } // flag and flag carrier area defense bonuses // we have to find the flag and carrier entities // find the flag @@ -484,7 +465,7 @@ void Team_FragBonuses(gentity_t * targ, gentity_t * inflictor, gentity_t * attac if (carrier && carrier != attacker) { VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1); - VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1); + VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v2); if (((VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS && trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin)) || @@ -528,6 +509,10 @@ void Team_CheckHurtCarrier(gentity_t * targ, gentity_t * attacker) else flag_pw = PW_REDFLAG; + if (g_gametype.integer == GT_1FCTF) { + flag_pw = PW_NEUTRALFLAG; + } + // flags if (targ->client->ps.powerups[flag_pw] && targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam) attacker->client->pers.teamState.lasthurtcarrier = level.time; diff --git a/code/game/g_trigger.c b/code/game/g_trigger.c index c3b84232..4a7991b0 100644 --- a/code/game/g_trigger.c +++ b/code/game/g_trigger.c @@ -108,7 +108,7 @@ void InitTrigger(gentity_t * self) self->r.svFlags = SVF_NOCLIENT; } -/*QUAKED trigger_multiple (.5 .5 .5) ? DOOR +/*QUAKED trigger_multiple (.5 .5 .5) ? RED_ONLY BLUE_ONLY DOOR "wait" : Seconds between triggerings, 0.5 default, -1 = one time only. "random" wait variance, default is 0 Variable sized repeatable trigger. Must be targeted at one or more entities. @@ -527,7 +527,7 @@ Any entity that touches this will be hurt. It does dmg points of damage each server frame Targeting the trigger will toggle its on / off state. -SILENT supresses playing the sound +SILENT suppresses playing the sound SLOW changes the damage rate to once per second NO_PROTECTION *nothing* stops the damage diff --git a/code/game/g_utils.c b/code/game/g_utils.c index 24546107..8b19f1e0 100644 --- a/code/game/g_utils.c +++ b/code/game/g_utils.c @@ -516,7 +516,6 @@ gentity_t *G_Spawn(void) gentity_t *e; e = NULL; // shut up warning - i = 0; // shut up warning for (force = 0; force < 2; force++) { // if we go through all entities and can't find one to free, // override the normal minimum times before use @@ -534,11 +533,11 @@ gentity_t *G_Spawn(void) G_InitGentity(e); return e; } - if (i != MAX_GENTITIES) { + if (level.num_entities < ENTITYNUM_MAX_NORMAL) { break; } } - if (i == ENTITYNUM_MAX_NORMAL) { + if (level.num_entities == ENTITYNUM_MAX_NORMAL) { for (i = 0; i < MAX_GENTITIES; i++) { G_Printf("%4i: %s\n", i, g_entities[i].classname); } @@ -565,6 +564,11 @@ qboolean G_EntitiesFree(void) int i; gentity_t *e; + if (level.num_entities < ENTITYNUM_MAX_NORMAL) { + // can open a new slot if needed + return qtrue; + } + e = &g_entities[MAX_CLIENTS]; for (i = MAX_CLIENTS; i < level.num_entities; i++, e++) { if (e->inuse) { diff --git a/code/game/g_weapon.c b/code/game/g_weapon.c index ae719e6e..966886e7 100644 --- a/code/game/g_weapon.c +++ b/code/game/g_weapon.c @@ -695,6 +695,7 @@ qboolean ShotgunPellet(vec3_t start, vec3_t end, gentity_t * ent) int damage, i, passent; gentity_t *traceEnt; vec3_t tr_start, tr_end; + qboolean hitClient = qfalse; passent = ent->s.number; VectorCopy(start, tr_start); @@ -710,6 +711,10 @@ qboolean ShotgunPellet(vec3_t start, vec3_t end, gentity_t * ent) } if (traceEnt->takedamage) { + if (LogAccuracyHit(traceEnt, ent)) { + hitClient = qtrue; + } + //Elder: added to discern handcannon and m3 damage if (ent->client && ent->client->ps.weapon == WP_HANDCANNON) { //G_Printf("Firing handcannon\n"); @@ -721,7 +726,7 @@ qboolean ShotgunPellet(vec3_t start, vec3_t end, gentity_t * ent) G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_M3); } - if (LogAccuracyHit(traceEnt, ent)) { + if (hitClient) { return qtrue; } @@ -975,6 +980,9 @@ void Weapon_LightningFire(gentity_t * ent) traceEnt = &g_entities[tr.entityNum]; if (traceEnt->takedamage) { + if( LogAccuracyHit( traceEnt, ent ) ) { + ent->client->accuracy_hits++; + } G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING); } @@ -983,9 +991,6 @@ void Weapon_LightningFire(gentity_t * ent) tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte(tr.plane.normal); tent->s.weapon = ent->s.weapon; - if (LogAccuracyHit(traceEnt, ent)) { - ent->client->accuracy_hits++; - } } else if (!(tr.surfaceFlags & SURF_NOIMPACT)) { tent = G_TempEntity(tr.endpos, EV_MISSILE_MISS); tent->s.eventParm = DirToByte(tr.plane.normal);