Lap based power level

This commit is contained in:
Sally Coolatta 2022-08-14 06:00:37 -04:00
parent f54eaee9af
commit ebb34ff9d4
22 changed files with 415 additions and 317 deletions

View file

@ -1641,34 +1641,14 @@ void CV_SaveVars(UINT8 **p, boolean in_demo)
// the client will reset all netvars to default before loading
WRITEUINT16(*p, 0x0000);
for (cvar = consvar_vars; cvar; cvar = cvar->next)
if (((cvar->flags & CV_NETVAR) && !CV_IsSetToDefault(cvar)) || (in_demo && cvar->netid == cv_numlaps.netid))
if ((cvar->flags & CV_NETVAR) && !CV_IsSetToDefault(cvar))
{
if (in_demo)
WRITESTRING(*p, cvar->name);
else
WRITEUINT16(*p, cvar->netid);
// UGLY HACK: Save proper lap count in net replays
if (in_demo && cvar->netid == cv_numlaps.netid)
{
if (cv_basenumlaps.value &&
(!(mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
|| (mapheaderinfo[gamemap - 1]->numlaps > cv_basenumlaps.value))
)
{
WRITESTRING(*p, cv_basenumlaps.string);
}
else
{
char buf[9];
sprintf(buf, "%d", mapheaderinfo[gamemap - 1]->numlaps);
WRITESTRING(*p, buf);
}
}
else
{
WRITESTRING(*p, cvar->string);
}
WRITESTRING(*p, cvar->string);
WRITEUINT8(*p, false);
++count;

View file

@ -102,7 +102,6 @@ static void Got_DiscordInfo(UINT8 **cp, INT32 playernum);
static void PointLimit_OnChange(void);
static void TimeLimit_OnChange(void);
static void NumLaps_OnChange(void);
static void BaseNumLaps_OnChange(void);
static void Mute_OnChange(void);
static void AutoBalance_OnChange(void);
@ -141,7 +140,6 @@ static void Color4_OnChange(void);
static void DummyConsvar_OnChange(void);
static void SoundTest_OnChange(void);
static void BaseNumLaps_OnChange(void);
static void KartFrantic_OnChange(void);
static void KartSpeed_OnChange(void);
static void KartEncore_OnChange(void);
@ -467,10 +465,9 @@ static CV_PossibleValue_t pointlimit_cons_t[] = {{1, "MIN"}, {MAXSCORE, "MAX"},
consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange);
static CV_PossibleValue_t timelimit_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "None"}, {0, NULL}};
consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange);
static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}};
consvar_t cv_numlaps = CVAR_INIT ("numlaps", "3", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t, NumLaps_OnChange);
static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, "Map default"}, {0, NULL}};
consvar_t cv_basenumlaps = CVAR_INIT ("basenumlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange);
static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {MAX_LAPS, "MAX"}, {0, "Map default"}, {0, NULL}};
consvar_t cv_numlaps = CVAR_INIT ("numlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, numlaps_cons_t, NumLaps_OnChange);
// Point and time limits for every gametype
INT32 pointlimits[NUMGAMETYPES];
@ -691,7 +688,6 @@ void D_RegisterServerCommands(void)
// misc
CV_RegisterVar(&cv_pointlimit);
CV_RegisterVar(&cv_numlaps);
CV_RegisterVar(&cv_basenumlaps);
CV_RegisterVar(&cv_autobalance);
CV_RegisterVar(&cv_teamscramble);
@ -4480,24 +4476,6 @@ static void PointLimit_OnChange(void)
CONS_Printf(M_GetText("Point limit disabled\n"));
}
static void NumLaps_OnChange(void)
{
if (K_CanChangeRules() == false)
{
return;
}
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
&& (cv_numlaps.value > mapheaderinfo[gamemap - 1]->numlaps))
{
CV_StealthSetValue(&cv_numlaps, mapheaderinfo[gamemap - 1]->numlaps);
}
// Just don't be verbose
if (gametyperules & GTR_CIRCUIT)
CONS_Printf(M_GetText("Number of laps set to %d\n"), cv_numlaps.value);
}
static void NetTimeout_OnChange(void)
{
connectiontimeout = (tic_t)cv_nettimeout.value;
@ -5759,22 +5737,24 @@ static void Command_ShowTime_f(void)
}
// SRB2Kart: On change messages
static void BaseNumLaps_OnChange(void)
static void NumLaps_OnChange(void)
{
if (K_CanChangeRules() == true)
if (K_CanChangeRules() == false)
{
const char *str = va("%d", cv_basenumlaps.value);
return;
}
if (cv_basenumlaps.value == 0)
{
str = "map defaults";
}
CONS_Printf(M_GetText("Number of laps will be changed to %s next round.\n"), str);
if (leveltime < starttime)
{
CONS_Printf(M_GetText("Number of laps have been set to %d.\n"), cv_numlaps.value);
numlaps = (UINT8)cv_numlaps.value;
}
else
{
CONS_Printf(M_GetText("Number of laps will be set to %d next round.\n"), cv_numlaps.value);
}
}
static void KartFrantic_OnChange(void)
{
if (K_CanChangeRules() == false)

View file

@ -42,7 +42,6 @@ extern consvar_t cv_itemrespawn;
extern consvar_t cv_pointlimit;
extern consvar_t cv_timelimit;
extern consvar_t cv_numlaps;
extern consvar_t cv_basenumlaps;
extern UINT32 timelimitintics;
extern consvar_t cv_allowexitlevel;

View file

@ -537,6 +537,7 @@ typedef struct player_s
INT16 totalring; // Total number of rings obtained for GP
tic_t realtime; // integer replacement for leveltime
UINT8 laps; // Number of laps (optional)
UINT8 latestlap;
INT32 starpostnum; // The number of the last starpost you hit
UINT8 ctfteam; // 0 == Spectator, 1 == Red, 2 == Blue

View file

@ -700,6 +700,7 @@ extern INT16 scramblecount; //for CTF team scramble
extern INT32 cheats;
// SRB2kart
extern UINT8 numlaps;
extern UINT8 gamespeed;
extern boolean franticitems;
extern boolean encoremode, prevencoremode;

View file

@ -1999,6 +1999,7 @@ void G_BeginRecording(void)
WRITEUINT8(demo_p, demoflags);
WRITEUINT8(demo_p, gametype & 0xFF);
WRITEUINT8(demo_p, numlaps);
// file list
m = demo_p;/* file count */
@ -2429,6 +2430,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
p += 16; // map md5
flags = READUINT8(p); // demoflags
p++; // gametype
p++; // numlaps
G_SkipDemoExtraFiles(&p);
aflags = flags & (DF_TIMEATTACK|DF_BREAKTHECAPSULES);
@ -2486,6 +2488,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
p += 16; // mapmd5
flags = READUINT8(p);
p++; // gametype
p++; // numlaps
G_SkipDemoExtraFiles(&p);
if (!(flags & aflags))
{
@ -2600,6 +2603,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
}
pdemo->gametype = READUINT8(info_p);
pdemo->numlaps = READUINT8(info_p);
pdemo->addonstatus = G_CheckDemoExtraFiles(&info_p, true);
info_p += 4; // RNG seed
@ -2626,20 +2630,11 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
if (!stricmp(kartspeed_cons_t[j].strvalue, svalue))
pdemo->kartspeed = kartspeed_cons_t[j].value;
}
else if (netid == cv_basenumlaps.netid && pdemo->gametype == GT_RACE)
pdemo->numlaps = atoi(svalue);
}
if (pdemoflags & DF_ENCORE)
pdemo->kartspeed |= DF_ENCORE;
/*// Temporary info until this is actually present in replays.
(void)extrainfo_p;
sprintf(pdemo->winnername, "transrights420");
pdemo->winnerskin = 1;
pdemo->winnercolor = SKINCOLOR_MOONSET;
pdemo->winnertime = 6666;*/
// Read standings!
count = 0;
@ -2835,6 +2830,7 @@ void G_DoPlayDemo(char *defdemoname)
demoflags = READUINT8(demo_p);
gametype = READUINT8(demo_p);
G_SetGametype(gametype);
numlaps = READUINT8(demo_p);
if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running.
G_SkipDemoExtraFiles(&demo_p);
@ -3258,6 +3254,7 @@ void G_AddGhost(char *defdemoname)
}
p++; // gametype
p++; // numlaps
G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts.
switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
@ -3475,7 +3472,7 @@ void G_UpdateStaffGhostName(lumpnum_t l)
}
p++; // Gametype
p++; // numlaps
G_SkipDemoExtraFiles(&p);
switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)

View file

@ -303,6 +303,7 @@ INT32 cheats; //for multiplayer cheat commands
// SRB2Kart
// Cvars that we don't want changed mid-game
UINT8 numlaps; // Removed from Cvar hell
UINT8 gamespeed; // Game's current speed (or difficulty, or cc, or etc); 0 for easy, 1 for normal, 2 for hard
boolean encoremode = false; // Encore Mode currently enabled?
boolean prevencoremode;
@ -2198,6 +2199,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT32 khudcardanimation;
INT16 totalring;
UINT8 laps;
UINT8 latestlap;
UINT16 skincolor;
INT32 skin;
UINT32 availabilities;
@ -2284,6 +2286,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
khudfault = 0;
nocontrol = 0;
laps = 0;
latestlap = 0;
totalring = 0;
roundscore = 0;
exiting = 0;
@ -2324,6 +2327,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
nocontrol = players[player].nocontrol;
laps = players[player].laps;
latestlap = players[player].latestlap;
totalring = players[player].totalring;
roundscore = players[player].roundscore;
@ -2380,6 +2385,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->karthud[khud_cardanimation] = khudcardanimation;
p->laps = laps;
p->latestlap = latestlap;
p->totalring = totalring;
p->bot = bot;

View file

@ -2421,7 +2421,7 @@ static void HU_DrawRankings(void)
if (circuitmap)
{
V_DrawCenteredString(64, 8, 0, "LAP COUNT");
V_DrawCenteredString(64, 16, hilicol, va("%d", cv_numlaps.value));
V_DrawCenteredString(64, 16, hilicol, va("%d", numlaps));
}
V_DrawCenteredString(256, 8, 0, "GAME SPEED");

View file

@ -1706,7 +1706,7 @@ static void K_DrawKartPositionNum(INT32 num)
{
localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3];
}
else if (stplyr->laps >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won
else if (stplyr->laps >= numlaps || stplyr->exiting) // Check for the final lap, or won
{
boolean useRedNums = K_IsPlayerLosing(stplyr);
@ -2337,7 +2337,7 @@ static void K_drawKartLapsAndRings(void)
V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_splitlapflag);
V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|V_SLIDEIN|splitflags, frameslash);
if (cv_numlaps.value >= 10)
if (numlaps >= 10)
{
UINT8 ln[2];
ln[0] = ((stplyr->laps / 10) % 10);
@ -2346,8 +2346,8 @@ static void K_drawKartLapsAndRings(void)
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]);
V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]);
ln[0] = ((abs(cv_numlaps.value) / 10) % 10);
ln[1] = (abs(cv_numlaps.value) % 10);
ln[0] = ((abs(numlaps) / 10) % 10);
ln[1] = (abs(numlaps) % 10);
V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]);
V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]);
@ -2355,7 +2355,7 @@ static void K_drawKartLapsAndRings(void)
else
{
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->laps) % 10]);
V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(cv_numlaps.value) % 10]);
V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(numlaps) % 10]);
}
// Rings
@ -2393,7 +2393,7 @@ static void K_drawKartLapsAndRings(void)
{
// Laps
V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_lapsticker);
V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", min(stplyr->laps, cv_numlaps.value), cv_numlaps.value));
V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", min(stplyr->laps, numlaps), numlaps));
// Rings
if (!uselives)
@ -4367,7 +4367,7 @@ static void K_drawLapStartAnim(void)
kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL);
}
if (stplyr->laps == (UINT8)(cv_numlaps.value))
if (stplyr->laps == (UINT8)(numlaps))
{
newval = (62 - (32 * max(0, progress - 76))) * FRACUNIT;
oldval = (62 - (32 * max(0, progressOld - 76))) * FRACUNIT;

View file

@ -8261,7 +8261,7 @@ void K_UpdateDistanceFromFinishLine(player_t *const player)
// distance calculation to work easily
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U)
{
const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps);
const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps);
player->distancetofinish += numfulllapsleft * K_GetCircuitLength();

View file

@ -1,5 +1,12 @@
/// \file k_pwrlv.c
/// \brief SRB2Kart Power Levels
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2022 by Sally Cochenour
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
// \brief Power Level system
#include "k_pwrlv.h"
#include "d_netcmd.h"
@ -19,9 +26,12 @@ UINT16 vspowerlevel[PWRLV_NUMTYPES];
// This is done so that clients will never be able to hack someone else's score over the server.
UINT16 clientpowerlevels[MAXPLAYERS][PWRLV_NUMTYPES];
// Which players spec-scummed, and their power level before scumming.
// On race finish, everyone is considered to have "won" against these people.
INT16 nospectategrief[MAXPLAYERS];
// Total calculated power add during the match,
// totalled at the end of the round.
INT16 clientPowerAdd[MAXPLAYERS];
// Players who spectated mid-race
UINT8 spectateGriefed = 0;
// Game setting scrambles based on server Power Level
SINT8 speedscramble = -1;
@ -52,8 +62,14 @@ void K_ClearClientPowerLevels(void)
{
UINT8 i, j;
for (i = 0; i < MAXPLAYERS; i++)
{
clientPowerAdd[i] = 0;
for (j = 0; j < PWRLV_NUMTYPES; j++)
{
clientpowerlevels[i][j] = 0;
}
}
}
// Adapted from this: http://wiki.tockdom.com/wiki/Player_Rating
@ -143,7 +159,206 @@ INT16 K_CalculatePowerLevelAvg(void)
return (INT16)(avg >> FRACBITS);
}
// -- K_UpdatePowerLevels could not be moved here due to usage of y_data, unfortunately. --
void K_UpdatePowerLevels(player_t *player)
{
const UINT8 playerNum = player - players;
const boolean exitBonus = ((player->laps > numlaps) || (player->pflags & PF_NOCONTEST));
SINT8 powerType = K_UsingPowerLevels();
INT16 yourScore = 0;
UINT16 yourPower = 0;
UINT8 i;
// Compare every single player against each other for power level increases.
// Every player you won against gives you more points, and vice versa.
// The amount of points won per match-up depends on the difference between the loser's power and the winner's power.
// See K_CalculatePowerLevelInc for more info.
if (powerType == PWRLV_DISABLED)
{
return;
}
if (!playeringame[playerNum] || player->spectator)
{
return;
}
CONS_Printf("Power Level Gain for player %d:\n", playerNum);
yourPower = clientpowerlevels[playerNum][powerType];
if (yourPower == 0)
{
// Guests don't record power level changes.
return;
}
CONS_Printf("Player %d's PWR.LV: %d\n", playerNum, yourPower);
if (gametyperules & GTR_CIRCUIT)
{
yourScore = MAXPLAYERS - player->position;
}
else
{
yourScore = player->score;
}
for (i = 0; i < MAXPLAYERS; i++)
{
UINT16 theirScore = 0;
INT16 theirPower = PWRLVRECORD_DEF;
INT16 diff = 0; // Loser PWR.LV - Winner PWR.LV
INT16 inc = 0; // Total pt increment
boolean won = false;
if (i == playerNum) // Same person
{
continue;
}
if (!playeringame[i] || players[i].spectator)
{
continue;
}
CONS_Printf("Player %d VS Player %d:\n", playerNum, i);
theirPower = clientpowerlevels[i][powerType];
if (theirPower == 0)
{
// No power level (splitscreen guests, bots)
continue;
}
CONS_Printf("Player %d's PWR.LV: %d\n", i, theirPower);
if (gametyperules & GTR_CIRCUIT)
{
theirScore = MAXPLAYERS - players[i].position;
}
else
{
theirScore = players[i].score;
}
if (yourScore == theirScore) // Tie -- neither get any points for this match up.
{
CONS_Printf("TIE, no change.\n");
continue;
}
won = (yourScore > theirScore);
if (won) // This player won!
{
diff = theirPower - yourPower;
inc += K_CalculatePowerLevelInc(diff);
CONS_Printf("WON! Diff is %d, total increment is %d\n", diff, inc);
}
else // This player lost...
{
diff = yourPower - theirPower;
inc -= K_CalculatePowerLevelInc(diff);
CONS_Printf("LOST... Diff is %d, total increment is %d\n", diff, inc);
}
if (exitBonus == false)
{
CONS_Printf("Reduced (%d / %d = %d) because it's not the end of the race\n", inc, numlaps, inc/numlaps);
inc /= numlaps;
}
if (inc == 0)
{
CONS_Printf("Total Result: No increment, no change.\n");
continue;
}
CONS_Printf("Total Result: Increment of %d.\n", inc);
clientPowerAdd[playerNum] += inc;
clientPowerAdd[i] -= inc;
}
}
INT16 K_FinalPowerIncrement(player_t *player, INT16 yourPower, INT16 increment)
{
INT16 inc = increment;
UINT8 numPlayers = 0;
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
{
continue;
}
numPlayers++;
}
if (inc <= 0)
{
if (player->position == 1)
{
// Won the whole match?
// Get at least one point.
inc = 1;
}
else
{
// You trade points in 1v1s,
// but is more lenient in bigger lobbies.
inc /= max(1, numPlayers-1);
}
}
if (yourPower + inc > PWRLVRECORD_MAX)
{
inc -= ((yourPower + inc) - PWRLVRECORD_MAX);
}
if (yourPower + inc < PWRLVRECORD_MIN)
{
inc -= ((yourPower + inc) - PWRLVRECORD_MIN);
}
return inc;
}
void K_CashInPowerLevels(void)
{
SINT8 powerType = K_UsingPowerLevels();
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (powerType != PWRLV_DISABLED)
{
INT16 inc = K_FinalPowerIncrement(&players[i], clientpowerlevels[i][powerType], clientPowerAdd[i]);
clientpowerlevels[i][powerType] += inc;
if (!demo.playback && i == consoleplayer)
{
vspowerlevel[powerType] = clientpowerlevels[i][powerType];
if (M_UpdateUnlockablesAndExtraEmblems())
{
S_StartSound(NULL, sfx_ncitem);
}
G_SaveGameData();
}
}
clientPowerAdd[i] = 0;
}
}
void K_SetPowerLevelScrambles(SINT8 powertype)
{
@ -227,7 +442,7 @@ void K_SetPowerLevelScrambles(SINT8 powertype)
{
case 5:
speed = KARTSPEED_HARD;
encore = true;
encore = P_RandomChance(FRACUNIT>>1);
break;
case 4:
speed = P_RandomChance((7<<FRACBITS)/10) ? KARTSPEED_HARD : KARTSPEED_NORMAL;
@ -238,7 +453,7 @@ void K_SetPowerLevelScrambles(SINT8 powertype)
encore = P_RandomChance(FRACUNIT>>2);
break;
case 2:
speed = 1;
speed = KARTSPEED_NORMAL;
encore = P_RandomChance(FRACUNIT>>3);
break;
case 1: default:
@ -254,7 +469,7 @@ void K_SetPowerLevelScrambles(SINT8 powertype)
CONS_Debug(DBG_GAMELOGIC, "Rolled speed: %d\n", speed);
CONS_Debug(DBG_GAMELOGIC, "Rolled encore: %s\n", (encore ? "true" : "false"));
if (cv_kartspeed.value == -1)
if (cv_kartspeed.value == KARTSPEED_AUTO)
speedscramble = speed;
else
speedscramble = -1;
@ -278,16 +493,13 @@ void K_PlayerForfeit(UINT8 playernum, boolean pointloss)
UINT16 theirpower = PWRLVRECORD_DEF;
INT16 diff = 0; // Loser PWR.LV - Winner PWR.LV
INT16 inc = 0;
UINT8 lapsLeft = 0;
UINT8 i;
// power level & spectating is netgames only
if (!netgame)
return;
// This server isn't using power levels anyway!
if (!cv_kartusepwrlv.value)
return;
// Hey, I just got here!
if (players[playernum].jointime <= 1)
return;
@ -296,14 +508,25 @@ void K_PlayerForfeit(UINT8 playernum, boolean pointloss)
if (gamestate != GS_LEVEL || leveltime <= starttime+(20*TICRATE))
return;
spectateGriefed++;
// This server isn't using power levels, so don't mess with them.
if (!cv_kartusepwrlv.value)
return;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
if ((playeringame[i] && !players[i].spectator)
|| (i == playernum))
{
p++;
}
}
if (p < 2) // no players
{
return;
}
if ((gametyperules & GTR_CIRCUIT))
powertype = PWRLV_RACE;
@ -317,14 +540,12 @@ void K_PlayerForfeit(UINT8 playernum, boolean pointloss)
return;
yourpower = clientpowerlevels[playernum][powertype];
// Set up the point compensation.
nospectategrief[playernum] = yourpower;
if (!pointloss) // This is set for stuff like sync-outs, which shouldn't be so harsh on the victim!
return;
lapsLeft = (numlaps - players[playernum].latestlap);
for (i = 0; i < MAXPLAYERS; i++)
{
INT16 thisInc = 0;
if (i == playernum)
continue;
@ -334,24 +555,40 @@ void K_PlayerForfeit(UINT8 playernum, boolean pointloss)
theirpower = clientpowerlevels[i][powertype];
diff = yourpower - theirpower;
inc -= K_CalculatePowerLevelInc(diff);
thisInc = K_CalculatePowerLevelInc(diff);
if (thisInc == 0)
{
continue;
}
thisInc *= lapsLeft;
clientPowerAdd[i] += thisInc;
inc -= thisInc;
}
if (inc == 0) // No change.
if (inc >= 0)
{
// No change. Also don't award points.
return;
}
if (yourpower + inc > PWRLVRECORD_MAX) // I mean... we're subtracting... but y'know how it is :V
if (yourpower + inc > PWRLVRECORD_MAX) // I mean... we're always subtracting... but y'know how it is :V
inc -= ((yourpower + inc) - PWRLVRECORD_MAX);
if (yourpower + inc < PWRLVRECORD_MIN)
inc -= ((yourpower + inc) - PWRLVRECORD_MIN);
clientpowerlevels[playernum][powertype] += inc;
if (!demo.playback && playernum == consoleplayer)
// pointloss isn't set for stuff like sync-outs,
// which shouldn't be so harsh on the victim!
if (!demo.playback && pointloss == true && playernum == consoleplayer)
{
vspowerlevel[powertype] = clientpowerlevels[playernum][powertype];
vspowerlevel[powertype] = clientpowerlevels[playernum][powertype] + inc;
if (M_UpdateUnlockablesAndExtraEmblems())
{
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData(); // save your punishment!
}
G_SaveGameData();
}
}

View file

@ -1,13 +1,27 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2022 by Sally Cochenour
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
// \brief Power Level system
#ifndef __K_PWRLV__
#define __K_PWRLV__
#include "doomtype.h"
#include "doomdef.h"
#include "d_player.h"
#define PWRLV_DISABLED -1
#define PWRLV_RACE 0
#define PWRLV_BATTLE 1
#define PWRLV_NUMTYPES 2
typedef enum
{
PWRLV_DISABLED = -1,
PWRLV_RACE = 0,
PWRLV_BATTLE = 1,
PWRLV_NUMTYPES = 2,
} pwrlv_type_t;
#define PWRLVRECORD_START 1000
#define PWRLVRECORD_DEF 5000
@ -19,13 +33,16 @@ extern SINT8 encorescramble;
extern UINT16 vspowerlevel[PWRLV_NUMTYPES];
extern UINT16 clientpowerlevels[MAXPLAYERS][PWRLV_NUMTYPES];
extern INT16 nospectategrief[MAXPLAYERS];
extern INT16 clientPowerAdd[MAXPLAYERS];
extern UINT8 spectateGriefed;
SINT8 K_UsingPowerLevels(void);
void K_ClearClientPowerLevels(void);
INT16 K_CalculatePowerLevelInc(INT16 diff);
INT16 K_CalculatePowerLevelAvg(void);
//void K_UpdatePowerLevels(void);
void K_UpdatePowerLevels(player_t *player);
INT16 K_FinalPowerIncrement(player_t *player, INT16 yourPower, INT16 increment);
void K_CashInPowerLevels(void);
void K_SetPowerLevelScrambles(SINT8 powertype);
void K_PlayerForfeit(UINT8 playernum, boolean nopointloss);

View file

@ -446,6 +446,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->realtime);
else if (fastcmp(field,"laps"))
lua_pushinteger(L, plr->laps);
else if (fastcmp(field,"latestlap"))
lua_pushinteger(L, plr->latestlap);
else if (fastcmp(field,"ctfteam"))
lua_pushinteger(L, plr->ctfteam);
else if (fastcmp(field,"checkskip"))
@ -786,6 +788,8 @@ static int player_set(lua_State *L)
plr->realtime = (tic_t)luaL_checkinteger(L, 3);
else if (fastcmp(field,"laps"))
plr->laps = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"latestlap"))
plr->latestlap = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"ctfteam"))
plr->ctfteam = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"checkskip"))

View file

@ -391,7 +391,7 @@ int LUA_PushGlobals(lua_State *L, const char *word)
lua_pushinteger(L, mapobjectscale);
return 1;
} else if (fastcmp(word,"numlaps")) {
lua_pushinteger(L, cv_numlaps.value);
lua_pushinteger(L, numlaps);
return 1;
} else if (fastcmp(word,"racecountdown")) {
lua_pushinteger(L, racecountdown);

View file

@ -1478,17 +1478,17 @@ static menuitem_t OP_GameOptionsMenu[] =
{IT_STRING | IT_SUBMENU, NULL, "Random Item Toggles...", {.submenu = &OP_MonitorToggleDef}, 10},
{IT_STRING | IT_CVAR, NULL, "Game Speed", {.cvar = &cv_kartspeed}, 30},
{IT_STRING | IT_CVAR, NULL, "Frantic Items", {.cvar = &cv_kartfrantic}, 40},
{IT_STRING | IT_CVAR, NULL, "Frantic Items", {.cvar = &cv_kartfrantic}, 40},
{IT_SECRET, NULL, "Encore Mode", {.cvar = &cv_kartencore}, 50},
{IT_STRING | IT_CVAR, NULL, "Number of Laps", {.cvar = &cv_basenumlaps}, 70},
{IT_STRING | IT_CVAR, NULL, "Number of Laps", {.cvar = &cv_numlaps}, 70},
{IT_STRING | IT_CVAR, NULL, "Exit Countdown Timer", {.cvar = &cv_countdowntime}, 80},
{IT_STRING | IT_CVAR, NULL, "Time Limit", {.cvar = &cv_timelimit}, 100},
{IT_STRING | IT_CVAR, NULL, "Starting Bumpers", {.cvar = &cv_kartbumpers}, 110},
{IT_STRING | IT_CVAR, NULL, "Karma Comeback", {.cvar = &cv_kartcomeback}, 120},
{IT_STRING | IT_CVAR, NULL, "Starting Bumpers", {.cvar = &cv_kartbumpers}, 110},
{IT_STRING | IT_CVAR, NULL, "Karma Comeback", {.cvar = &cv_kartcomeback}, 120},
{IT_STRING | IT_CVAR, NULL, "Track Power Levels", {.cvar = &cv_kartusepwrlv}, 140},
{IT_STRING | IT_CVAR, NULL, "Track Power Levels", {.cvar = &cv_kartusepwrlv}, 140},
};
static menuitem_t OP_ServerOptionsMenu[] =

View file

@ -792,16 +792,11 @@ boolean P_CheckRacers(void)
boolean eliminatelast = cv_karteliminatelast.value;
boolean everyonedone = true;
boolean eliminatebots = false;
boolean griefed = false;
const boolean griefed = (spectateGriefed > 0);
// Check if all the players in the race have finished. If so, end the level.
for (i = 0; i < MAXPLAYERS; i++)
{
if (nospectategrief[i] != -1) // prevent spectate griefing
{
griefed = true;
}
if (!playeringame[i] || players[i].spectator || players[i].lives <= 0) // Not playing
{
// Y'all aren't even playing
@ -922,7 +917,7 @@ boolean P_CheckRacers(void)
// We're still playing, but no one else is, so we need to reset spectator griefing.
if (numplayersingame <= 1)
{
memset(nospectategrief, -1, sizeof (nospectategrief));
spectateGriefed = 0;
}
// Turns out we're still having a good time & playing the game, we didn't have to do anything :)

View file

@ -28,6 +28,9 @@
//#define VIEWHEIGHTS "41"
// Maximum laps per map.
#define MAX_LAPS 99
// Maximum player score.
#define MAXSCORE 99999990 // 999999990

View file

@ -106,6 +106,7 @@ static void P_NetArchivePlayers(void)
{
WRITEINT16(save_p, clientpowerlevels[i][j]);
}
WRITEINT16(save_p, clientPowerAdd[i]);
if (!playeringame[i])
continue;
@ -167,6 +168,7 @@ static void P_NetArchivePlayers(void)
WRITEINT16(save_p, players[i].totalring);
WRITEUINT32(save_p, players[i].realtime);
WRITEUINT8(save_p, players[i].laps);
WRITEUINT8(save_p, players[i].latestlap);
WRITEINT32(save_p, players[i].starpostnum);
WRITEUINT8(save_p, players[i].ctfteam);
@ -404,6 +406,7 @@ static void P_NetUnArchivePlayers(void)
{
clientpowerlevels[i][j] = READINT16(save_p);
}
clientPowerAdd[i] = READINT16(save_p);
// Do NOT memset player struct to 0
// other areas may initialize data elsewhere
@ -466,6 +469,7 @@ static void P_NetUnArchivePlayers(void)
players[i].totalring = READINT16(save_p); // Total number of rings obtained for GP
players[i].realtime = READUINT32(save_p); // integer replacement for leveltime
players[i].laps = READUINT8(save_p); // Number of laps (optional)
players[i].latestlap = READUINT8(save_p);
players[i].starpostnum = READINT32(save_p);
players[i].ctfteam = READUINT8(save_p); // 1 == Red, 2 == Blue
@ -4495,6 +4499,7 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT8(save_p, battlecapsules);
WRITEUINT8(save_p, gamespeed);
WRITEUINT8(save_p, numlaps);
WRITEUINT8(save_p, franticitems);
WRITEUINT8(save_p, comeback);
@ -4515,8 +4520,7 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT32(save_p, indirectitemcooldown);
WRITEUINT32(save_p, mapreset);
for (i = 0; i < MAXPLAYERS; i++)
WRITEINT16(save_p, nospectategrief[i]);
WRITEUINT8(save_p, spectateGriefed);
WRITEUINT8(save_p, thwompsactive);
WRITEUINT8(save_p, lastLowestLap);
@ -4644,6 +4648,7 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
battlecapsules = (boolean)READUINT8(save_p);
gamespeed = READUINT8(save_p);
numlaps = READUINT8(save_p);
franticitems = (boolean)READUINT8(save_p);
comeback = (boolean)READUINT8(save_p);
@ -4664,8 +4669,7 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
indirectitemcooldown = READUINT32(save_p);
mapreset = READUINT32(save_p);
for (i = 0; i < MAXPLAYERS; i++)
nospectategrief[i] = READINT16(save_p);
spectateGriefed = READUINT8(save_p);
thwompsactive = (boolean)READUINT8(save_p);
lastLowestLap = READUINT8(save_p);

View file

@ -4063,25 +4063,37 @@ static void P_InitPlayers(void)
static void P_InitGametype(void)
{
spectateGriefed = 0;
K_CashInPowerLevels(); // Pushes power level changes even if intermission was skipped
P_InitPlayers();
if (modeattacking && !demo.playback)
P_LoadRecordGhosts();
if ((gametyperules & GTR_CIRCUIT) && server)
numlaps = 0;
if (gametyperules & GTR_CIRCUIT)
{
if ((netgame || multiplayer) && cv_basenumlaps.value
if ((netgame || multiplayer) && cv_numlaps.value
&& (!(mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
|| (mapheaderinfo[gamemap - 1]->numlaps > cv_basenumlaps.value)))
|| (mapheaderinfo[gamemap - 1]->numlaps > cv_numlaps.value)))
{
CV_StealthSetValue(&cv_numlaps, cv_basenumlaps.value);
numlaps = cv_numlaps.value;
}
else
{
CV_StealthSetValue(&cv_numlaps, mapheaderinfo[gamemap - 1]->numlaps);
numlaps = mapheaderinfo[gamemap - 1]->numlaps;
}
}
wantedcalcdelay = wantedfrequency*2;
indirectitemcooldown = 0;
mapreset = 0;
thwompsactive = false;
lastLowestLap = 0;
spbplace = -1;
// Start recording replay in multiplayer with a temp filename
//@TODO I'd like to fix dedis crashing when recording replays for the future too...
if (!demo.playback && multiplayer && !dedicated)
@ -4117,6 +4129,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
// Map header should always be in place at this point
INT32 i, ranspecialwipe = 0;
sector_t *ss;
levelloading = true;
// This is needed. Don't touch.
@ -4430,17 +4443,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
K_InitDirector();
}
wantedcalcdelay = wantedfrequency*2;
indirectitemcooldown = 0;
mapreset = 0;
for (i = 0; i < MAXPLAYERS; i++)
nospectategrief[i] = -1;
thwompsactive = false;
lastLowestLap = 0;
spbplace = -1;
// clear special respawning que
iquehead = iquetail = 0;

View file

@ -1917,14 +1917,6 @@ static void K_HandleLapIncrement(player_t *player)
player->karthud[khud_laphand] = 0; // No hands in FREE PLAY
player->karthud[khud_lapanimation] = 80;
// save best lap for record attack
if (player == &players[consoleplayer])
{
if (curlap < bestlap || bestlap == 0)
bestlap = curlap;
curlap = 0;
}
}
if (rainbowstartavailable == true)
@ -1938,18 +1930,18 @@ static void K_HandleLapIncrement(player_t *player)
rainbowstartavailable = false;
}
if (netgame && player->laps >= (UINT8)cv_numlaps.value)
if (netgame && player->laps >= numlaps)
CON_LogMessage(va(M_GetText("%s has finished the race.\n"), player_names[player-players]));
player->starpostnum = 0;
if (P_IsDisplayPlayer(player))
{
if (player->laps == (UINT8)(cv_numlaps.value)) // final lap
if (player->laps == numlaps) // final lap
S_StartSound(NULL, sfx_s3k68);
else if ((player->laps > 1) && (player->laps < (UINT8)(cv_numlaps.value))) // non-final lap
else if ((player->laps > 1) && (player->laps < numlaps)) // non-final lap
S_StartSound(NULL, sfx_s221);
else if (player->laps > (UINT8)(cv_numlaps.value))
else if (player->laps > numlaps)
{
// finished
S_StartSound(NULL, sfx_s3k6a);
@ -1958,7 +1950,7 @@ static void K_HandleLapIncrement(player_t *player)
}
else
{
if ((player->laps > (UINT8)(cv_numlaps.value)) && (player->position == 1))
if ((player->laps > numlaps) && (player->position == 1))
{
// opponent finished
S_StartSound(NULL, sfx_s253);
@ -1966,12 +1958,34 @@ static void K_HandleLapIncrement(player_t *player)
}
// finished race exit setup
if (player->laps > (unsigned)cv_numlaps.value)
if (player->laps > numlaps)
{
P_DoPlayerExit(player);
P_SetupSignExit(player);
}
if (player->laps > player->latestlap)
{
if (player->laps > 1)
{
// save best lap for record attack
if (modeattacking && player == &players[consoleplayer])
{
if (curlap < bestlap || bestlap == 0)
{
bestlap = curlap;
}
curlap = 0;
}
// Update power levels for this lap.
K_UpdatePowerLevels(player);
}
player->latestlap = player->laps;
}
thwompsactive = true; // Lap 2 effects
lowestLap = P_FindLowestLap();

View file

@ -873,7 +873,7 @@ void P_RestoreMusic(player_t *player)
#if 0
// Event - Final Lap
// Still works for GME, but disabled for consistency
if ((gametyperules & GTR_CIRCUIT) && player->laps >= (UINT8)(cv_numlaps.value))
if ((gametyperules & GTR_CIRCUIT) && player->laps >= numlaps)
S_SpeedMusic(1.2f);
#endif
if (mapmusresume && cv_resume.value)
@ -3851,6 +3851,7 @@ void P_DoTimeOver(player_t *player)
}
player->pflags |= PF_NOCONTEST;
K_UpdatePowerLevels(player);
if (G_GametypeUsesLives())
{

View file

@ -214,7 +214,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
{
INT32 i, j;
boolean completed[MAXPLAYERS];
INT32 numplayersingame = 0, numgriefers = 0;
INT32 numplayersingame = 0;
// Initialize variables
if (rankingsmode > 1)
@ -273,9 +273,6 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
{
data.val[i] = UINT32_MAX;
if (nospectategrief[i] != -1)
numgriefers++;
if (!playeringame[i] || players[i].spectator)
{
data.increase[i] = INT16_MIN;
@ -324,10 +321,10 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
{
if ((powertype == PWRLV_DISABLED)
&& !(players[i].pflags & PF_NOCONTEST)
&& (data.pos[data.numplayers] < (numplayersingame + numgriefers)))
&& (data.pos[data.numplayers] < (numplayersingame + spectateGriefed)))
{
// Online rank is handled further below in this file.
data.increase[i] = K_CalculateGPRankPoints(data.pos[data.numplayers], numplayersingame + numgriefers);
data.increase[i] = K_CalculateGPRankPoints(data.pos[data.numplayers], numplayersingame + spectateGriefed);
players[i].score += data.increase[i];
}
@ -890,157 +887,6 @@ void Y_Ticker(void)
}
}
static void K_UpdatePowerLevels(void)
{
INT32 i, j;
INT32 numplayersingame = 0, numgriefers = 0;
INT16 increment[MAXPLAYERS];
// Compare every single player against each other for power level increases.
// Every player you won against gives you more points, and vice versa.
// The amount of points won per match-up depends on the difference between the loser's power and the winner's power.
// See K_CalculatePowerLevelInc for more info.
for (i = 0; i < MAXPLAYERS; i++)
{
increment[i] = 0;
if (nospectategrief[i] != -1)
numgriefers++;
if (!playeringame[i] || players[i].spectator)
continue;
numplayersingame++;
}
for (i = 0; i < numplayersingame; i++)
{
UINT16 yourpower = PWRLVRECORD_DEF;
UINT16 theirpower = PWRLVRECORD_DEF;
INT16 diff = 0; // Loser PWR.LV - Winner PWR.LV
INT16 inc = 0; // Total pt increment
UINT8 ipnum = data.num[i];
UINT8 jpnum;
CONS_Debug(DBG_GAMELOGIC, "Power Level Gain for player %d:\n", ipnum);
if (clientpowerlevels[ipnum][powertype] == 0) // splitscreen guests don't record power level changes
continue;
yourpower = clientpowerlevels[ipnum][powertype];
CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", ipnum, yourpower);
for (j = 0; j < numplayersingame; j++)
{
boolean won = false;
jpnum = data.num[j];
if (i == j || ipnum == jpnum) // Same person
continue;
CONS_Debug(DBG_GAMELOGIC, "Player %d VS Player %d:\n", ipnum, jpnum);
if (data.val[i] == data.val[j]) // Tie -- neither get any points for this match up.
{
CONS_Debug(DBG_GAMELOGIC, "TIE, no change.\n");
continue;
}
if (clientpowerlevels[jpnum][powertype] == 0) // No power level (splitscreen guests, bots)
continue;
theirpower = clientpowerlevels[jpnum][powertype];
CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower);
if ((gametyperules & GTR_CIRCUIT))
{
if (data.val[i] < data.val[j])
won = true;
}
else
{
if (data.val[i] > data.val[j])
won = true;
}
if (won) // This player won!
{
diff = theirpower - yourpower;
inc += K_CalculatePowerLevelInc(diff);
CONS_Debug(DBG_GAMELOGIC, "WON! Diff is %d, total increment is %d\n", diff, inc);
}
else // This player lost...
{
diff = yourpower - theirpower;
inc -= K_CalculatePowerLevelInc(diff);
CONS_Debug(DBG_GAMELOGIC, "LOST... Diff is %d, total increment is %d\n", diff, inc);
}
}
if (numgriefers != 0) // Automatic win against quitters.
{
for (jpnum = 0; jpnum < MAXPLAYERS; jpnum++)
{
if (nospectategrief[jpnum] == -1) // Empty slot
continue;
if (ipnum == jpnum) // Same person
continue;
CONS_Debug(DBG_GAMELOGIC, "Player %d VS Player %d (griefer):\n", ipnum, jpnum);
if (nospectategrief[jpnum] == 0) // No power level (splitscreen guests, bots)
continue;
theirpower = nospectategrief[jpnum];
CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower);
diff = theirpower - yourpower;
inc += K_CalculatePowerLevelInc(diff);
CONS_Debug(DBG_GAMELOGIC, "AUTO-WON! Diff is %d, total increment is %d\n", diff, inc);
}
}
if (inc == 0)
{
data.increase[ipnum] = INT16_MIN;
CONS_Debug(DBG_GAMELOGIC, "Total Result: No increment, no change.\n");
continue;
}
if (yourpower + inc > PWRLVRECORD_MAX)
inc -= ((yourpower + inc) - PWRLVRECORD_MAX);
if (yourpower + inc < PWRLVRECORD_MIN)
inc -= ((yourpower + inc) - PWRLVRECORD_MIN);
CONS_Debug(DBG_GAMELOGIC, "Total Result: Increment of %d.\n", inc);
increment[ipnum] = inc;
}
CONS_Debug(DBG_GAMELOGIC, "Setting final power levels...\n");
for (i = 0; i < MAXPLAYERS; i++)
{
if (increment[i] == 0)
continue;
data.increase[i] = increment[i];
clientpowerlevels[i][powertype] += data.increase[i];
if (!demo.playback && i == consoleplayer)
{
CONS_Debug(DBG_GAMELOGIC, "Player %d is you! Saving...\n", i);
vspowerlevel[powertype] = clientpowerlevels[i][powertype];
if (M_UpdateUnlockablesAndExtraEmblems())
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData();
}
}
}
//
// Y_DetermineIntermissionType
//
@ -1121,7 +967,7 @@ void Y_StartIntermission(void)
//if (dedicated) return;
// This should always exist, but just in case...
if(!mapheaderinfo[prevmap])
if (!mapheaderinfo[prevmap])
P_AllocMapHeader(prevmap);
switch (intertype)
@ -1157,7 +1003,18 @@ void Y_StartIntermission(void)
if (powertype != PWRLV_DISABLED)
{
K_UpdatePowerLevels();
for (i = 0; i < nump; i++)
{
// Kind of a hack to do this here,
// but couldn't think of a better way.
data.increase[i] = K_FinalPowerIncrement(
&players[data.num[i]],
clientpowerlevels[data.num[i]][powertype],
clientPowerAdd[data.num[i]]
);
}
K_CashInPowerLevels();
}
//if (intertype == int_race || intertype == int_battle)