From 51b6fa24f01c2c541580f494bd9a24a2d3bc9b2d Mon Sep 17 00:00:00 2001 From: zed Date: Fri, 24 Aug 2012 19:34:31 +0200 Subject: [PATCH 1/2] Initial Version of BB's code base based on SVN 649. --- code/botlib/be_ai_goal.c | 10 +- code/botlib/be_ai_move.c | 10 +- code/cgame/cg_draw.c | 77 +++-- code/cgame/cg_drawtools.c | 26 +- code/cgame/cg_event.c | 2 +- code/cgame/cg_info.c | 15 +- code/cgame/cg_local.h | 3 + code/cgame/cg_main.c | 15 +- code/game/ai_dmnet.c | 65 +++- code/game/ai_dmq3.c | 597 +++++++++++++++++++++----------- code/game/ai_main.c | 4 +- code/game/ai_main.h | 5 +- code/game/bg_misc.c | 63 +++- code/game/bg_pmove.c | 349 ++++++++++++++++++- code/game/bg_public.h | 1 + code/game/g_active.c | 15 +- code/game/g_bot.c | 4 +- code/game/g_client.c | 373 ++++++++++++++++---- code/game/g_cmds.c | 704 ++++++++++++++++++++++++++++++-------- code/game/g_combat.c | 133 ++----- code/game/g_items.c | 26 +- code/game/g_local.h | 23 +- code/game/g_main.c | 216 ++++++++++-- code/game/g_missile.c | 130 +++++++ code/game/g_mover.c | 18 +- code/game/g_spawn.c | 124 +++++++ code/game/g_svcmds.c | 351 +++++++++++++++++-- code/game/g_team.c | 2 +- code/qcommon/cvar.c | 13 + code/qcommon/files.c | 18 + code/qcommon/q_shared.c | 74 ++++ code/qcommon/q_shared.h | 5 +- code/server/server.h | 53 ++- code/server/sv_bot.c | 218 ++++++++++++ code/server/sv_ccmds.c | 644 +++++++++++++++++++++++++++++++++- code/server/sv_client.c | 65 +++- code/server/sv_init.c | 37 +- code/server/sv_main.c | 39 ++- code/server/sv_snapshot.c | 96 +++++- 39 files changed, 3940 insertions(+), 683 deletions(-) diff --git a/code/botlib/be_ai_goal.c b/code/botlib/be_ai_goal.c index 88c049fc..aab0ca71 100644 --- a/code/botlib/be_ai_goal.c +++ b/code/botlib/be_ai_goal.c @@ -1478,8 +1478,11 @@ int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelfla //if the bot is in solid or if the area the bot is in has no reachability links if (!areanum || !AAS_AreaReachability(areanum)) { +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "bot is in solid or area has no reachability links: %d %d\n",areanum,gs->lastreachabilityarea); +#endif //DEBUG //use the last valid area the bot was in - areanum = gs->lastreachabilityarea; + if (gs->lastreachabilityarea>0) {areanum = gs->lastreachabilityarea;} } //end if //remember the last area with reachabilities the bot was in gs->lastreachabilityarea = areanum; @@ -1567,7 +1570,12 @@ int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelfla bestweight = weight; bestitem = li; } //end if + } +#ifdef DEBUG + else { + botimport.Print(PRT_MESSAGE, "can't reach %d\n", li->entitynum); } //end if +#endif //DEBUG } //end if } //end if } //end for diff --git a/code/botlib/be_ai_move.c b/code/botlib/be_ai_move.c index 7aa5908f..3e86bd88 100644 --- a/code/botlib/be_ai_move.c +++ b/code/botlib/be_ai_move.c @@ -656,10 +656,10 @@ int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avo switch(reach->traveltype & TRAVELTYPE_MASK) { + case TRAVEL_LADDER: checkbetween = qtrue; break;// patch bots: put priority on ladder travel case TRAVEL_WALK: checkbetween = qtrue; break; case TRAVEL_CROUCH: checkbetween = qtrue; break; case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break; - case TRAVEL_LADDER: checkbetween = qtrue; break; case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break; case TRAVEL_JUMP: checkbetween = qfalse; break; case TRAVEL_SWIM: checkbetween = qtrue; break; @@ -1302,7 +1302,7 @@ void BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_mover // Joe Kari: applying the patch of The Doctor: http://forum.smokin-guns.org/viewtopic.php?f=28&t=3075 //if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) - if ( trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE ) + if ( trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE )// patch bots: fixes some problems of bots running into things (removed !trace.startsolid && ) { result->blocked = qtrue; result->blockentity = trace.ent; @@ -2935,10 +2935,10 @@ int BotReachabilityTime(aas_reachability_t *reach) { switch(reach->traveltype & TRAVELTYPE_MASK) { + case TRAVEL_LADDER: return 6;// patch bots: put priority on ladder travel case TRAVEL_WALK: return 5; case TRAVEL_CROUCH: return 5; case TRAVEL_BARRIERJUMP: return 5; - case TRAVEL_LADDER: return 6; case TRAVEL_WALKOFFLEDGE: return 5; case TRAVEL_JUMP: return 5; case TRAVEL_SWIM: return 5; @@ -3327,10 +3327,10 @@ void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, in #endif //DEBUG switch(reach.traveltype & TRAVELTYPE_MASK) { + case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;// patch bots: put priority on ladder travel case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break; case TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break; case TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break; - case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; case TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break; case TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break; case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; @@ -3436,10 +3436,10 @@ void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, in // switch(reach.traveltype & TRAVELTYPE_MASK) { + case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;// patch bots: put priority on ladder travel case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;//BotFinishTravel_Walk(ms, &reach); break; case TRAVEL_CROUCH: /*do nothing*/ break; case TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break; - case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; case TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break; case TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break; case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c index 065b7ae6..023ca11c 100644 --- a/code/cgame/cg_draw.c +++ b/code/cgame/cg_draw.c @@ -898,6 +898,38 @@ static float CG_DrawAttacker( float y ) { } #endif +/* +================ +CG_DrawSpeedMeter + +================ +*/ +static float CG_DrawSpeedMeter( float y ) { + char *s; + int w; + vec_t *vel; + int speed; + + /* speed meter can get in the way of the scoreboard */ + if ( cg.scoreBoardShowing ) { + return y; + } + + vel = cg.snap->ps.velocity; + /* ignore vertical component of velocity */ + speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]); + + s = va( "%iu/s", speed ); + + //w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + w = CG_DrawStrlen( s ) * 11; + + //CG_DrawBigString( 635 - w, y + 2, s, 1.0F); + CG_Text_Paint(640-w, y + 5 + BIGCHAR_HEIGHT, 0.4f, colorWhite, s, 0, 0, 3); + return y + BIGCHAR_HEIGHT + 4; +} + + /* ================== CG_DrawSnapshot @@ -1508,6 +1540,9 @@ static void CG_DrawUpperRight(stereoFrame_t stereoFrame) if ( cg_drawTimer.integer ) { y = CG_DrawTimer( y ); } + if ( cg_drawspeed.integer ) { + y = CG_DrawSpeedMeter( y ); + } #ifdef SMOKINGUNS if ( cg_drawdebug.integer ) { // Draw debug info on the upper right @@ -2521,23 +2556,23 @@ void CG_CenterPrint( const char *str, int y, int charWidth ) { // remove "^" if nessecary for(i = 0; cg.centerPrint[i]; i++){ - // ignore if there are two of them - if(cg.centerPrint[i] == '^'){ - remove = qtrue; - continue; - } - - if(remove){ - int j; - - // move the others two steps further - for( j = i-1; cg.centerPrint[j+2]; j++){ - cg.centerPrint[j] = cg.centerPrint[j+2]; - } - cg.centerPrint[j] = '\0'; - remove = qfalse; - i -= 2; // string had been moved back (2 shifts) - } +// // ignore if there are two of them// patch colored names: allow colors +// if(cg.centerPrint[i] == '^'){ +// remove = qtrue; +// continue; +// } +// +// if(remove){ +// int j; +// +// // move the others two steps further +// for( j = i-1; cg.centerPrint[j+2]; j++){ +// cg.centerPrint[j] = cg.centerPrint[j+2]; +// } +// cg.centerPrint[j] = '\0'; +// remove = qfalse; +// i -= 2; // string had been moved back (2 shifts) +// } } #endif @@ -4078,8 +4113,7 @@ static void CG_DrawBuyMenu( void ) { item = CG_GetBuyItem(); - if(cg.snap->ps.stats[STAT_MONEY] < item->prize) - return; + if(cg.snap->ps.stats[STAT_MONEY] >= item->prize) { // don't buy other special weapons if handling a gatling if(item->giType == IT_WEAPON && bg_weaponlist[item->giTag].wp_sort == WPS_GUN && gatling){ @@ -4116,7 +4150,10 @@ static void CG_DrawBuyMenu( void ) { cg.weaponSelect = item->giTag; } } - + } else { + trap_S_StartSound(NULL, cg.snap->ps.clientNum, CHAN_ANNOUNCER, cgs.media.bang[1]);// patch buy: if wecannot buy, don't get stuck in the menu + cg.oldbutton = qtrue; + } if(numpressed){ CG_CloseBuyMenu(); } diff --git a/code/cgame/cg_drawtools.c b/code/cgame/cg_drawtools.c index abc28ff9..22b3f9b6 100644 --- a/code/cgame/cg_drawtools.c +++ b/code/cgame/cg_drawtools.c @@ -789,7 +789,7 @@ void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color if ( style & UI_DROPSHADOW ) { drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; - drawcolor[3] = color[3]; + drawcolor[3] = 0.5;// patch colored loading screen: implement colored dropshadows UI_DrawBannerString2( x+2, y+2, str, drawcolor ); } @@ -808,7 +808,10 @@ int UI_ProportionalStringWidth( const char* str ) { while ( *s ) { ch = *s & 127; charWidth = propMap[ch][2]; - if ( charWidth != -1 ) { + if ( Q_IsColorString( s ) ) {// patch colored loading screen: implement colored dropshadows + s += 2; + continue; + } else if ( charWidth != -1 ) { width += charWidth; width += PROP_GAP_WIDTH; } @@ -835,6 +838,7 @@ void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, fl float fcol; float fwidth; float fheight; + vec4_t newColor; // draw the colored text trap_R_SetColor( color ); @@ -846,7 +850,21 @@ void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, fl while ( *s ) { ch = *s & 127; - if ( ch == ' ' ) { + if ( Q_IsColorString( s ) ) {// patch colored loading screen: implement colored dropshadows + memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); + newColor[3] = color[3]; + if (color[0]!=1 && color[1]!=1 && color[2]!=1 && color[3]==0.5) { + newColor[0]*=0.25; + newColor[1]*=0.25; + newColor[2]*=0.25; + newColor[3]=0.85; + } else { + newColor[3]=1; + } + trap_R_SetColor( newColor ); + s += 2; + continue; + } else if ( ch == ' ' ) { aw = (float)PROP_SPACE_WIDTH * cgs.screenXScale * sizeScale; } else if ( propMap[ch][2] != -1 ) { fcol = (float)propMap[ch][0] / 256.0f; @@ -916,7 +934,7 @@ void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t if ( style & UI_DROPSHADOW ) { drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; - drawcolor[3] = color[3]; + drawcolor[3] = 0.5;// patch colored loading screen: implement colored dropshadows UI_DrawProportionalString2( x+2, y+2, str, drawcolor, sizeScale, cgs.media.charsetProp ); } diff --git a/code/cgame/cg_event.c b/code/cgame/cg_event.c index 6f3b6b36..a213c92b 100644 --- a/code/cgame/cg_event.c +++ b/code/cgame/cg_event.c @@ -575,7 +575,7 @@ static void CG_Obituary( entityState_t *ent ) { if(weapon){ if ( ent->eFlags & EF_SAME_TEAM ) CG_Printf("%s ^1teamkilled^7 %s with %s.\n", attackerName, targetName, bg_weaponlist[weapon].name ); - else + else if ( cg_killmsg.integer == 1 )// patch messages: optionally disable kill messages in chat window. They're still shown in the top right corner. CG_Printf("%s killed %s with %s.\n", attackerName, targetName, bg_weaponlist[weapon].name ); return; } diff --git a/code/cgame/cg_info.c b/code/cgame/cg_info.c index 68210367..47ff0cb2 100644 --- a/code/cgame/cg_info.c +++ b/code/cgame/cg_info.c @@ -228,13 +228,14 @@ void CG_LoadingClient( int clientNum ) { } Q_strncpyz( personality, Info_ValueForKey( info, "n" ), sizeof(personality) ); + CG_LoadingString( personality );// patch colored loading screen: draw names before cleaning name Q_CleanStr( personality ); if( cgs.gametype == GT_SINGLE_PLAYER ) { trap_S_RegisterSound( va( "sound/player/announce/%s.wav", personality ), qtrue ); } - CG_LoadingString( personality ); + } @@ -324,7 +325,7 @@ void CG_DrawInformation( void ) { #ifndef SMOKINGUNS y = 180-32; #else - y = 30; + y = 18;// patch colored loading screen #endif // don't print server lines if playing a local game @@ -332,10 +333,10 @@ void CG_DrawInformation( void ) { if ( !atoi( buf ) ) { // server hostname Q_strncpyz(buf, Info_ValueForKey( info, "sv_hostname" ), 1024); - Q_CleanStr(buf); + //Q_CleanStr(buf); UI_DrawProportionalString( 320, y, buf, - UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); - y += PROP_HEIGHT; + UI_CENTER|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT+10; // pure server s = Info_ValueForKey( sysInfo, "sv_pure" ); @@ -375,7 +376,8 @@ void CG_DrawInformation( void ) { // cheats warning s = Info_ValueForKey( sysInfo, "sv_cheats" ); if ( s[0] == '1' ) { - UI_DrawProportionalString( 320, y, "CHEATS ARE ENABLED", + y += PROP_HEIGHT; + UI_DrawProportionalString( 320, y, "^1CHEATS ARE ENABLED!",// patch colored loading screen UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; #ifndef SMOKINGUNS @@ -437,6 +439,7 @@ void CG_DrawInformation( void ) { s = "Unknown Gametype"; break; } + y += PROP_HEIGHT; UI_DrawProportionalString( 320, y, s, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index 35809857..1f57f21f 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -1812,6 +1812,7 @@ extern vmCvar_t cg_serverduellimit; extern vmCvar_t cg_impactparticles; extern vmCvar_t cg_gunsmoke; extern vmCvar_t cg_addguns; +extern vmCvar_t cg_killmsg; extern vmCvar_t cg_hitmsg; extern vmCvar_t cg_hitfarmsg; extern vmCvar_t cg_ownhitmsg; @@ -1821,6 +1822,8 @@ extern vmCvar_t cg_debug; extern vmCvar_t cg_glowflares; extern vmCvar_t cg_boostfps; extern vmCvar_t cg_drawdebug; +extern vmCvar_t cg_drawspeed; + //extern vmCvar_t cg_teamredcount; //extern vmCvar_t cg_teambluecount; diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c index d0f17d4d..f8d199e8 100644 --- a/code/cgame/cg_main.c +++ b/code/cgame/cg_main.c @@ -212,6 +212,7 @@ vmCvar_t cg_oldPlasma; vmCvar_t cg_trueLightning; #endif +vmCvar_t g_climbable;// patch g_climbable: shared between game and cgame for compiling issues vmCvar_t cg_redTeamName; vmCvar_t cg_blueTeamName; vmCvar_t cg_currentSelectedPlayer; @@ -254,6 +255,7 @@ vmCvar_t cg_serverduellimit; vmCvar_t cg_impactparticles; vmCvar_t cg_gunsmoke; vmCvar_t cg_addguns; +vmCvar_t cg_killmsg; vmCvar_t cg_hitmsg; vmCvar_t cg_hitfarmsg; vmCvar_t cg_ownhitmsg; @@ -263,6 +265,7 @@ vmCvar_t cg_debug; vmCvar_t cg_glowflares; vmCvar_t cg_boostfps; vmCvar_t cg_drawdebug; +vmCvar_t cg_drawspeed; //music volume vmCvar_t cg_musicvolume; @@ -440,8 +443,8 @@ static cvarTable_t cvarTable[] = { #endif { &cg_blood, "com_blood", "1", CVAR_ARCHIVE }, { &cg_synchronousClients, "g_synchronousClients", "0", 0 }, // communicated by systeminfo - { &cg_redTeamName, "g_redteam", DEFAULT_REDTEAM_NAME, CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO }, - { &cg_blueTeamName, "g_blueteam", DEFAULT_BLUETEAM_NAME, CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO }, + { &cg_redTeamName, "g_redteam", DEFAULT_REDTEAM_NAME, CVAR_ARCHIVE | CVAR_SERVERINFO },// patch cleanup user variables + { &cg_blueTeamName, "g_blueteam", DEFAULT_BLUETEAM_NAME, CVAR_ARCHIVE | CVAR_SERVERINFO }, { &cg_currentSelectedPlayer, "cg_currentSelectedPlayer", "0", CVAR_ARCHIVE}, { &cg_currentSelectedPlayerName, "cg_currentSelectedPlayerName", "", CVAR_ARCHIVE}, { &cg_singlePlayer, "ui_singlePlayerActive", "0", CVAR_USERINFO}, @@ -472,7 +475,7 @@ static cvarTable_t cvarTable[] = { { &cg_timescaleFadeSpeed, "cg_timescaleFadeSpeed", "0", 0}, { &cg_timescale, "timescale", "1", 0}, #ifndef SMOKINGUNS - { &cg_scorePlum, "cg_scorePlums", "1", CVAR_USERINFO | CVAR_ARCHIVE}, + { &cg_scorePlum, "cg_scorePlums", "1", CVAR_ARCHIVE}, { &cg_smoothClients, "cg_smoothClients", "0", CVAR_USERINFO | CVAR_ARCHIVE}, #endif { &cg_cameraMode, "com_cameraMode", "0", CVAR_CHEAT}, @@ -495,7 +498,7 @@ static cvarTable_t cvarTable[] = { { &sv_fps, "sv_fps", "20", 0 }, { &cg_projectileNudge, "cg_projectileNudge", "0", CVAR_ARCHIVE }, { &cg_optimizePrediction, "cg_optimizePrediction", "1", CVAR_ARCHIVE }, - { &cl_timeNudge, "cl_timeNudge", "0", CVAR_ARCHIVE }, + { &cl_timeNudge, "cl_timeNudge", "0", CVAR_ARCHIVE | CVAR_USERINFO }, { &cg_latentSnaps, "cg_latentSnaps", "0", CVAR_USERINFO | CVAR_CHEAT }, { &cg_latentCmds, "cg_latentCmds", "0", CVAR_USERINFO | CVAR_CHEAT }, { &cg_plOut, "cg_plOut", "0", CVAR_USERINFO | CVAR_CHEAT }, @@ -522,6 +525,8 @@ static cvarTable_t cvarTable[] = { { &cg_glowflares, "cg_glowflares", "1", CVAR_ARCHIVE }, { &cg_boostfps, "cg_boostfps", "1", CVAR_ARCHIVE }, { &cg_drawdebug, "cg_drawdebug", "0", CVAR_CHEAT }, + { &cg_drawspeed, "cg_drawspeed", "0", CVAR_ARCHIVE }, + { &cg_killmsg, "cg_killmsg", "0", CVAR_ARCHIVE }, { &cg_hitmsg, "cg_hitmsg", "1", CVAR_ARCHIVE }, { &cg_hitfarmsg, "cg_hitfarmsg", "1", CVAR_ARCHIVE }, { &cg_ownhitmsg, "cg_ownhitmsg", "1", CVAR_ARCHIVE }, @@ -1919,7 +1924,7 @@ void CG_BuildSpectatorString(void) { if (cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_SPECTATOR && (cgs.gametype != GT_DUEL || client == -1 || cg.scores[client].realspec)) { #endif - Q_strcat(cg.spectatorList, sizeof(cg.spectatorList), va("%s ", cgs.clientinfo[i].name)); + Q_strcat(cg.spectatorList, sizeof(cg.spectatorList), va("^7%s^3, ", cgs.clientinfo[i].name));// patch spectator-list: correctly colored } } i = strlen(cg.spectatorList); diff --git a/code/game/ai_dmnet.c b/code/game/ai_dmnet.c index 7830d371..a23528d7 100644 --- a/code/game/ai_dmnet.c +++ b/code/game/ai_dmnet.c @@ -805,8 +805,12 @@ int BotGetItemLongTermGoal(bot_state_t *bs, int tfl, bot_goal_t *goal) { // break the loop if there is no real goal if(count >= 10){ // bot "needs" time to buy something - if(bs->buytime > level.time) + if(bs->buytime > level.time) { +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "wait buy %d>%d.", bs->buytime, level.time); +#endif //DEBUG return qfalse; + } BotAI_LookForNodes(bs, goal, vec_money[0], vec_money[1]); trap_BotEmptyGoalStack(bs->gs); @@ -816,9 +820,14 @@ int BotGetItemLongTermGoal(bot_state_t *bs, int tfl, bot_goal_t *goal) { //choose a new goal if (trap_BotChooseLTGItem(bs->gs, bs->origin, bs->inventory, tfl)) { bs->ltg_time = FloatTime() + 20; +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "new goal."); +#endif //DEBUG } else {//the bot gets sorta stuck with all the avoid timings, shouldn't happen though - +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "avoid stuck."); +#endif //DEBUG //reset the avoid goals and the avoid reach trap_BotResetAvoidGoals(bs->gs); trap_BotResetAvoidReach(bs->ms); @@ -828,10 +837,16 @@ int BotGetItemLongTermGoal(bot_state_t *bs, int tfl, bot_goal_t *goal) { valid = (goal->entitynum >= 0 && goal->entitynum < MAX_GENTITIES); - if(!valid) + if(!valid) { +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "invalid."); +#endif //DEBUG continue; - + } if(valid){ +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "valid."); +#endif //DEBUG /*if(g_gametype.integer == GT_BR && bs->cur_ps.persistant[PERS_ROBBER] && !Q_stricmp(g_entities[goal->entitynum].classname, "item_money") && !(g_entities[goal->entitynum].r.svFlags & SVF_NOCLIENT)){ @@ -877,7 +892,9 @@ int BotGetItemLongTermGoal(bot_state_t *bs, int tfl, bot_goal_t *goal) { continue; }*/ } - +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "grabbable %d.", BG_CanItemBeGrabbed(g_gametype.integer, &g_entities[goal->entitynum].s, &bs->cur_ps)); +#endif //DEBUG } while (!valid || (bs->flags & BFL_AVOID) || !Q_stricmp(g_entities[goal->entitynum].classname, "freed") || !Q_stricmp(g_entities[goal->entitynum].classname, "player") || @@ -1009,7 +1026,7 @@ int BotGetLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal) if (DotProduct(dir, dir2) > 0.7) { // back up BotSetupForMovement(bs); - trap_BotMoveInDirection(bs->ms, dir2, 400, MOVE_WALK); + //trap_BotMoveInDirection(bs->ms, dir2, 400, MOVE_WALK);// patch bots: do not use the primitive BotMoveInDirection } } } @@ -2166,6 +2183,8 @@ int AINode_Seek_ActivateEntity(bot_state_t *bs) { bsp_trace_t bsptrace; aas_entityinfo_t entinfo; + BotAI_Print(PRT_MESSAGE, "Seek_ActivateEntity\n"); + if (BotIsObserver(bs)) { BotClearActivateGoalStack(bs); AIEnter_Observer(bs, "active entity: observer"); @@ -2318,7 +2337,7 @@ int AINode_Seek_ActivateEntity(bot_state_t *bs) { bs->activatestack->time = 0; } //check if the bot is blocked - BotAIBlocked(bs, &moveresult, qtrue); + BotAIBlocked(bs, &moveresult, qtrue);// AINode_Seek_ActivateEntity } // BotClearPath(bs, &moveresult); @@ -2426,6 +2445,9 @@ int AINode_Seek_NBG(bot_state_t *bs) { #ifdef SMOKINGUNS qboolean override = ((bs->flags & BFL_AVOID )||( bs->flags & BFL_ESCAPE)); #endif +#ifdef DEBUG + Com_Printf("seek NBG:\n"); +#endif //DEBUG if (BotIsObserver(bs)) { AIEnter_Observer(bs, "seek nbg: observer"); @@ -2493,8 +2515,11 @@ int AINode_Seek_NBG(bot_state_t *bs) { return qfalse; } //predict obstacles - if (BotAIPredictObstacles(bs, &goal)) + if (BotAIPredictObstacles(bs, &goal)) { + Com_Printf("some obstacle\n"); return qfalse; + } + //initialize the movement state BotSetupForMovement(bs); @@ -2601,15 +2626,22 @@ AINode_Seek_LTG */ int AINode_Seek_LTG(bot_state_t *bs) { + vec3_t oldOrigin, diff; bot_goal_t goal; vec3_t target, dir; bot_moveresult_t moveresult; int range; -#ifdef SMOKINGUNS + + #ifdef SMOKINGUNS qboolean override = ((bs->flags & BFL_AVOID )||( bs->flags & BFL_ESCAPE)); #endif //char buf[128]; //bot_goal_t tmpgoal; +#ifdef DEBUG + Com_Printf("AINode_Seek_LTG\n"); +#endif //DEBUG + + VectorCopy(bs->origin,oldOrigin); if (BotIsObserver(bs)) { AIEnter_Observer(bs, "seek ltg: observer"); @@ -2674,10 +2706,16 @@ int AINode_Seek_LTG(bot_state_t *bs) } // BotTeamGoals(bs, qfalse); +#ifdef DEBUG + Com_Printf("longtermgoal."); +#endif //get the current long term goal if (!BotLongTermGoal(bs, bs->tfl, qfalse, &goal)) { return qtrue; } +#ifdef DEBUG + Com_Printf("goal: %d. nearbygoal.\n", goal.number); +#endif //check for nearby goals periodicly #ifndef SMOKINGUNS if (bs->check_time < FloatTime()) { @@ -2723,9 +2761,14 @@ int AINode_Seek_LTG(bot_state_t *bs) return qfalse; } } + //predict obstacles - if (BotAIPredictObstacles(bs, &goal)) + if (BotAIPredictObstacles(bs, &goal)) { +#ifdef DEBUG + Com_Printf("Some obstacle\n"); +#endif return qfalse; + } //initialize the movement state BotSetupForMovement(bs); @@ -2790,6 +2833,8 @@ int AINode_Seek_LTG(bot_state_t *bs) //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; // + // VectorSubtract(oldOrigin,bs->origin,diff); // DEBUG + // BotAI_Print(PRT_MESSAGE, "movement %d %d %d\n", moveresult.blockentity,moveresult.blocked,moveresult.flags); return qtrue; } diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index 566b9c06..da9ce370 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -1605,11 +1605,11 @@ int BotSynonymContext(bot_state_t *bs) { } #ifdef SMOKINGUNS -int BotChooseBestWeapon(playerState_t *ps, int oldweapon){ +int BotChooseBestWeapon(playerState_t *ps, int oldweapon){// patch bot: adding molotov, dynamite and gatling int i; // first look for weapons which already have ammo in it - for(i=WP_DYNAMITE-1; i>0; i--){ + for(i=WP_MOLOTOV; i>0; i--){// allow bots to use all weapons if(ps->stats[STAT_WEAPONS] & (1 << i) && (ps->ammo[i] || ps->ammo[bg_weaponlist[i].clip])){ @@ -1636,26 +1636,13 @@ void BotChooseWeapon(bot_state_t *bs) { if (bs->cur_ps.weaponstate == WEAPON_RAISING || bs->cur_ps.weaponstate == WEAPON_DROPPING) { -#ifndef SMOKINGUNS - trap_EA_SelectWeapon(bs->client, bs->weaponnum); -#else bs->cmdweapon = bs->weaponnum; -#endif - } - else { -#ifndef SMOKINGUNS - newweaponnum = trap_BotChooseBestFightWeapon(bs->ws, bs->inventory); -#else + } else { newweaponnum = BotChooseBestWeapon(&bs->cur_ps, bs->weaponnum); -#endif if (bs->weaponnum != newweaponnum) bs->weaponchange_time = FloatTime(); bs->weaponnum = newweaponnum; //BotAI_Print(PRT_MESSAGE, "bs->weaponnum = %d\n", bs->weaponnum); -#ifndef SMOKINGUNS - trap_EA_SelectWeapon(bs->client, bs->weaponnum); -#else bs->cmdweapon = bs->weaponnum; -#endif } } @@ -1669,7 +1656,39 @@ qboolean BotAI_FindNearestNode(qboolean valid, bot_state_t *bs, vec3_t org, vec3 #endif void BotSetupForMovement(bot_state_t *bs) { bot_initmove_t initmove; - + bot_activategoal_t activategoal; + vec3_t forward; + int entitynum,i,dist,lastdist; + entitynum=0; + lastdist=1000000; + +// DEBUG treat gatling as activatable entity => no bsp entity +// for(i=0; iorigin, forward); +// //the distance towards the gatling +// dist = VectorNormalize(forward); +// if (dist<1000 && (entitynum==0 || lastdist>dist)) { +// entitynum = i; +// lastdist = dist; +// BotAI_Print(PRT_MESSAGE, "picked a gatling %d", i); +// } +// } +// } +// if (entitynum>0) { +// if (BotGetActivateGoal(bs, entitynum, &activategoal)) { +// //memset(activategoal, 0, sizeof(bot_activategoal_t)); +// //activategoal. = g_entities[i]. +// +// if (bs->activatestack && !bs->activatestack->inuse) +// bs->activatestack = NULL; +// // if not already trying to activate this entity +// if (!BotIsGoingToActivateEntity(bs, activategoal.goal.entitynum)) { +// BotAI_Print(PRT_MESSAGE, "go for gatling %d",entitynum); +// BotGoForActivateGoal(bs, &activategoal); +// } +// } +// } #ifdef SMOKINGUNS // the first goal for the bot in br, if he has the goldbag is the escape area /*if(g_gametype.integer == GT_BR && @@ -1716,6 +1735,9 @@ void BotSetupForMovement(bot_state_t *bs) { if ((bs->cur_ps.pm_flags & PMF_TIME_WATERJUMP) && (bs->cur_ps.pm_time > 0)) { initmove.or_moveflags |= MFL_WATERJUMP; } + if ((bs->cur_ps.torsoAnim==BOTH_LADDER || bs->cur_ps.torsoAnim==BOTH_LADDER_STAND) && (bs->cur_ps.pm_time > 0)) {// thedoctor + initmove.or_moveflags |= MFL_AGAINSTLADDER; + } //set presence type if (bs->cur_ps.pm_flags & PMF_DUCKED) initmove.presencetype = PRESENCE_CROUCH; else initmove.presencetype = PRESENCE_NORMAL; @@ -2462,10 +2484,10 @@ float BotFeelingBad(bot_state_t *bs) { BotWantsToRetreat ================== */ -#ifndef SMOKINGUNS + int BotWantsToRetreat(bot_state_t *bs) { aas_entityinfo_t entinfo; - +#ifndef SMOKINGUNS if (gametype == GT_CTF) { //always retreat when carrying a CTF flag if (BotCTFCarryingFlag(bs)) @@ -2506,11 +2528,12 @@ int BotWantsToRetreat(bot_state_t *bs) { if (bs->ltgtype == LTG_GETFLAG) return qtrue; // +#endif if (BotAggression(bs) < 50) return qtrue; return qfalse; } -#endif + /* ================== @@ -2837,6 +2860,29 @@ void BotRoamGoal(bot_state_t *bs, vec3_t goal) { VectorCopy(bestorg, goal); } +int BotMoveInDir(bot_moveresult_t *moveresult, bot_state_t *bs, vec3_t direction, int distance, int movetype, int tfl) { + vec3_t newpoint; + aas_entityinfo_t entinfo; + bot_goal_t goal; + + + //get the enemy entity info + BotEntityInfo(bs->enemy, &entinfo); + + VectorNormalize(direction); + VectorMA(bs->origin,distance,direction,newpoint); + + goal.entitynum = bs->enemy; + goal.areanum = bs->lastenemyareanum; + VectorCopy(newpoint, goal.origin); + VectorSet(goal.mins, -8, -8, -8); + VectorSet(goal.maxs, 8, 8, 8); + //initialize the movement state + BotSetupForMovement(bs); + //move towards the goal + trap_BotMoveToGoal(moveresult, bs->ms, &goal, tfl); + return !moveresult->blocked; +} /* ================== BotAttackMove @@ -2850,8 +2896,21 @@ bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { aas_entityinfo_t entinfo; bot_moveresult_t moveresult; bot_goal_t goal; + qboolean goback = qtrue; attackentity = bs->enemy; + //get the enemy entity info + BotEntityInfo(attackentity, &entinfo); + //direction towards the enemy + VectorSubtract(entinfo.origin, bs->origin, forward); + //the distance towards the enemy + dist = VectorNormalize(forward); + + //if the bot uses a gatling + if(dist>60 && bs->weaponnum == WP_GATLING && g_entities[bs->client].client->ps.stats[STAT_GATLING_MODE]) { + return moveresult; + } + // if (bs->attackchase_time > FloatTime()) { //create the chase goal @@ -2871,23 +2930,38 @@ bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { // attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); jumper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_JUMPER, 0, 1); + jumper = 0.1f; croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); - //if the bot is really stupid - if (attack_skill < 0.2) return moveresult; + if (bs->enemy && entinfo.weapon==WP_KNIFE) {// run!!! + croucher = 0.0f; + jumper = 0.0f; + } else { + if (bs->cur_ps.weapon == WP_KNIFE) {// knife = run + croucher = 0.04f; + jumper = 0.01f; + } + else if ((bs->flags & BFL_RELOAD) || (bs->flags & BFL_RELOAD2)) {// reload = jump'n'run + croucher = 0.05f; + jumper = 0.95f; + } + if (bs->cur_ps.groundEntityNum == ENTITYNUM_NONE) {// fall = crouch + croucher = 1.0f; + moveresult.type = MOVE_CROUCH; + return moveresult; + } + } + + +// //if the bot is really stupid +// if (attack_skill < 0.2) return moveresult; //initialize the movement state BotSetupForMovement(bs); - //get the enemy entity info - BotEntityInfo(attackentity, &entinfo); - //direction towards the enemy - VectorSubtract(entinfo.origin, bs->origin, forward); - //the distance towards the enemy - dist = VectorNormalize(forward); + VectorNegate(forward, backward); //walk, crouch or jump movetype = MOVE_WALK; // if (bs->attackcrouch_time < FloatTime() - 1) { -#ifndef SMOKINGUNS if (random() < jumper) { movetype = MOVE_JUMP; } @@ -2895,11 +2969,6 @@ bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { else if (bs->attackcrouch_time < FloatTime() - 1 && random() < croucher) { bs->attackcrouch_time = FloatTime() + croucher * 5; } -#else - if (bs->attackcrouch_time < FloatTime() - 1 && random() < croucher) { - bs->attackcrouch_time = FloatTime() + croucher * 5; - } -#endif } if (bs->attackcrouch_time > FloatTime()) movetype = MOVE_CROUCH; //if the bot should jump @@ -2912,34 +2981,60 @@ bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { bs->attackjump_time = FloatTime() + 1; } } -#ifndef SMOKINGUNS - if (bs->cur_ps.weapon == WP_GAUNTLET) { -#else - if (bs->cur_ps.weapon == WP_KNIFE) { -#endif - attack_dist = 0; - attack_range = 0; - } - else { - attack_dist = IDEAL_ATTACKDIST; - attack_range = 40; + + if (bs->weaponnum == WP_KNIFE || (entinfo.weapon==WP_KNIFE && dist<200) || ((entinfo.flags & BFL_RELOAD || bs->flags & BFL_RELOAD2) && dist<140) || dist<70) { + // attack! + movetype = MOVE_WALK; + bs->weaponnum=WP_KNIFE; + if (dist<60 || (g_entities[bs->client].client->ps.ammo[WP_KNIFE]>1 && dist<1500)) { + if (dist<20) { + g_entities[bs->client].client->ps.stats[STAT_WP_MODE]=0; } - //if the bot is stupid - if (attack_skill <= 0.4) { - //just walk to or away from the enemy - if (dist > attack_dist + attack_range) { - if (trap_BotMoveInDirection(bs->ms, forward, 400, movetype)) return moveresult; + trap_EA_Attack(bs->client); } - if (dist < attack_dist - attack_range) { - if (trap_BotMoveInDirection(bs->ms, backward, 400, movetype)) return moveresult; + if (dist>40 || random() > 0.66) { + goback = qfalse; + if (BotMoveInDir(&moveresult, bs, forward, 400, movetype, tfl)) { + return moveresult; } +// if (trap_BotMoveInDirection(bs->ms, forward, 400, movetype)) { +// return moveresult; +// } + } else { + if (BotMoveInDir(&moveresult, bs, backward, 400, movetype, tfl)) { return moveresult; } +// if (trap_BotMoveInDirection(bs->ms, backward, 400, movetype)) { +// return moveresult; +// } + } + } + + + + if (bs->cur_ps.weapon == WP_KNIFE) { + attack_dist = 30; + attack_range = 0; + } else { + attack_dist = IDEAL_ATTACKDIST; + attack_range = 40; + } +// //if the bot is stupid +// if (attack_skill <= 0.4) { +// //just walk to or away from the enemy +// if (dist > attack_dist + attack_range) { +// if (trap_BotMoveInDirection(bs->ms, forward, 400, movetype)) return moveresult; +// } +// if (dist < attack_dist - attack_range) { +// if (trap_BotMoveInDirection(bs->ms, backward, 400, movetype)) return moveresult; +// } +// return moveresult; +// } //increase the strafe time bs->attackstrafe_time += bs->thinktime; //get the strafe change time - strafechange_time = 0.4 + (1 - attack_skill) * 0.2; - if (attack_skill > 0.7) strafechange_time += crandom() * 0.2; + strafechange_time = 0.4 + (1 - attack_skill) * 0.3;// 0.4 + thedoctor + if (attack_skill > 0.7) strafechange_time += crandom() * 0.3; //if the strafe direction should be changed if (bs->attackstrafe_time > strafechange_time) { //some magic number :) @@ -2950,7 +3045,6 @@ bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { } } // - for (i = 0; i < 2; i++) { hordir[0] = forward[0]; hordir[1] = forward[1]; hordir[2] = 0; @@ -2960,27 +3054,68 @@ bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { //reverse the vector depending on the strafe direction if (bs->flags & BFL_STRAFERIGHT) VectorNegate(sideward, sideward); //randomly go back a little - if (random() > 0.9) { - VectorAdd(sideward, backward, sideward); - } - else { +// if (random() > 0.9) { +// VectorAdd(sideward, backward, sideward); +// } else { //walk forward or backward to get at the ideal attack distance - if (dist > attack_dist + attack_range) { + if (bs->cur_ps.torsoAnim==BOTH_LADDER || bs->cur_ps.torsoAnim==BOTH_LADDER_STAND) { + VectorMA(sideward, 1, backward, sideward);// thedoctor: does not work? + VectorNormalize(sideward); + } else { + if (dist > attack_dist + attack_range || !goback) { VectorAdd(sideward, forward, sideward); } else if (dist < attack_dist - attack_range) { VectorAdd(sideward, backward, sideward); } } +// } //perform the movement - if (trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) + if (BotMoveInDir(&moveresult, bs, sideward, 400, movetype, tfl)) { return moveresult; + } else { //movement failed, flip the strafe direction bs->flags ^= BFL_STRAFERIGHT; - bs->attackstrafe_time = 0; + bs->attackstrafe_time = - bs->thinktime*10; + CrossProduct(hordir, up, sideward); + //reverse the vector depending on the strafe direction + if (bs->flags & BFL_STRAFERIGHT) VectorNegate(sideward, sideward); + if (dist > attack_dist + attack_range || !goback) { + VectorAdd(sideward, forward, sideward); } + else if (dist < attack_dist - attack_range) { + VectorAdd(sideward, backward, sideward); + } + if (BotMoveInDir(&moveresult, bs, sideward, 400, movetype, tfl)) { + return moveresult; + } else if (BotMoveInDir(&moveresult, bs, forward, 400, movetype, tfl)) { + return moveresult; + } + } +// if (trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) { +// return moveresult; +// } else { +// //movement failed, flip the strafe direction +// bs->flags ^= BFL_STRAFERIGHT; +// bs->attackstrafe_time = - bs->thinktime*10; +// CrossProduct(hordir, up, sideward); +// //reverse the vector depending on the strafe direction +// if (bs->flags & BFL_STRAFERIGHT) VectorNegate(sideward, sideward); +// if (dist > attack_dist + attack_range || !goback) { +// VectorAdd(sideward, forward, sideward); +// } +// else if (dist < attack_dist - attack_range) { +// VectorAdd(sideward, backward, sideward); +// } +// if (trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) { +// return moveresult; +// } else if (trap_BotMoveInDirection(bs->ms, forward, 400, movetype)) { +// return moveresult; +// } +// } + //bot couldn't do any usefull movement -// bs->attackchase_time = AAS_Time() + 6; + bs->attackchase_time = FloatTime() + 6; return moveresult; } @@ -3294,6 +3429,10 @@ int BotFindEnemy(bot_state_t *bs, int curenemy) { float squaredist, cursquaredist; aas_entityinfo_t entinfo, curenemyinfo; vec3_t dir, angles; + vec3_t forward; + int entitynum,dist,lastdist; + entitynum=0; + lastdist=1000000; alertness = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ALERTNESS, 0, 1); easyfragger = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_EASY_FRAGGER, 0, 1); @@ -3372,14 +3511,8 @@ int BotFindEnemy(bot_state_t *bs, int curenemy) { //calculate the distance towards the enemy VectorSubtract(entinfo.origin, bs->origin, dir); squaredist = VectorLengthSquared(dir); - //if this entity is not carrying a flag -#ifndef SMOKINGUNS - if (!EntityCarriesFlag(&entinfo)) - { //if this enemy is further away than the current one if (curenemy >= 0 && squaredist > cursquaredist) continue; - } //end if -#endif //if the bot has no if (squaredist > Square(900.0 + alertness * 4000.0)) continue; //if on the same team @@ -3399,25 +3532,17 @@ int BotFindEnemy(bot_state_t *bs, int curenemy) { VectorSubtract(bs->origin, entinfo.origin, dir); vectoangles(dir, angles); //if the bot isn't in the fov of the enemy -#ifndef SMOKINGUNS - if (!InFieldOfVision(entinfo.angles, 90, angles)) { -#else if (!InFieldOfVision(entinfo.angles, 90, angles, bs->client)) { -#endif //update some stuff for this enemy BotUpdateBattleInventory(bs, i); //if the bot doesn't really want to fight -#ifndef SMOKINGUNS if (BotWantsToRetreat(bs)) continue; -#endif } } //found an enemy bs->enemy = entinfo.number; -#ifdef SMOKINGUNS //bs->flags &= ~BFL_SEEK; AIEnter_Battle_Chase(bs, "found enemy: enter chase"); -#endif if (curenemy >= 0) bs->enemysight_time = FloatTime() - 2; else bs->enemysight_time = FloatTime(); bs->enemysuicide = qfalse; @@ -3425,6 +3550,31 @@ int BotFindEnemy(bot_state_t *bs, int curenemy) { bs->enemyvisible_time = FloatTime(); return qtrue; } + +// DEBUG: treat gatling as enemy => SEGFAULT +// for(i=0; iorigin, forward); +// //the distance towards the gatling +// dist = VectorNormalize(forward); +// if (dist<1000 && (entitynum==0 || lastdist>dist)) { +// entitynum = i; +// lastdist = dist; +// BotAI_Print(PRT_MESSAGE, "picked a gatling %d", i); +// } +// } +// } +// if (entitynum>0) { +// bs->enemy = entinfo.number; +// //bs->flags &= ~BFL_SEEK; +// AIEnter_Battle_Chase(bs, "found enemy: enter chase"); +// if (curenemy >= 0) bs->enemysight_time = FloatTime() - 2; +// else bs->enemysight_time = FloatTime(); +// bs->enemysuicide = qfalse; +// bs->enemydeath_time = 0; +// bs->enemyvisible_time = FloatTime(); +// return qtrue; +// } return qfalse; } @@ -3649,14 +3799,15 @@ void BotAimAtEnemy(bot_state_t *bs) { int i; float enemyvisible; #endif - float dist, f, aim_skill, aim_accuracy, speed, reactiontime; - vec3_t dir, bestorigin, end, start, groundtarget, cmdmove, enemyvelocity; + float dist, f, aim_skill, aim_accuracy, speed, reactiontime, angle; + vec3_t dir, bestorigin, end, start, groundtarget, cmdmove, enemyvelocity, forward; vec3_t mins = {-4,-4,-4}, maxs = {4, 4, 4}; weaponinfo_t wi; aas_entityinfo_t entinfo; bot_goal_t goal; bsp_trace_t trace; vec3_t target; + int height; //if the bot has no enemy if (bs->enemy < 0) { @@ -3961,6 +4112,50 @@ void BotAimAtEnemy(bot_state_t *bs) { trap_EA_View(bs->client, bs->viewangles); } } + VectorSubtract(entinfo.origin, bs->origin, forward); + //the distance towards the enemy + // arctan arcsin + // + // + // o + // 180 /|\ 0=360 + // 135 /_\ 45 + // 90 + dist = VectorNormalize(forward); + if (bs->weaponnum == WP_KNIFE) {// throw knife + angle = 0.5*atan2( (dist*DEFAULT_GRAVITY),(1200*1200) ); + angle = angle*50; + ////height = 0; + ////angle = 3.14159388/2-asin(((DEFAULT_GRAVITY*dist)/1200)*sqrt(2/((1200*1200-2*height*DEFAULT_GRAVITY)+sqrt((1200*1200-2*height*DEFAULT_GRAVITY)*(1200*1200-2*height*DEFAULT_GRAVITY) - (4*DEFAULT_GRAVITY*DEFAULT_GRAVITY)*(dist*dist+height*height))))); + //BotAI_Print(PRT_MESSAGE, "dist=%f, angle=%f, viewangle=%f\n",dist,angle,bs->viewangles[0]); + bs->viewangles[0]=360-angle; + bs->ideal_viewangles[PITCH]=bs->viewangles[0]; + if (g_entities[bs->client].client->ps.ammo[WP_KNIFE]>1) { + g_entities[bs->client].client->ps.stats[STAT_WP_MODE]=1; + } + trap_EA_View(bs->client, bs->viewangles); + } + if (bs->weaponnum == WP_DYNAMITE) {// throw dynamite + angle = 0.5*atan2( (dist*DEFAULT_GRAVITY),140000 ); + //BotAI_Print(PRT_MESSAGE, "dist=%f, angle=%f, viewangle=%f\n",dist,angle,bs->viewangles[0]); + angle = angle*25; + bs->viewangles[0]=360-angle; + bs->ideal_viewangles[PITCH]=bs->viewangles[0]; + g_entities[bs->client].client->ps.stats[STAT_WP_MODE]=-5; + trap_EA_View(bs->client, bs->viewangles); + } + if (bs->weaponnum == WP_MOLOTOV) {// throw molotov + angle = 0.5*atan2( (dist*DEFAULT_GRAVITY),70000 ); + //BotAI_Print(PRT_MESSAGE, "dist=%f, angle=%f, viewangle=%f\n",dist,angle,bs->viewangles[0]); + bs->viewangles[0]=360-angle; + bs->ideal_viewangles[PITCH]=bs->viewangles[0]; + g_entities[bs->client].client->ps.stats[STAT_WP_MODE]=-1; + trap_EA_View(bs->client, bs->viewangles); + } +// if (bot_rocketjump.integer>0) { +// bs->viewangles[0]=ANGLE2SHORT(bot_rocketjump.integer); +// bs->ideal_viewangles[PITCH]=bs->viewangles[0]; +// } } /* @@ -3997,7 +4192,6 @@ void BotCheckBuy(bot_state_t *bs){ money = bs->cur_ps.stats[STAT_MONEY]; bs->flags &= ~BFL_ESCAPE; bs->flags &= ~BFL_AVOID; - bs->buytime = level.time + 3000 + rand()%4000; // first try to buy a weapon for(i=1, mark = -1, mark2 = -1; ; i++){ @@ -4015,9 +4209,9 @@ void BotCheckBuy(bot_state_t *bs){ item->giType != IT_WEAPON) continue; - // Todo: add gatling support - if(item->giTag == WP_GATLING) - continue; +// // Todo: add gatling support +// if(item->giTag == WP_GATLING) +// continue; // don't buy if he already has that weapon if( ((bs->cur_ps.stats[STAT_WEAPONS] & (1 << item->giTag) ) @@ -4028,9 +4222,9 @@ void BotCheckBuy(bot_state_t *bs){ continue; // if the bot already has a better weapon - j = WP_REM58; - while(BG_FindPlayerWeapon(j, WP_DYNAMITE, &bs->cur_ps)){ - j = BG_FindPlayerWeapon(j, WP_DYNAMITE, &bs->cur_ps); + j = WP_KNIFE; + while(BG_FindPlayerWeapon(j, WP_MOLOTOV+1, &bs->cur_ps)){ + j = BG_FindPlayerWeapon(j, WP_MOLOTOV+1, &bs->cur_ps); temp = BG_FindItemForWeapon(j); @@ -4090,8 +4284,12 @@ void BotCheckBuy(bot_state_t *bs){ if(item->prize == 0 || item->weapon_sort != WS_MISC) continue; - if(item->giTag == WP_MOLOTOV || item->giTag == WP_DYNAMITE || item->giTag == WP_KNIFE) - continue; + // don't buy WP_MOLOTOV, WP_DYNAMITE, WP_KNIFE + //if(random()>0.90 && item->giTag == WP_MOLOTOV) + // continue; + + if (random()>0.50 && item->giTag == WP_DYNAMITE) continue;// + //if(item->giTag == WP_KNIFE) continue; if(item->giType == IT_ARMOR && bs->cur_ps.stats[STAT_ARMOR]) continue; @@ -4099,6 +4297,8 @@ void BotCheckBuy(bot_state_t *bs){ if(item->giType == IT_POWERUP && bs->cur_ps.powerups[item->giTag]) continue; + if (item->giTag==PW_SCOPE) continue; + if(mark == -1){ mark = i; continue; @@ -4129,7 +4329,7 @@ void BotCheckBuy(bot_state_t *bs){ money -= item->prize; bought = qtrue ; } - + bs->buytime = level.time + 3000 + rand()%4000;// update the buytime only in case we buy something bs->flags ^= BFL_BOUGHT; BotChooseWeapon(bs); @@ -4686,6 +4886,8 @@ int BotFuncBreakableGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *act (!bg_weaponlist[weapon].clip || !bs->cur_ps.ammo[bg_weaponlist[weapon].clip]))){ return qfalse; } + } else { + activategoal->weapon = WP_KNIFE; } return qtrue; } @@ -4723,6 +4925,7 @@ int BotFuncDoorActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *act activategoal->goal.areanum = bs->areanum; VectorSet(activategoal->goal.mins, -8, -8, -8); VectorSet(activategoal->goal.maxs, 8, 8, 8); + activategoal->weapon = WP_KNIFE; return qtrue; } @@ -5277,129 +5480,127 @@ extern vec3_t playerMaxs; #endif void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate) { int movetype, bspent; - vec3_t hordir, start, end, mins, maxs, sideward, angles, up = {0, 0, 1}; + vec3_t hordir, start, end, mins, maxs, sideward, direction, left, right, angles, forward, backward, up = {0, 0, 1};// patch bot: evasive maneuvers aas_entityinfo_t entinfo; bot_activategoal_t activategoal; -#ifdef SMOKINGUNS - bsp_trace_t bsptrace; -#ifdef OBSTACLEDEBUG - char *netname ; -#endif -#endif + char classname[128]; + char model[MAX_INFO_STRING], tmpmodel[128]; + vec3_t absmins, absmaxs; + int i,j; + gentity_t *ent; - // if the bot is not blocked by anything - if (!moveresult->blocked) { - bs->notblocked_time = FloatTime(); + //BotAI_Print(PRT_MESSAGE, "time: %f\n", (FloatTime()-bs->last_blocked_time)); + // if the bot is not blocked by anything or uses a gatling + if ((bs->weaponnum == WP_GATLING && g_entities[bs->client].client->ps.stats[STAT_GATLING_MODE]) || !moveresult->blocked && (bs->origin[0]!=bs->myorigin[0] && bs->origin[1]!=bs->myorigin[1])) { + if(FloatTime()-bs->last_blocked_time>1) { + bs->non_blocked_time = FloatTime(); + VectorCopy(bs->origin,bs->myorigin); return; } - // if stuck in a solid area - if ( moveresult->type == RESULTTYPE_INSOLIDAREA ) { - // move in a random direction in the hope to get out - BotRandomMove(bs, moveresult); - // - return; - } - // get info for the entity that is blocking the bot - BotEntityInfo(moveresult->blockentity, &entinfo); -#ifdef OBSTACLEDEBUG - ClientName(bs->client, netname, sizeof(netname)); - BotAI_Print(PRT_MESSAGE, "%s: I'm blocked by model %d\n", netname, entinfo.modelindex); -#endif // OBSTACLEDEBUG - // if blocked by a bsp model and the bot wants to activate it - if (activate && entinfo.modelindex > 0 && entinfo.modelindex <= max_bspmodelindex) { - // find the bsp entity which should be activated in order to get the blocking entity out of the way - bspent = BotGetActivateGoal(bs, entinfo.number, &activategoal); - if (bspent) { - // - if (bs->activatestack && !bs->activatestack->inuse) - bs->activatestack = NULL; - // if not already trying to activate this entity - if (!BotIsGoingToActivateEntity(bs, activategoal.goal.entitynum)) { - // - BotGoForActivateGoal(bs, &activategoal); + } else { + bs->last_blocked_time = FloatTime(); } - // if ontop of an obstacle or - // if the bot is not in a reachability area it'll still - // need some dynamic obstacle avoidance, otherwise return - if (!(moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) && - trap_AAS_AreaReachability(bs->areanum)) - return; + + // climbing ladders (and possibly walls) +// if bot is climbing +// if there's a reachable ledge +// //jump (go up) and move forward +// otherwise +// // random and crouch + + // if we are blocked, but didn't get an entity, look for entities nearby + // this happens if he trace hits a wall before an entity, even though, the blocking reason is the entity + if (moveresult->blockentity==0) { + for (i=0; iblockentity==0; i++) { + ent = &g_entities[i]; + if (!ent) {break;} + if ((ent->r.contents & CONTENTS_MOVER) && !!Q_stricmp(ent->classname,"func_door_rotate")) { + VectorSubtract(ent->r.mins, g_entities[bs->entitynum].r.maxs, absmins); + VectorSubtract(ent->r.maxs, g_entities[bs->entitynum].r.mins, absmaxs); + VectorAdd(absmins, ent->r.currentOrigin, absmins); + VectorAdd(absmaxs, ent->r.currentOrigin, absmaxs); + + for (j=0; j<3; j++) { + if (bs->origin[j] < absmins[j] || bs->origin[j] > absmaxs[j]) { + break; } - else { - // enable any routing areas that were disabled - BotEnableActivateGoalAreas(&activategoal, qtrue); + if (j==3) { + BotAI_Print(PRT_MESSAGE, "checking for doors nearby - found one\n"); + moveresult->blockentity = i; + moveresult->blocked = qtrue; } + } //end for } - // just some basic dynamic obstacle avoidance code - hordir[0] = moveresult->movedir[0]; - hordir[1] = moveresult->movedir[1]; - hordir[2] = 0; - // if no direction just take a random direction - if (VectorNormalize(hordir) < 0.1) { - VectorSet(angles, 0, 360 * random(), 0); - AngleVectors(angles, hordir, NULL, NULL); } - // -#ifndef SMOKINGUNS - //if (moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) movetype = MOVE_JUMP; - //else - movetype = MOVE_WALK; -#else - if (moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) movetype = MOVE_JUMP; - else movetype = MOVE_WALK; -#endif - // if there's an obstacle at the bot's feet and head then - // the bot might be able to crouch through - VectorCopy(bs->origin, start); - start[2] += 18; - VectorMA(start, 5, hordir, end); -#ifndef SMOKINGUNS - VectorSet(mins, -16, -16, -24); - VectorSet(maxs, 16, 16, 4); - // - //bsptrace = AAS_Trace(start, mins, maxs, end, bs->entitynum, MASK_PLAYERSOLID); - //if (bsptrace.fraction >= 1) movetype = MOVE_CROUCH; -#else - VectorCopy(playerMins, mins); - VectorCopy(playerMaxs, maxs); - // - BotAI_Trace(&bsptrace, start, mins, maxs, end, bs->entitynum, MASK_PLAYERSOLID); - if (bsptrace.fraction >= 1) { - movetype = MOVE_CROUCH; - bs->crouchtime = level.time; } - if(bs->crouchtime + 1000 > level.time) - movetype = MOVE_CROUCH; -#endif - // get the sideward vector - CrossProduct(hordir, up, sideward); - // - if (bs->flags & BFL_AVOIDRIGHT) VectorNegate(sideward, sideward); - // try to crouch straight forward? -#ifndef SMOKINGUNS - if (movetype != MOVE_CROUCH || !trap_BotMoveInDirection(bs->ms, hordir, 400, movetype)) { -#else - if (!trap_BotMoveInDirection(bs->ms, hordir, 400, movetype)) { -#endif - // perform the movement - if (!trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) { - // flip the avoid direction flag - bs->flags ^= BFL_AVOIDRIGHT; - // flip the direction - // VectorNegate(sideward, sideward); - VectorMA(sideward, -1, hordir, sideward); - // move in the other direction - trap_BotMoveInDirection(bs->ms, sideward, 400, movetype); - } + // if stuck in a solid area +// if ( moveresult->type == RESULTTYPE_INSOLIDAREA ) { +// // move in a random direction in the hope to get out +// BotRandomMove(bs, moveresult); +// // +// return; +// } + + // get info for the entity that is blocking the bot + BotEntityInfo(moveresult->blockentity, &entinfo); + +// BotAI_Print(PRT_MESSAGE, g_entities[moveresult->blockentity].classname); + if (!Q_stricmp(g_entities[moveresult->blockentity].classname, "func_breakable")) { + BotAI_Print(PRT_MESSAGE, "BREAKABLE\n"); + bs->weaponnum = WP_KNIFE; + + // generally, look into the direction of the breakable + VectorCopy(moveresult->movedir,direction); + direction[2]=0; + VectorNormalize(direction); + vectoangles(direction, bs->viewangles); + trap_EA_View(bs->client, bs->viewangles); + trap_EA_Attack(bs->client); + + } else if (bs->origin[0]!=bs->myorigin[0] && bs->origin[1]!=bs->myorigin[1]) {// DEBUG DOCTOR + // generally, look into the direction of the breakable + VectorCopy(moveresult->movedir,direction); + direction[2]=0; + VectorNormalize(direction); + vectoangles(direction, bs->ideal_viewangles); + //trap_EA_View(bs->client, bs->ideal_viewangles); + BotRandomMove(bs, moveresult); } - // - if (bs->notblocked_time < FloatTime() - 0.4) { + +// if (activate && entinfo.modelindex > 0 && entinfo.modelindex <= max_bspmodelindex && (FloatTime() - bs->last_blocked_time > 2) ) { +// // find the bsp entity which should be activated in order to get the blocking entity out of the way +// bspent = BotGetActivateGoal(bs, entinfo.number, &activategoal); +// if (bspent) { +// if (bs->activatestack && !bs->activatestack->inuse) +// bs->activatestack = NULL; +// // if not already trying to activate this entity +// if (!BotIsGoingToActivateEntity(bs, activategoal.goal.entitynum)) { +// BotAI_Print(PRT_MESSAGE, " and will\n"); +// BotGoForActivateGoal(bs, &activategoal); +// } else { +// BotAI_Print(PRT_MESSAGE, " and already going to\n"); +// } +// // if ontop of an obstacle or +// // if the bot is not in a reachability area it'll still +// // need some dynamic obstacle avoidance, otherwise return +// if (!(moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) && +// trap_AAS_AreaReachability(bs->areanum)) +// return; +// } else { +// // enable any routing areas that were disabled +// BotEnableActivateGoalAreas(&activategoal, qtrue); +// BotAI_Print(PRT_MESSAGE, " but can't\n"); +// } +// } + + if (bs->non_blocked_time < FloatTime() - 4) { // just reset goals and hope the bot will go into another direction? // is this still needed?? if (bs->ainode == AINode_Seek_NBG) bs->nbg_time = 0; else if (bs->ainode == AINode_Seek_LTG) bs->ltg_time = 0; } + VectorCopy(bs->origin,bs->myorigin); } /* @@ -5597,12 +5798,12 @@ void BotCheckForGrenades(bot_state_t *bs, entityState_t *state) { // if this is not a grenade if (state->eType == ET_MISSILE && state->weapon == WP_DYNAMITE && state->apos.trDelta[0]){ - } else if ((state->eType == ET_MISSILE || !Q_stricmp(g_entities[state->number].classname, "sprotz")) - && state->apos.trDelta[0] && state->weapon == WP_MOLOTOV){ + trap_BotAddAvoidSpot(bs->ms, state->pos.trBase, 600, AVOID_ALWAYS); + } else if ((!Q_stricmp(g_entities[state->number].classname, "whiskey_pool")) && state->apos.trDelta[0] == 1) { + trap_BotAddAvoidSpot(bs->ms, state->pos.trBase, 100, AVOID_ALWAYS); } else return; - // try to avoid the grenade - trap_BotAddAvoidSpot(bs->ms, state->pos.trBase, 1000, AVOID_ALWAYS); + // add to list /*if (bs->numexplosives >= MAX_EXPLOSIVES) diff --git a/code/game/ai_main.c b/code/game/ai_main.c index 0df8c992..d474674b 100644 --- a/code/game/ai_main.c +++ b/code/game/ai_main.c @@ -1468,7 +1468,7 @@ int BotAIStartFrame(int time) { BotUpdateInfoConfigStrings(); } - if (bot_pause.integer) { + if (bot_pause.integer && trap_AAS_Initialized()) {//|| (bot_pause.integer == 2 && g_humancount==0)// patch bot: make bots auto-pausable // execute bot user commands every frame for( i = 0; i < MAX_CLIENTS; i++ ) { if( !botstates[i] || !botstates[i]->inuse ) { @@ -1716,7 +1716,7 @@ int BotAISetup( int restart ) { trap_Cvar_Register(&bot_thinktime, "bot_thinktime", "100", CVAR_CHEAT); trap_Cvar_Register(&bot_memorydump, "bot_memorydump", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_saveroutingcache, "bot_saveroutingcache", "0", CVAR_CHEAT); - trap_Cvar_Register(&bot_pause, "bot_pause", "0", CVAR_CHEAT); + trap_Cvar_Register(&bot_pause, "bot_pause", "0", 0);// patch bot: make bots pausable trap_Cvar_Register(&bot_report, "bot_report", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_testsolid, "bot_testsolid", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_testclusters, "bot_testclusters", "0", CVAR_CHEAT); diff --git a/code/game/ai_main.h b/code/game/ai_main.h index 9701dd30..d2b355ba 100644 --- a/code/game/ai_main.h +++ b/code/game/ai_main.h @@ -59,6 +59,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define BFL_SEEK 0x0020000 // bot seeks around #define BFL_NOGOAL 0x0040000 // get no goal #define BFL_ACT_BUTTON 0x0080000 +#define BFL_CLIMB_UP 0x0160000 #endif @@ -234,7 +235,8 @@ typedef struct bot_state_s float weaponchange_time; //time the bot started changing weapons float firethrottlewait_time; //amount of time to wait float firethrottleshoot_time; //amount of time to shoot - float notblocked_time; //last time the bot was not blocked + float last_blocked_time; //last time the bot was blocked + float non_blocked_time; //last time the bot was not blocked float blockedbyavoidspot_time; //time blocked by an avoid spot float predictobstacles_time; //last time the bot predicted obstacles int predictobstacles_goalareanum; //last goal areanum the bot predicted obstacles for @@ -331,6 +333,7 @@ typedef struct bot_state_s #ifdef SMOKINGUNS bot_orient_t orientation; bot_goal_t save_goal; + vec3_t myorigin; //origin of the bot #endif } bot_state_t; diff --git a/code/game/bg_misc.c b/code/game/bg_misc.c index 06b2c22f..fef26982 100644 --- a/code/game/bg_misc.c +++ b/code/game/bg_misc.c @@ -24,12 +24,20 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // // bg_misc.c -- both games misc functions, all completely stateless +#ifdef QAGAME +#include "g_local.h" +#include "../qcommon/q_shared.h" +#else #include "../qcommon/q_shared.h" #include "bg_public.h" +#endif #ifdef SMOKINGUNS vec3_t playerMins = {-14, -14, MINS_Z}; vec3_t playerMaxs = {14, 14, MAXS_Z}; +// vec3_t playerMins = {-15, -15, -24}; +// vec3_t playerMaxs = { 15, 15, 32}; + vec3_t gatling_mins = {-3, -3, 0.0}; vec3_t gatling_maxs = {3, 3, 35}; @@ -1584,7 +1592,7 @@ gitem_t bg_itemlist[] = 0, 0, 0, - 0,// $ + 0,// $ price 0, /* precache */ "", /* sounds */ "" @@ -2005,7 +2013,7 @@ gitem_t bg_itemlist[] = /* icon */ "hud/weapons/dynamite", 3, /* pickup */ "Dynamite", - 5, + 2,// amount IT_AMMO, WP_DYNAMITE, 16, @@ -2298,6 +2306,23 @@ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const play #else int belt = 1, i; + //--------- begin (nobots/nohumans item pickup fix) +#ifdef QAGAME + gentity_t *item_ent; + gentity_t *player_ent; + item_ent = &g_entities[ ent->number ]; + player_ent = &g_entities[ ps->clientNum ]; + + if ( ( item_ent->flags & FL_NO_BOTS ) && ( player_ent->r.svFlags & SVF_BOT ) ) { + return qfalse; + } + // just to be symmetric, we have a nohumans option... + if ( ( item_ent->flags & FL_NO_HUMANS ) && !( player_ent->r.svFlags & SVF_BOT ) ) { + return qfalse; + } +#endif + //---------- end (nobots/nohumans item pickup fix) + // hika additional comments: // Molotovs, dynamites and knives are no longer affected by belt double ammo effect. // See ./code/g_cmds.c : Cmd_BuyItem_f() and @@ -3392,18 +3417,38 @@ int BG_MapPrefix(char *map, int gametype){ gt[2] = map[2]; gt[3] = '\0'; - if(!Q_stricmp(gt,"br_")){ - return GT_BR; - } else if(!Q_stricmp(gt, "du_")){ + if(!Q_stricmp(gt,"dm_")){ + if (gametype==GT_BR) { + return GT_TEAM; + } + } else if (!Q_stricmp(gt,"du_")){ return GT_DUEL; } } } - if(gametype == GT_SINGLE_PLAYER || - gametype == GT_DUEL || - gametype >= GT_BR) - return GT_FFA; + +// if(map && map[0] && map[1]){ +// if(map[2] == '_'){ +// char gt[4]; +// +// gt[0] = map[0]; +// gt[1] = map[1]; +// gt[2] = map[2]; +// gt[3] = '\0'; +// +// if(!Q_stricmp(gt,"br_")){ +// return GT_BR; +// } else if(!Q_stricmp(gt, "du_")){ +// return GT_DUEL; +// } +// } +// } +// +// if(gametype == GT_SINGLE_PLAYER || +// gametype == GT_DUEL || +// gametype >= GT_BR) +// return GT_FFA; return gametype; } diff --git a/code/game/bg_pmove.c b/code/game/bg_pmove.c index 7070c416..b8af99d6 100644 --- a/code/game/bg_pmove.c +++ b/code/game/bg_pmove.c @@ -70,6 +70,342 @@ float pm_ladderfriction = 3000; // Friction is high enough so you don't slip do int c_pmove = 0; +////From AltUrt: Walljump constants +//#define WALLJUMP_BOOST 140 //used in the checkWalljump function, no change to the actual walljump made here. +//#define MAX_WALLJUMPS 3 //no explanation needed +//#define WALLJUMP_SPEED 580.0f //used in VectorMA for walljumps, affects player velocity +//#define WALLJUMP_MAX_VEL 600 //max velocity during walljumps. I cut this in half, it was 1200 which seems way too high --xamis +//#define WALLJUMP_MAX_UP_VEL 250 //cap the upward velocity on a walljump +//#define WALLJUMP_UP_VEL_BOOST 1.1f //give the player some upward boost off the wall +// +// +// patch ledgeclimb: from alturt. Does not work yet +//#define LEDGEGRABMAXHEIGHT 45 +//#define LEDGEGRABHEIGHT 42 +//#define LEDGEVERTOFFSET LEDGEGRABHEIGHT +//#define LEDGEGRABMINHEIGHT 40 +// +////max distance you can be from the ledge for ledge grabbing to work +//#define LEDGEGRABDISTANCE 20 +// +////min distance you can be from the ledge for ledge grab to work +//#define LEDGEGRABMINDISTANCE 4 +// +////distance at which the animation grabs the ledge +//#define LEDGEHOROFFSET 22 +// +// +//float vectoyaw( const vec3_t vec ) { +// float yaw; +// +// if (vec[YAW] == 0 && vec[PITCH] == 0) { +// yaw = 0; +// } else { +// if (vec[PITCH]) { +// yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI ); +// } else if (vec[YAW] > 0) { +// yaw = 90; +// } else { +// yaw = 270; +// } +// if (yaw < 0) { +// yaw += 360; +// } +// } +// +// return yaw; +//} +// +//void PM_SetPMViewAngle(playerState_t *ps, vec3_t angle, usercmd_t *ucmd) +//{ +// int i; +// +// for (i=0 ; i<3 ; i++) +// { // set the delta angle +// int cmdAngle; +// +// cmdAngle = ANGLE2SHORT(angle[i]); +// ps->delta_angles[i] = cmdAngle - ucmd->angles[i]; +// } +// VectorCopy (angle, ps->viewangles); +//} +// +////lets go of a ledge +//void BG_LetGoofLedge(playerState_t *ps) +//{ +// ps->pm_flags &= ~PMF_EDGE; +// ps->torsoTimer = 0; +// ps->legsTimer = 0; +//} +// +//qboolean BG_InLedgeMove( int anim ) +//{ +// +// switch ( anim ) +// { +// case BOTH_LEDGECLIMB: +// return qtrue; +// break; +// default: +// return qfalse; +// break; +// } +// +//} +// +//void PM_SetVelocityforLedgeMove( playerState_t *ps, int anim ) +//{ +// vec3_t fwdAngles, moveDir; +// +// if( pm->ps->torsoTimer > 750 ) +// { +// ps->velocity[0] = 0; +// ps->velocity[1] = 0; +// ps->velocity[2] = 40; +// } +// else if( pm->ps->torsoTimer > 550 ) +// { +// ps->velocity[0] = 0; +// ps->velocity[1] = 0; +// ps->velocity[2] = 100; +// } +// else if( pm->ps->torsoTimer > 500 ) +// { +// ps->velocity[0] = 0; +// ps->velocity[1] = 0; +// ps->velocity[2] = 60; +// } +// else if( pm->ps->torsoTimer > 400 ) +// { +// ps->velocity[0] = 0; +// ps->velocity[1] = 0; +// ps->velocity[2] = 50; +// } +// else if( pm->ps->torsoTimer > 120 ) +// { +// ps->velocity[0] = 0; +// ps->velocity[1] = 0; +// ps->velocity[2] = 120; +// } +// else if( pm->ps->torsoTimer > 90 ) +// { +// ps->velocity[0] = 0; +// ps->velocity[1] = 0; +// ps->velocity[2] = 140; +// } +// else if(pm->ps->torsoTimer > 0) +// { +// ps->velocity[0] = 170; +// ps->velocity[1] = 170; +// ps->velocity[2] = 194; +// VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0); +// AngleVectors( fwdAngles, moveDir, NULL, NULL ); +// VectorScale(moveDir, 190, moveDir); +// VectorCopy(moveDir, ps->velocity); +// } +// else +// { +// BG_LetGoofLedge(ps); +// VectorClear(ps->velocity); +// } +// +//} +// +//qboolean LedgeGrabableEntity(int entityNum) +//{//indicates if the given entity is an entity that can be ledgegrabbed. +// //bgEntity_t *ent = PM_BGEntForNum(entityNum); +// // switch(ent->s.eType) +// // { +// // case ET_PLAYER: +// // case ET_ITEM: +// // case ET_MISSILE: +// // return qfalse; +// //default: +// return qtrue; +// // }; +//} +// +//void PM_GrabWallForClimb( playerState_t *ps ) +//{ +// PM_StartTorsoAnim ( BOTH_LEDGECLIMB ); +// PM_StartLegsAnim ( BOTH_LEDGECLIMB ); +// ps->torsoTimer = 900; +// ps->legsTimer = 900; +// ps->weaponTime = 900; +// PM_AddEvent(EV_LEDGEGRAB); +// ps->pm_flags |= PMF_EDGE; +//} +// +////Switch to this animation and keep repeating this animation while updating its timers +// +//void PM_AdjustAngleForLedgeClimb( playerState_t *ps, usercmd_t *ucmd ) +//{ +// if( ps->pm_flags & PMF_EDGE ){ +// if(ucmd->forwardmove <= 0) +// { +// BG_LetGoofLedge(ps); +// return; +// } +// else +// { +// PM_SetVelocityforLedgeMove(ps, ps->legsAnim); +// return; +// } +// } +//} +// +//qboolean LedgeTrace( trace_t *trace, vec3_t dir, float *lerpup, float *lerpfwd, float *lerpyaw) +//{//scan for for a ledge in the given direction +// vec3_t traceTo, traceFrom, wallangles; +// VectorMA( pm->ps->origin, LEDGEGRABDISTANCE, dir, traceTo ); +// VectorCopy(pm->ps->origin, traceFrom); +// +// if ( CheckLadder() != 0 ) +// return qfalse; +// +// traceFrom[2] += LEDGEGRABMINHEIGHT; +// traceTo[2] += LEDGEGRABMINHEIGHT; +// +// pm->trace( trace, traceFrom, NULL, NULL, traceTo, pm->ps->clientNum, pm->tracemask ); +// +// if(trace->fraction < 1 && LedgeGrabableEntity(trace->entityNum)) +// {//hit a wall, pop into the wall and fire down to find top of wall +// VectorMA(trace->endpos, 0.5, dir, traceTo); +// +// VectorCopy(traceTo, traceFrom); +// +// traceFrom[2] += (LEDGEGRABMAXHEIGHT - LEDGEGRABMINHEIGHT); +// +// pm->trace( trace, traceFrom, NULL, NULL, traceTo, pm->ps->clientNum, pm->tracemask ); +// +// if(trace->fraction == 1.0 || trace->startsolid || !LedgeGrabableEntity(trace->entityNum)) +// { +// return qfalse; +// } +// } +// +// //check to make sure we found a good top surface and go from there +// vectoangles(trace->plane.normal, wallangles); +// if(wallangles[PITCH] > -20) +// {//no ledge or the ledge is too steep +// +// return qfalse; +// } +// else +// { +// VectorCopy(trace->endpos, traceTo); +// *lerpup = trace->endpos[2] - pm->ps->origin[2] - LEDGEVERTOFFSET; +// +// VectorCopy(pm->ps->origin, traceFrom); +// traceTo[2]-= 1; +// +// traceFrom[2] = traceTo[2]; +// +// pm->trace( trace, traceFrom, NULL, NULL, traceTo, pm->ps->clientNum, MASK_SOLID ); +// vectoangles(trace->plane.normal, wallangles); +// if(trace->fraction == 1.0 +// || wallangles[PITCH] > 20 || wallangles[PITCH] < -20 +// || !LedgeGrabableEntity(trace->entityNum)) +// {//no ledge or too steep of a ledge +// return qfalse; +// } +// +// +// *lerpfwd = Distance(trace->endpos, traceFrom) - LEDGEHOROFFSET; +// *lerpyaw = vectoyaw( trace->plane.normal )+180; +// return qtrue; +// } +//} +// +////check for ledge grab +// +//void PM_CheckGrab(void) +//{ +// vec3_t checkDir, traceTo, fwdAngles; +// trace_t trace; +// float lerpup = 0; +// float lerpfwd = 0; +// float lerpyaw = 0; +// qboolean skipcmdtrace = qfalse; +// +// // if(pm->ps->groundEntityNum != ENTITYNUM_NONE )//&& pm->ps->inAirAnim) +// // {//not in the air don't attempt a ledge grab +// // return; +// // } +// +// +// if(BG_InLedgeMove(pm->ps->legsAnim) || pm->ps->pm_type == PM_SPECTATOR) +// {//already on ledge, a spectator, or in a special jump +// return; +// } +// +// if(pm->ps->velocity == 0) +// return;//Not moving forward +// +// //try looking in front of us first +// VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f); +// AngleVectors( fwdAngles, checkDir, NULL, NULL ); +// +// if( !VectorCompare(pm->ps->velocity, vec3_origin) ) +// {//player is moving +// if(LedgeTrace(&trace, checkDir, &lerpup, &lerpfwd, &lerpyaw)) +// { +// skipcmdtrace = qtrue; +// } +// } +// +// if(!skipcmdtrace) +// { +// //TRYING to go. +// if(!pm->cmd.forwardmove) +// {//no dice abort +// return; +// } +// else +// { +// if ( pm->cmd.forwardmove > 0 ) +// {//already tried this direction. +// return; +// } +// else if ( pm->cmd.forwardmove < 0 ) +// { +// AngleVectors( fwdAngles, checkDir, NULL, NULL ); +// VectorScale( checkDir, -1, checkDir ); +// VectorNormalize(checkDir); +// } +// +// if(!LedgeTrace(&trace, checkDir, &lerpup, &lerpfwd, &lerpyaw)) +// {//no dice +// return; +// } +// } +// } +// +// VectorCopy(pm->ps->origin, traceTo); +// VectorMA(pm->ps->origin, lerpfwd, checkDir, traceTo); +// traceTo[2] += lerpup; +// +// //check to see if we can actually latch to that position. +// pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, traceTo, pm->ps->clientNum, MASK_PLAYERSOLID ); +// if(trace.fraction != 1 || trace.startsolid) +// { +// return; +// } +// +// //turn to face wall +// pm->ps->viewangles[YAW] = lerpyaw; +// // PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd); +// pm->cmd.angles[YAW] = ANGLE2SHORT( pm->ps->viewangles[YAW] ) - pm->ps->delta_angles[YAW]; +// pm->ps->weaponTime = 0; +// pm->cmd.upmove=0; +// VectorCopy(trace.endpos, pm->ps->origin); +// VectorCopy(vec3_origin, pm->ps->velocity); +// PM_GrabWallForClimb(pm->ps); +// +//} +////End of ledgegrab functions + + /* =============== PM_AddEvent @@ -3708,7 +4044,7 @@ static void PM_LadderMove( void ) { PM_SlideMove( qfalse ); // move without gravity } - +extern vmCvar_t g_climbable;// patch climable /* ============= CheckLadder [ ARTHUR TOMLIN ] @@ -3748,7 +4084,8 @@ void CheckLadder( void ) pm->trace (&trace, origin, pm->mins, pm->maxs, spot, pm->ps->clientNum, MASK_PLAYERSOLID); - if ((trace.fraction < 1) && (trace.surfaceFlags & SURF_LADDER)){ + if (trace.fraction < 1 && g_climbable.integer==666) Com_Printf("surface: %i contents: %i\n", trace.surfaceFlags, trace.contents); + if ((trace.fraction < 1) && ( (!pml.walking && !pml.groundPlane && (trace.surfaceFlags & g_climbable.integer)) || (trace.surfaceFlags & SURF_LADDER) || ( pm->waterlevel > 0.75))){// pml.groundPlan was added recently pml.ladder = qtrue; } else { pml.ladder = qfalse; @@ -3995,16 +4332,14 @@ void PmoveSingle (pmove_t *pmove) { PM_AirMove(); } else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) { #else - if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) { + if (pml.ladder) {// patch outofwaterclimb + PM_LadderMove(); + } else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) { #endif PM_WaterJumpMove(); } else if ( pm->waterlevel > 1 ) { // swimming PM_WaterMove(); -#ifdef SMOKINGUNS - } else if (pml.ladder) { - PM_LadderMove(); -#endif } else if ( pml.walking ) { // walking on ground PM_WalkMove(); diff --git a/code/game/bg_public.h b/code/game/bg_public.h index 3afa1f49..536f08ec 100644 --- a/code/game/bg_public.h +++ b/code/game/bg_public.h @@ -329,6 +329,7 @@ typedef enum { #define PMF_INVULEXPAND 16384 // invulnerability sphere set to full size #ifdef SMOKINGUNS #define PMF_SUICIDE 32768 +#define PMF_EDGE 128// patch URT edge climbing #endif #define PMF_ALL_TIMES (PMF_TIME_WATERJUMP|PMF_TIME_LAND|PMF_TIME_KNOCKBACK) diff --git a/code/game/g_active.c b/code/game/g_active.c index 61f083d0..92dc3f2b 100644 --- a/code/game/g_active.c +++ b/code/game/g_active.c @@ -393,7 +393,7 @@ void G_TouchTriggers( gentity_t *ent ) { (ent->client->oldbuttons & BUTTON_ACTIVATE)) && (!(ent->r.svFlags & SVF_BOT) || - (hit->moverState != MOVER_POS1 && hit->moverState != MOVER_POS2) ) ) // bots dont need to press down activate, but sometimes they need to close doors in order to re-open them outwards + (hit->moverState != MOVER_POS1 && hit->moverState != MOVER_POS2) ) ) // patch bots: bots dont need to press down activate, but sometimes they need to close doors in order to re-open them outwards continue; @@ -758,11 +758,11 @@ qboolean ClientInactivityTimer( gclient_t *client ) { client->inactivityTime = level.time + g_inactivity.integer * 1000; client->inactivityWarning = qfalse; } else if ( !client->pers.localClient ) { - if ( level.time > client->inactivityTime ) { + if ( level.time > client->inactivityTime && g_humancount) {// patch energy save trap_DropClient( client - level.clients, "Dropped due to inactivity" ); return qfalse; } - if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) { + if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning && g_humancount) {// patch energy save client->inactivityWarning = qtrue; trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" ); } @@ -1067,7 +1067,7 @@ void ClientEvents( gentity_t *ent, int oldEventSequence ) { #endif SelectSpawnPoint( ent->client->ps.origin, origin, angles, qfalse ); #else - SelectSpawnPoint( ent->client->ps.origin, origin, angles, qfalse, ent->mappart, ent->client); + SelectSpawnPoint( ent->client->ps.origin, origin, angles, ent->mappart, ent);// patch spawn point #endif TeleportPlayer( ent, origin, angles ); break; @@ -1274,6 +1274,7 @@ void ClientThink_real( gentity_t *ent ) { } client->pers.realPing = sum / NUM_PING_SAMPLES; +// client->ps.timeNudge = sum / NUM_PING_SAMPLES;// patch timenudge } else { // if g_truePing is off, use the normal ping @@ -1306,6 +1307,7 @@ void ClientThink_real( gentity_t *ent ) { // adjust the real ping to reflect the new latency client->pers.realPing += time - ucmd->serverTime; +// client->ps.timeNudge += time - ucmd->serverTime; // patch timenudge } //unlagged - lag simulation #2 @@ -1330,6 +1332,7 @@ void ClientThink_real( gentity_t *ent ) { if ( client->pers.latentSnaps ) { // adjust the real ping client->pers.realPing += client->pers.latentSnaps * (1000 / sv_fps.integer); +// client->ps.timeNudge += client->pers.latentSnaps * (1000 / sv_fps.integer);// patch timenudge // adjust the attack time so backward reconciliation will work client->attackTime -= client->pers.latentSnaps * (1000 / sv_fps.integer); } @@ -1527,12 +1530,12 @@ void ClientThink_real( gentity_t *ent ) { #ifdef SMOKINGUNS pm.ps->oldbuttons = client->buttons; #endif - + pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP;// if ( pm.ps->pm_type == PM_DEAD ) { pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; } else if ( ent->r.svFlags & SVF_BOT ) { - pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP; +// pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP;// #ifdef SMOKINGUNS client->ps.stats[STAT_FLAGS] |= SF_BOT; #endif diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 298d279a..792c87c1 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -1075,9 +1075,8 @@ void G_InitBots( qboolean restart ) { G_LoadBots(); G_LoadArenas(); - trap_Cvar_Register( &bot_minplayers, "bot_minplayers", "0", CVAR_SERVERINFO ); + trap_Cvar_Register( &bot_minplayers, "bot_minplayers", "0", CVAR_ARCHIVE ); // smokinguns -#ifdef SMOKINGUNS trap_Cvar_Register( &bot_noBR, "bot_noBR", "0", CVAR_ARCHIVE ); trap_Cvar_Register( &bot_noDuel, "bot_noDuel", "0", CVAR_ARCHIVE ); @@ -1085,7 +1084,6 @@ void G_InitBots( qboolean restart ) { || (g_gametype.integer == GT_DUEL && bot_noDuel.integer) ) { trap_SendConsoleCommand( EXEC_INSERT, "kickbots\n"); } -#endif if( g_gametype.integer == GT_SINGLE_PLAYER ) { trap_GetServerinfo( serverinfo, sizeof(serverinfo) ); diff --git a/code/game/g_client.c b/code/game/g_client.c index 0b0b89df..7204c8f3 100644 --- a/code/game/g_client.c +++ b/code/game/g_client.c @@ -50,6 +50,38 @@ void SP_info_player_deathmatch( gentity_t *ent ) { ent->flags |= FL_NO_HUMANS; } } +void SP_remove_info_player_deathmatch( gentity_t *ent ) {// patch additem: remove certain items + gentity_t *spot; + spot = NULL; + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + G_FreeEntity(spot); + } + G_FreeEntity(ent); +} +void SP_remove_pickup_money( gentity_t *ent ) { + gentity_t *spot; + spot = NULL; + while ((spot = G_Find (spot, FOFS(classname), "pickup_money")) != NULL) { + G_FreeEntity(spot); + } + G_FreeEntity(ent); +} +void SP_remove_item_money( gentity_t *ent ) { + gentity_t *spot; + spot = NULL; + while ((spot = G_Find (spot, FOFS(classname), "item_money")) != NULL) { + G_FreeEntity(spot); + } + G_FreeEntity(ent); +} +void SP_remove_item_belt( gentity_t *ent ) { + gentity_t *spot; + spot = NULL; + while ((spot = G_Find (spot, FOFS(classname), "item_belt")) != NULL) { + G_FreeEntity(spot); + } + G_FreeEntity(ent); +} /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) equivelant to info_player_deathmatch @@ -506,6 +538,146 @@ gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, ve #endif } +/* +=========== +SelectDeathmatchSpawnPoint + +Chooses a player start, deathmatch start as follows: + - random + - but not the spawn point "avoid" nearest to the point of death + - but not + - observes +============ +*/ +gentity_t *SelectDeathmatchSpawnPoint( gentity_t *ent, gentity_t *avoid ) {// patch spawn points + gentity_t *spot,*spot2; + int count; + int selection; + gentity_t *spots[MAX_SPAWN_POINTS]; + + count = 0; + spot = NULL; + + // round robin test +// while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { +// // obey nobots "1" +// if (spot2) { +// if (spot2->timestamp>spot->timestamp) { +// spot2 = spot; +// } +// } else { +// spot2 = spot; +// } +// } +// if (spot2) {spot2->timestamp = level.time;} +// return spot2; + + + // + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + // obey nobots "1" + if ( ( spot->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // obey nohumans "1" + if ( ( spot->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // do not telefrag and do not use avoid + if ( SpotWouldTelefrag( spot ) || spot==avoid ) { + continue; + } + // only if the spot wasn't used within the last 15 seconds + if (level.time < (spot->timestamp) || (level.time - (spot->timestamp))>15000) { + spots[ count ] = spot; + count++; + } + } + + if (!count) {// if we didn't find a spawn point + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + // obey nobots "1" + if ( ( spot->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // obey nohumans "1" + if ( ( spot->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // do not telefrag and do not use avoid + if ( SpotWouldTelefrag( spot ) || spot==avoid ) { + continue; + } + // only if the spot wasn't used within the last 10 seconds + if ((level.time - (spot->timestamp))>10000) { + spots[ count ] = spot; + count++; + } + } + } + + if (!count) {// if we didn't find a spawn point + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + // obey nobots "1" + if ( ( spot->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // obey nohumans "1" + if ( ( spot->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // do not telefrag and do not use avoid + if ( SpotWouldTelefrag( spot ) || spot==avoid ) { + continue; + } + // only if the spot wasn't used within the last 5 seconds + if ((level.time - (spot->timestamp))>5000) { + spots[ count ] = spot; + count++; + } + } + } + + if (!count) {// if we didn't find a spawn point + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + // obey nobots "1" + if ( ( spot->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // obey nohumans "1" + if ( ( spot->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // do not telefrag and do not use avoid + if ( SpotWouldTelefrag( spot ) || spot==avoid ) { + continue; + } + } + } + + if ( !count ) { // no spots that won't telefrag + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + // obey nobots "1" + if ( ( spot->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // obey nohumans "1" + if ( ( spot->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // don't care for telefrags anymore + spots[ count ] = spot; + count++; + } + } + + if ( !count ) {return NULL;} + + selection = rand() % count; + spots[ selection ]->timestamp = level.time; + return spots[ selection ]; +} + /* =========== SelectSpawnPoint @@ -513,29 +685,16 @@ SelectSpawnPoint Chooses a player start, deathmatch start, etc ============ */ -#ifndef SMOKINGUNS -gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles, qboolean isbot ) { - return SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles, isbot ); -#else -gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles, qboolean isbot, int mappart, gclient_t *client ) { - return SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles, isbot, mappart, client ); -#endif +gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles, int mappart, gentity_t *ent ) { +// return SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles, mappart, client ); - /* + gclient_t *client; gentity_t *spot; gentity_t *nearestSpot; + client = ent->client; nearestSpot = SelectNearestDeathmatchSpawnPoint( avoidPoint ); - - spot = SelectRandomDeathmatchSpawnPoint ( ); - if ( spot == nearestSpot ) { - // roll again if it would be real close to point of death - spot = SelectRandomDeathmatchSpawnPoint ( ); - if ( spot == nearestSpot ) { - // last try - spot = SelectRandomDeathmatchSpawnPoint ( ); - } - } + spot = SelectDeathmatchSpawnPoint ( ent, nearestSpot ); // find a single player start spot if (!spot) { @@ -547,7 +706,6 @@ gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles, q VectorCopy (spot->s.angles, angles); return spot; - */ } /* @@ -558,38 +716,24 @@ Try to find a spawn point marked 'initial', otherwise use normal spawn selection. ============ */ -#ifndef SMOKINGUNS -gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles, qboolean isbot ) { -#else -gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles, qboolean isbot, int mappart, gclient_t *client) { -#endif +gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles, int mappart, gentity_t *ent) { + gclient_t *client; gentity_t *spot; + client = ent->client; spot = NULL; - - while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) - { - if(((spot->flags & FL_NO_BOTS) && isbot) || - ((spot->flags & FL_NO_HUMANS) && !isbot)) - { - continue; - } - - if((spot->spawnflags & 0x01)) + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + if ( spot->spawnflags & 1 ) { break; } + } - if (!spot || SpotWouldTelefrag(spot)) -#ifndef SMOKINGUNS - return SelectSpawnPoint(vec3_origin, origin, angles, isbot); -#else - return SelectSpawnPoint( vec3_origin, origin, angles, isbot, mappart, client ); -#endif + if ( !spot || SpotWouldTelefrag( spot ) ) { + return SelectSpawnPoint( vec3_origin, origin, angles, mappart, ent ); + } VectorCopy (spot->s.origin, origin); -#ifndef SMOKINGUNS - origin[2] += 9; -#endif + //origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; @@ -1154,13 +1298,13 @@ static void ClientCleanName(const char *in, char *out, int outSize) { colorlessLen--; - if(ColorIndex(*in) == 0) - { - // Disallow color black in names to prevent players - // from getting advantage playing in front of black backgrounds - outpos--; - continue; - } +// if(ColorIndex(*in) == 0) +// { +// // Disallow color black in names to prevent players +// // from getting advantage playing in front of black backgrounds +// outpos--; +// continue; +// } } else { @@ -1213,8 +1357,9 @@ if desired. */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; - int teamTask, teamLeader, team, health; - char *s; + int teamTask, teamLeader, team, health, muted; + char *s, *s_internal; + char model[MAX_QPATH]; char headModel[MAX_QPATH]; char oldname[MAX_STRING_CHARS]; @@ -1227,8 +1372,12 @@ void ClientUserinfoChanged( int clientNum ) { char blueTeam[MAX_INFO_STRING]; char userinfo[MAX_INFO_STRING]; #ifdef SMOKINGUNS + char guid[MAX_INFO_STRING]; char version[MAX_INFO_STRING]; char md5[MAX_INFO_STRING]; + char engine_check[MAX_INFO_STRING]; + char ip[MAX_INFO_STRING]; + qtime_t q; #endif ent = g_entities + clientNum; @@ -1333,6 +1482,7 @@ void ClientUserinfoChanged( int clientNum ) { Q_strncpyz( client->pers.cleanname, oldname, sizeof(client->pers.cleanname) ); Q_CleanStr( client->pers.cleanname ); } +// } // Tequila: if there is a vote kick on the oldname, cancel renaming and // consider this is a mandatory vote to be kicked @@ -1392,6 +1542,13 @@ void ClientUserinfoChanged( int clientNum ) { Info_SetValueForKey(userinfo, "name", client->pers.netname); trap_SetUserinfo( clientNum, userinfo ); #endif +// if ( client->pers.connected == CON_CONNECTED ) { +// if ( strcmp( oldname, client->pers.netname ) ) { +// trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname) ); +// Q_strncpyz( client->pers.cleanname, client->pers.netname, sizeof(client->pers.cleanname) ); +// } +// } + // set max health #ifndef SMOKINGUNS @@ -1405,7 +1562,8 @@ void ClientUserinfoChanged( int clientNum ) { } } #else - health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); +// health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); + health = Comma_ValueForIndex( g_handicaps.string, clientNum ); client->pers.maxHealth = health; if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; @@ -1555,6 +1713,46 @@ void ClientUserinfoChanged( int clientNum ) { #endif strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" )); strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" )); + strcpy(guid, Info_ValueForKey(userinfo, "cl_guid")); + strcpy(engine_check, Info_ValueForKey( userinfo, "sa_engine_check1")); + strcpy(ip, Info_ValueForKey( userinfo, "ip")); + + // send over a subset of the userinfo keys so other clients can + // print scoreboards, display models, and play custom sounds + // this is not the userinfo, more like the configstring actually + + if ( ent->r.svFlags & SVF_BOT ) { + s = va("n\\%s\\t\\%i\\model\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", + client->pers.netname, client->sess.sessionTeam, model, + client->pers.maxHealth, client->sess.wins, client->sess.losses, + Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader); + trap_SetConfigstring( CS_PLAYERS+clientNum, s ); + G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s ); + } else { + trap_RealTime(&q); + muted = Comma_ValueForIndex( g_muted.string, clientNum ); + if (muted) { + s = va("n\\^7[^1:-x^7] %s\\t\\%i\\model\\%s\\g_redteam\\%s\\g_blueteam\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d\\v\\%s", + client->pers.netname, client->sess.sessionTeam, model, redTeam, blueTeam, + client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader, version); + } else { + s = va("n\\%s\\t\\%i\\model\\%s\\g_redteam\\%s\\g_blueteam\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d\\v\\%s", + client->pers.netname, client->sess.sessionTeam, model, redTeam, blueTeam, + client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader, version); + } + trap_SetConfigstring( CS_PLAYERS+clientNum, s ); + + s_internal = va("guid\\%s\\n\\%s\\t\\%i\\model\\%s\\g_redteam\\%s\\g_blueteam\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d\\v\\%s\\ip\\%s\\d\\%s\\m\\%s\\e\\%s", + guid, + client->pers.netname, client->sess.sessionTeam, model, redTeam, blueTeam, + client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader, version, ip, va("%02i.%02i.%04i %02i:%02i", q.tm_mday, q.tm_mon+1, 1900+q.tm_year,q.tm_hour,q.tm_min), md5, engine_check); + // \n name,\t team, \model, \g_redteam, \g_blueteam, \hc, \w, \l, \tt, \tl, \v + G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s_internal ); + } +} +/* + strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" )); + strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" )); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds @@ -1589,7 +1787,7 @@ void ClientUserinfoChanged( int clientNum ) { // this is not the userinfo, more like the configstring actually G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s ); -} +}*/ /* @@ -1618,6 +1816,7 @@ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; + static char temp[1024]; ent = &g_entities[ clientNum ]; @@ -1690,8 +1889,31 @@ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { #endif } - // get and distribute relevent parameters + // get and distribute relevant parameters G_LogPrintf( "ClientConnect: %i\n", clientNum ); + + if ( firstTime ) { + strcpy(temp,g_handicaps.string); + Comma_SetValueAtIndex(temp, clientNum, ""); + trap_Cvar_Set("g_handicaps", va(temp)); + + strcpy(temp,g_muted.string); + Comma_SetValueAtIndex(temp, clientNum, ""); + trap_Cvar_Set("g_muted", va(temp)); + + strcpy(temp,g_voteless.string); + Comma_SetValueAtIndex(temp, clientNum, ""); + trap_Cvar_Set("g_voteless", va(temp)); + + strcpy(temp,g_lockedTeams.string); + Comma_SetValueAtIndex(temp, clientNum, ""); + trap_Cvar_Set("g_lockedTeams", va(temp)); + + strcpy(temp,g_protected.string); + Comma_SetValueAtIndex(temp, clientNum, ""); + trap_Cvar_Set("g_protected", va(temp)); + } + ClientUserinfoChanged( clientNum ); #ifdef SMOKINGUNS // Tequila: cleanname must has been set if given name is valid @@ -1757,7 +1979,7 @@ void ClientBegin( int clientNum ) { vec3_t viewangles; vec3_t origin; #endif - int flags; + int flags,forcedteam,muted; ent = g_entities + clientNum; @@ -1807,6 +2029,16 @@ void ClientBegin( int clientNum ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname) ); } #else + forcedteam = Comma_ValueForIndex( g_lockedTeams.string, clientNum ); + if (forcedteam==3) { + client->sess.sessionTeam = TEAM_SPECTATOR; + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "You have been forced to spectate." ); + } + muted = Comma_ValueForIndex( g_muted.string, clientNum ); + if (muted==1) { + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "You have been muted." ); + } + if ( client->sess.sessionTeam < TEAM_SPECTATOR ) { if ( g_gametype.integer != GT_DUEL ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname) ); @@ -1949,11 +2181,11 @@ Returns the best of the added weapons. static int AddDefaultWeapons( playerState_t *ps ) { int startingWeapon; - if ( g_gametype.integer == GT_DUEL ) { - startingWeapon = WP_REM58; - } else { +// if ( g_gametype.integer == GT_DUEL ) { +// startingWeapon = WP_REM58; +// } else { startingWeapon = g_startingWeapon.integer; - } +// } if ( startingWeapon < 0 || startingWeapon >= WP_NUM_WEAPONS ) { startingWeapon = 0; @@ -2076,9 +2308,7 @@ void ClientSpawn(gentity_t *ent) { } else { spawnPoint = SelectSpawnPoint ( client->ps.origin, - spawn_origin, spawn_angles, - !!(ent->r.svFlags & SVF_BOT), - ent->mappart, ent->client); + spawn_origin, spawn_angles, ent->mappart, ent); } #endif } @@ -2092,8 +2322,7 @@ void ClientSpawn(gentity_t *ent) { spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); #else - spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles, - !!(ent->r.svFlags & SVF_BOT), ent->mappart, ent->client ); + spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles, ent->mappart, ent ); #endif } else @@ -2101,11 +2330,7 @@ void ClientSpawn(gentity_t *ent) { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint ( client->ps.origin, -#ifndef SMOKINGUNS - spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); -#else - spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT), ent->mappart, client); -#endif + spawn_origin, spawn_angles, ent->mappart, ent); } } client->pers.teamState.state = TEAM_ACTIVE; @@ -2461,6 +2686,14 @@ void ClientDisconnect( int clientNum ) { StopFollowing( &g_entities[i] ); } } +// // clear all votes to prevent vote enabling by a single person via reconnect// patch kick vote prevention +// for ( i=0 ; iclient->pers.connected == CON_CONNECTED diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index 1c80ee93..1cd19d15 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -252,6 +252,99 @@ int ClientNumberFromCleanName( char *name ) { return -1; } + +/* +================== +Cmd_AASTrace_f + + +================== +*/ +void Cmd_AASTrace_f(gentity_t *ent)// patch DEBUG +{ + trace_t trace; + char arg1[MAX_STRING_TOKENS]; + int contentmask,entcontent; + vec3_t forward,end,direction,angle; + + if ( trap_Argc() != 2 ) { + trap_SendServerCommand( ent-g_entities, "print \"usage: aastrace CONTENTMASK\n\"" ); + contentmask=0x80000000-1; + } else { + trap_Argv( 1, arg1, sizeof( arg1 ) ); + contentmask = atoi( arg1 ); + } + VectorCopy(ent->s.apos.trBase,direction); + VectorNormalize(direction);// normalize + VectorMA(ent->r.currentOrigin, 40, direction, end); + trap_Trace(&trace, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, end, ent->s.clientNum, contentmask); + if (trace.entityNum>=1022) { + trap_SendServerCommand( ent-g_entities, va("print \"Content is: %s C%i/C%i/C%i [S%i,A%i] E%i (%f,%f,%f)\n\"", + g_entities[trace.entityNum].classname, trace.contents, contentmask, 0, + trace.startsolid,trace.allsolid, + trace.entityNum, + end[0],end[1],end[2])); + } else { + trap_SendServerCommand( ent-g_entities, va("print \"Content is: %s C%i/C%i/C%i [S%i,A%i] E%i (%f,%f,%f) (%f,%f,%f)\n\"", + g_entities[trace.entityNum].classname, trace.contents, contentmask, g_entities[trace.entityNum].r.contents, + trace.startsolid,trace.allsolid, + trace.entityNum, + end[0],end[1],end[2], + g_entities[trace.entityNum].s.origin[0],g_entities[trace.entityNum].s.origin[1],g_entities[trace.entityNum].s.origin[2])); + VectorSubtract(g_entities[trace.entityNum].s.origin, ent->r.currentOrigin, direction); + // } + vectoangles(direction, angle); + SetClientViewAngle(ent,angle); + trap_SendServerCommand( ent-g_entities, va("print \"new angles: (%f,%f,%f)\n\"",direction[0],direction[1],direction[2])); + + } + +} + +/* +================== +Cmd_EntityValues_f + + +================== +*/ +void Cmd_EntityValues_f(gentity_t *ent) {// patch DEBUG + trap_SendServerCommand( ent-g_entities, + va("print \"s.apos.trBase: (%f,%f,%f), s.pos.trBase: (%f,%f,%f), r.currentOrigin: (%f,%f,%f)\n\"" +// angle: %f; s.angles: (%f,%f,%f), s.angles2: (%f,%f,%f), s.origin: (%f,%f,%f), s.origin2: (%f,%f,%f), movedir: (%f,%f,%f), r.currentAngle: (%f,%f,%f), [they do not change] + +// ,ent->angle +// ,ent->s.angles[0] +// ,ent->s.angles[1] +// ,ent->s.angles[2] +// ,ent->s.angles2[0] +// ,ent->s.angles2[1] +// ,ent->s.angles2[2] +// ,ent->s.origin[0] +// ,ent->s.origin[1] +// ,ent->s.origin[2] +// ,ent->s.origin2[0] +// ,ent->s.origin2[1] +// ,ent->s.origin2[2] +// ,ent->movedir[0] +// ,ent->movedir[1] +// ,ent->movedir[2] +// ,ent->r.currentAngles[0] +// ,ent->r.currentAngles[1] +// ,ent->r.currentAngles[2] + ,ent->s.apos.trBase[0] + ,ent->s.apos.trBase[1] + ,ent->s.apos.trBase[2] + ,ent->s.pos.trBase[0] + ,ent->s.pos.trBase[1] + ,ent->s.pos.trBase[2] + ,ent->r.currentOrigin[0] + ,ent->r.currentOrigin[1] + ,ent->r.currentOrigin[2] + ));//)); +}// +// + /* ================== Cmd_Give_f @@ -581,28 +674,25 @@ void BroadcastTeamChange( gclient_t *client, int oldTeam ) return; if ( client->sess.sessionTeam == TEAM_RED ) { - trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined %s.\n\"", client->pers.netname, g_redteam.string) ); + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " ^3joined %s.\n\"", client->pers.netname, g_redteam.string) ); } else if ( client->sess.sessionTeam == TEAM_BLUE ) { - trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined %s.\n\"", client->pers.netname, g_blueteam.string)); + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " ^3joined %s.\n\"", client->pers.netname, g_blueteam.string)); } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) { // Joe Kari: no need to shout, especially if I want to spec someone suspicious, I don't want to get caught. //trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"", client->pers.netname)); - trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " joined the spectators.\n\"", client->pers.netname)); + trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " joined the spectators.\n\"", client->pers.netname));// patch don't annoy players } else if ( client->sess.sessionTeam == TEAM_FREE ) { - trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the gunfight.\n\"", client->pers.netname)); + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " ^3joined the gunfight.\n\"", client->pers.netname)); } #else if ( client->sess.sessionTeam == TEAM_RED ) { - trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the red team.\n\"", + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " ^3joined the ^1red ^3team.\n\"", client->pers.netname) ); } else if ( client->sess.sessionTeam == TEAM_BLUE ) { - trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the blue team.\n\"", - client->pers.netname)); - } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) { - trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"", + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " ^3joined the ^4blue ^3team.\n\"", client->pers.netname)); } else if ( client->sess.sessionTeam == TEAM_FREE ) { - trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"", + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " ^3joined the battle.\n\"", client->pers.netname)); } #endif @@ -1067,7 +1157,7 @@ Cmd_Team_f ================= */ void Cmd_Team_f( gentity_t *ent ) { - int oldTeam; + int oldTeam,forcedteam;// patch forceteam, mute char s[MAX_TOKEN_CHARS]; if ( trap_Argc() != 2 ) { @@ -1115,6 +1205,20 @@ void Cmd_Team_f( gentity_t *ent ) { trap_Argv( 1, s, sizeof( s ) ); #ifdef SMOKINGUNS + forcedteam = Comma_ValueForIndex( g_lockedTeams.string, ent-g_entities ); + if ((s[0] == 'b' && forcedteam>0 && forcedteam!=2) + || (s[0] == 'B' && forcedteam>0 && forcedteam!=20) + || (s[0] == 'r' && forcedteam>0 && forcedteam!=1) + || (s[0] == 'R' && forcedteam>0 && forcedteam!=10) + || (s[0] == 'f' && forcedteam>0 && forcedteam<10) + || (s[0] == 'F' && forcedteam>0 && forcedteam<10) + //|| (s[0] == 's' && forcedteam>0 && forcedteam!=3) + //|| (s[0] == 'S' && forcedteam>0 && forcedteam!=3) + ) { + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "You are forbidden to join/change team." ); + return; + } + // change to the spectators if ( g_gametype.integer >= GT_RTP) { if(s[0] == 'b' || s[0] == 'B'){ @@ -1323,7 +1427,7 @@ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, cons #define EC "\x19" void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) { - int j; + int j,muted; gentity_t *other; int color; char name[64]; @@ -1333,25 +1437,38 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) // don't let text be too long for malicious reasons char text[MAX_SAY_TEXT]; char location[64]; + int clientid = ent->client - level.clients; + qtime_t q; + if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) { mode = SAY_ALL; } + trap_RealTime(&q); - -#ifdef SMOKINGUNS - // Joe Kari: new feature that allow admin to mute someone - // It is probably more funny to display "(muted)" than turn it off completely ^^ - if ( ent->client->pers.muted ) { - if ( ent->client->pers.muted > 1 ) return ; - chatText = "^9(muted)" ; - } -#endif + muted = Comma_ValueForIndex( g_muted.string, clientid ); + if (muted==1) { + G_LogPrintf( "muted: %i: [%s] %s: %s\n", clientid, va("%02i.%02i.%04i %02i:%02i", q.tm_mday, q.tm_mon+1, 1900+q.tm_year,q.tm_hour,q.tm_min), ent->client->pers.netname, chatText ); + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "You have been muted." ); + return; + } +// #ifdef SMOKINGUNS +// // Joe Kari: new feature that allow admin to mute someone +// // It is probably more funny to display "(muted)" than turn it off completely ^^ +// if ( ent->client->pers.muted ) { +// if ( ent->client->pers.muted > 1 ) return ; +// chatText = "^9(muted)" ; +// } +// #endif switch ( mode ) { default: case SAY_ALL: - G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText ); + if (Q_strncasecmp("!!", chatText, 2)) {// log as is + G_LogPrintf( "say: %i: [%s] %s: %s\n", clientid, va("%02i.%02i.%04i %02i:%02i", q.tm_mday, q.tm_mon+1, 1900+q.tm_year,q.tm_hour,q.tm_min), ent->client->pers.netname, chatText ); + } else {// log but omit the second exclamation mark + G_LogPrintf( "say: %i: [%s] %s: %s\n", clientid, va("%02i.%02i.%04i %02i:%02i", q.tm_mday, q.tm_mon+1, 1900+q.tm_year,q.tm_hour,q.tm_min), ent->client->pers.netname, chatText+1 ); + } Com_sprintf (name, sizeof(name), "%s%c%c"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_GREEN; #ifdef SMOKINGUNS @@ -1359,7 +1476,7 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) #endif break; case SAY_TEAM: - G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText ); + G_LogPrintf( "sayteam: %i: [%s] %s: %s\n", clientid, va("%02i.%02i.%04i %02i:%02i", q.tm_mday, q.tm_mon+1, 1900+q.tm_year,q.tm_hour,q.tm_min), ent->client->pers.netname, chatText ); if (Team_GetLocationMsg(ent, location, sizeof(location))) Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC") (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location); @@ -1372,6 +1489,7 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) #endif break; case SAY_TELL: + if (target!=ent) G_LogPrintf( "tell: %i to %i: [%s] %s to %s: %s\n", clientid, (target->client-level.clients), va("%02i.%02i.%04i %02i:%02i", q.tm_mday, q.tm_mon+1, 1900+q.tm_year,q.tm_hour,q.tm_min), ent->client->pers.netname, target->client->pers.netname, chatText ); if (target && g_gametype.integer >= GT_TEAM && target->client->sess.sessionTeam == ent->client->sess.sessionTeam && Team_GetLocationMsg(ent, location, sizeof(location))) @@ -1413,12 +1531,15 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) Com_sprintf(endname, sizeof(endname), "%s", name); #endif - // send it to all the apropriate clients + if (Q_strncasecmp("!!", text, 2)) {// if not !!, say it + if (Q_strncasecmp("!", text, 1)) { + // send it to all the appropriate clients for (j = 0; j < level.maxclients; j++) { other = &g_entities[j]; -#ifndef SMOKINGUNS - G_SayTo( ent, other, mode, color, name, text ); -#else + +//#ifndef SMOKINGUNS +// G_SayTo( ent, other, mode, color, name, text ); +//#else if(g_splitChat.integer && g_gametype.integer >= GT_RTP){ if(ent->client->sess.sessionTeam >= TEAM_SPECTATOR && @@ -1429,7 +1550,11 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) color = COLOR_WHITE; G_SayTo( ent, other, mode, color, endname, text ); -#endif + } + } else { + trap_SendServerCommand( -1, va("print \"%s%c%c%s\n\"", endname, Q_COLOR_ESCAPE, COLOR_WHITE, text)); + } +//#endif } } @@ -1486,7 +1611,6 @@ static void Cmd_Tell_f( gentity_t *ent ) { p = ConcatArgs( 2 ); - 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 // also don't send the chat back to a bot @@ -1683,7 +1807,7 @@ static void Cmd_VoiceTaunt_f( gentity_t *ent ) { who = g_entities + ent->client->lastkilled_client; if (who->client) { // who is the person I just killed - if (who->client->lasthurt_mod == MOD_GAUNTLET) { + if (who->client->lasthurt_mod == MOD_KNIFE) { if (!(who->r.svFlags & SVF_BOT)) { G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse ); // and I killed them with a gauntlet } @@ -1724,23 +1848,63 @@ static void Cmd_VoiceTaunt_f( gentity_t *ent ) { // just say something G_Voice( ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse ); } -#else +#endif + /* ================== IsAvailableMap ================== */ static qboolean IsAvailableMap(const char *mapname) { + char buf[MAX_STRING_CHARS]; char expanded[MAX_QPATH]; fileHandle_t f; // make sure the map level file exists Com_sprintf (expanded, sizeof(expanded), "maps/%s.bsp", mapname); trap_FS_FOpenFile( expanded, &f, FS_READ ); + if (!f) { + return qfalse; + } else { + trap_FS_FCloseFile(f); + } + + + if (g_vstrInsteadOfMap.integer) { + trap_Cvar_VariableStringBuffer( va("%s",mapname), buf, sizeof(buf) ); + if (!strlen(buf)) return qfalse; + } + + return qtrue; +} + +/* +================== +IsAvailableSpecial +================== +*/ +static qboolean IsAvailableSpecial(const char *special) { + char buf[MAX_STRING_CHARS]; + trap_Cvar_VariableStringBuffer(va("weapons%s",special),buf,sizeof(buf)); + G_LogPrintf( va("weapons%s",special) ); + return strlen(buf) ? qtrue : qfalse ; +} +/* +================== +IsPatchedMap +================== +*/ +static qboolean IsPatchedMap(const char *mapname) { // patch dm on br maps: + char expanded[MAX_QPATH]; + fileHandle_t f; + + // make sure the map level file exists + Com_sprintf (expanded, sizeof(expanded), "powerups/%s.txt", mapname); + trap_FS_FOpenFile( expanded, &f, FS_READ ); return f ? qtrue : qfalse ; } -#endif + static char *gc_orders[] = { @@ -1802,70 +1966,122 @@ static const char *gameNames[] = { #endif }; + +int Q_strncasecmp (const char *s1, const char *s2, int n) // patch callvote: improved voting system +{ + int c1, c2; + + do + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + } while (c1); + + return 0; // strings are equal +} + +const char* getGameName(int i) { + if( i == GT_SINGLE_PLAYER || i < GT_FFA || i > GT_BR) { + return "unknown gametype"; + } else { + return gameNames[i]; + } +} + +const static char *special[17] = {"All", "Knife", "Knives", "Remington", "Remingtons", "Colt", "Rifle", "Lightning", "Sharps", "Shotgun", "SawedOff", "Pumpgun", "Dynamite", "Molotov", "NoFirearms", "NoGatling", "NoDynamite"}; /* ================== Cmd_CallVote_f ================== */ void Cmd_CallVote_f( gentity_t *ent ) { + char* s; + char* sep; + char* csep; char* c; - int i; + char copy[MAX_STRING_CHARS]; + int i,j; + int voteless,protected,old; // patch callvote: improved voting system char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; + char buf[256]; + char key[256]; int diff; int clientnum; char* arg_str; + char list[MAX_STRING_TOKENS-40]; + gclient_t *cl; + int clientid = ent-g_entities; + + voteless = Comma_ValueForIndex( g_voteless.string, clientid ); + if (voteless==1) { + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "You are not allowed to call a vote." ); + return; + } if ( !g_allowVote.integer ) { - trap_SendServerCommand( ent-g_entities, "print \"Voting is not allowed here.\n\"" ); - trap_SendServerCommand( ent-g_entities, "cp \"Voting is not allowed here\n\"" ); + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "Voting not allowed here." ); return; } #ifdef SMOKINGUNS - // Joe Kari: muted player can't callvote... hihihi ^^ - if ( ent->client->pers.muted ) { - trap_SendServerCommand( ent-g_entities, "print \"You try to call a vote, but nobody can hear your voice.\n\"" ); - trap_SendServerCommand( ent-g_entities, "cp \"...\n\"" ); - return; - } +// // Joe Kari: muted player can't callvote... hihihi ^^ +// if ( ent->client->pers.muted ) { +// trap_SendServerCommand( ent-g_entities, "print \"You try to call a vote, but nobody can hear your voice.\n\"" ); +// trap_SendServerCommand( ent-g_entities, "cp \"...\n\"" ); +// return; +// } // Joe Kari: a tips to avoid stOopid punk with fast computer to call a vote before anyone have time to join, // allowing them to do everything they want... now you have to wait some time. if ( level.time < ( g_voteMinLevelTime.integer * 1000 ) ) { diff = ( g_voteMinLevelTime.integer * 1000 - level.time ) / 1000 ; - trap_SendServerCommand( ent-g_entities, va("print \"This level has just begun, wait %i seconds before calling a vote...\n\"", diff ) ); - trap_SendServerCommand( ent-g_entities, va("cp \"Level has just begun, wait %i seconds\n\"", diff ) ); + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", va("Level has just begun, wait %i seconds before calling a vote...", diff) ); return; } #endif if ( level.voteTime ) { - trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" ); - trap_SendServerCommand( ent-g_entities, "cp \"A vote is already in progress\n\"" ); + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "A vote is already in progress." ); return; } if ( ent->client->pers.voteCount >= g_maxVote.integer ) { - trap_SendServerCommand( ent-g_entities, "print \"You have reached your maximum number of votes.\n\"" ); - trap_SendServerCommand( ent-g_entities, "cp \"Maximum number of votes reached\n\"" ); + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "You have called the maximum number of votes." ); return; } #ifndef SMOKINGUNS - if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { + if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {(sizeof(special)/sizeof(special[0])) #else // Tequila: g_voteDelay can now be set to the number of seconds a player must wait between 2 votes if ( ent->client->pers.lastVoteTime > 0 && level.time < ent->client->pers.lastVoteTime + g_voteDelay.integer * 1000 ) { diff = ( ent->client->pers.lastVoteTime + g_voteDelay.integer * 1000 - level.time ) / 1000 ; - trap_SendServerCommand( ent-g_entities, va("print \"You can't call too many vote, wait %i seconds...\n\"", diff ) ) ; - trap_SendServerCommand( ent-g_entities, va("cp \"Can't call too many vote, wait %i seconds\n\"", diff ) ) ; + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", va("You can't call too many votes, wait %i seconds.", diff) ); + return; + } + if ( level.time <= level.voteTime + 10*1000 ) { + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", va("Only allowed to call a vote each %i seconds.", 10) ); + trap_SendServerCommand( ent-g_entities, va("cp \"Wait %i seconds...\n\"", + (level.voteTime + 10*1000 - level.time)/1000 +1) ); return; } - if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR && (g_gametype.integer != GT_DUEL || ent->client->realspec)) { -#endif - trap_SendServerCommand( ent-g_entities, "print \"Can't call a vote as spectator.\n\"" ); - trap_SendServerCommand( ent-g_entities, "cp \"Can't call a vote as spectator\n\"" ); + if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR && g_humancount>1 && (g_gametype.integer != GT_DUEL || ent->client->realspec)) {// let spectators on an empty server change the map + #endif + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "Not allowed to call a vote as spectator." ); return; } @@ -1879,7 +2095,7 @@ void Cmd_CallVote_f( gentity_t *ent ) { case '\n': case '\r': case ';': - trap_SendServerCommand( ent-g_entities, va("print \"Invalid vote string argument: %s\n\"",arg2) ); + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", va("Invalid vote string argument: %s.", arg2) ); return; break; } @@ -1902,28 +2118,97 @@ void Cmd_CallVote_f( gentity_t *ent ) { return; } #else + + if ( !strlen(arg1) ) {// if no vote type was specified + if (g_allowVote.integer == 0) {// if no vote is allowed + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", "Voting currently disabled." ); + return; + } else {// list the available votes + s = "The following votes are available:"; + sep = ""; csep = ","; + if ( (g_allowVote.integer & 1) != 0) {s = va("%s%s ^2map_restart^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 2) != 0) {s = va("%s%s ^2nextmap^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 4) != 0) {s = va("%s%s ^2map^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 8) != 0) {s = va("%s%s ^2g_gametype^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 16) != 0) {s = va("%s%s ^2kick^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 32) != 0) {s = va("%s%s ^2kicknum^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 64) != 0) {s = va("%s%s ^2g_dowarmup^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 128) != 0) {s = va("%s%s ^2timelimit^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 256) != 0) {s = va("%s%s ^2fraglimit^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 512) != 0) {s = va("%s%s ^2mapcycle^3", s, sep);sep=csep;} + if ( (g_allowVote.integer & 1024) != 0) {s = va("%s%s ^2weapons^3", s, sep);sep=csep;} + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", s); + return; + } + } + + // correct vote types (aliases) + if ( !Q_stricmp( arg1, "special" )) { + Q_strncpyz(arg1, "weapons", sizeof(arg1)); + } else if ( !Q_stricmp( arg1, "specials" )) { + Q_strncpyz(arg1, "weapons", sizeof(arg1)); + } else if ( !Q_stricmp( arg1, "clientkick" )) { + Q_strncpyz(arg1, "kicknum", sizeof(arg1)); + } else if ( !Q_stricmp( arg1, "maps" )) { + Q_strncpyz(arg1, "map", sizeof(arg1)); + } + + + if ( ( !Q_stricmp( arg1, "map_restart" )) && ((g_allowVote.integer & 1) != 0) ) { + } else if ( ( !Q_stricmp( arg1, "nextmap" ) ) && ((g_allowVote.integer & 2) != 0) ) { + } else if ( ( !Q_stricmp( arg1, "map" ) ) && ((g_allowVote.integer & 4) != 0) ) { + } else if ( ( !Q_stricmp( arg1, "g_gametype" ) ) && ((g_allowVote.integer & 8) != 0) ) { + } else if ( ( !Q_stricmp( arg1, "kick" ) ) && ((g_allowVote.integer & 16) != 0) && g_allowVoteKick.integer) { + } else if ( ( !Q_stricmp( arg1, "kicknum" ) ) && ((g_allowVote.integer & 32) != 0) && g_allowVoteKick.integer) { + } else if ( ( !Q_stricmp( arg1, "g_doWarmup" ) ) && ((g_allowVote.integer & 64) != 0) ) { + } else if ( ( !Q_stricmp( arg1, "timelimit" ) ) && ((g_allowVote.integer & 128) != 0) ) { + } else if ( ( !Q_stricmp( arg1, "fraglimit" ) ) && ((g_allowVote.integer & 256) != 0) ) { + } else if ( ( !Q_stricmp( arg1, "mapcycle" ) ) && ((g_allowVote.integer & 512) != 0) ) { + } else if ( ( !Q_stricmp( arg1, "weapons" ) ) && ((g_allowVote.integer & 1024) != 0) ) { + if (!strlen(arg2)) { + Q_strncpyz(copy,"The available weapon specials include:",sizeof(copy)); + sep = ""; csep = ","; + j = (sizeof(special)/sizeof(special[0])); + for (i=0;iclient->pers.netname, arg1, ConcatArgs(2) ); + } else if ( !Q_stricmp( arg1, "map" ) ) { + if ( strlen(arg2) && !IsAvailableMap(arg2) ) { + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", va("map not found or not available: %s", arg2 ) ); return; } // Validate argument, it must be an integer } else if ( !Q_stricmp( arg1, "g_gametype" ) && !strcmp(arg2,va("%i",atoi(arg2))) ) { // Validate argument, it must be a name - } else if ( !Q_stricmp( arg1, "mute" ) && strlen(arg2) ) { - // Validate argument, it must be a name - } else if ( !Q_stricmp( arg1, "unmute" ) && strlen(arg2) ) { - // Validate argument, it must be a name +// } else if ( !Q_stricmp( arg1, "mute" ) && strlen(arg2) ) { +// // Validate argument, it must be a name +// } else if ( !Q_stricmp( arg1, "unmute" ) && strlen(arg2) ) { +// // Validate argument, it must be a name } else if ( !Q_stricmp( arg1, "kick" ) && strlen(arg2) ) { // Validate argument, it must be an integer } else if ( !Q_stricmp( arg1, "kicknum" ) && !strcmp(arg2,va("%i",atoi(arg2))) ) { - // Validate argument, it must be an integer (DEPRECATED) - } else if ( !Q_stricmp( arg1, "clientkick" ) && !strcmp(arg2,va("%i",atoi(arg2))) ) { // Validate argument, it must be an integer } else if ( !Q_stricmp( arg1, "g_doWarmup" ) && !strcmp(arg2,va("%i",atoi(arg2))) ) { // Validate argument, it must be an integer @@ -1931,19 +2216,59 @@ void Cmd_CallVote_f( gentity_t *ent ) { // Validate argument, it must be an integer } else if ( !Q_stricmp( arg1, "fraglimit" ) && !strcmp(arg2,va("%i",atoi(arg2))) ) { } else if ( !Q_stricmp( arg1, "mapcycle" ) ) { + } else if ( !Q_stricmp( arg1, "weapons" ) ) { + if ( strlen(arg2) && !IsAvailableSpecial(arg2) ) { + G_SayTo( ent, ent, SAY_ALL, COLOR_YELLOW, "console: ", va("weapons special not found or not available: %s", arg2 ) ); + return; + } } else { - trap_SendServerCommand( ent-g_entities, va("print \"Invalid vote string command: %s\n\"", ConcatArgs(1) ) ); - trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map , g_gametype , mute , unmute , kick , kicknum [reason], g_doWarmup , timelimit