Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into big-large-map-markers

# Conflicts:
#	src/d_netcmd.c
This commit is contained in:
toaster 2022-09-17 13:57:26 +01:00
commit 1a284ec7c6
76 changed files with 5603 additions and 3055 deletions

View file

@ -65,6 +65,7 @@ r_skins.c
r_sky.c
r_splats.c
r_things.c
r_bbox.c
r_textures.c
r_patch.c
r_patchrotation.c

View file

@ -34,6 +34,7 @@
#include "k_menu.h"
#include "filesrch.h"
#include "m_misc.h"
#include "m_random.h"
#ifdef _WINDOWS
#include "win32/win_main.h"
@ -243,6 +244,81 @@ static void CONS_Bind_f(void)
bindtable[key] = Z_StrDup(COM_Argv(2));
}
static void CONS_Choose_f(void)
{
size_t na = COM_Argc();
if (na < 2)
{
CONS_Printf(M_GetText("choose <option1> [<option2>] [<option3>] [...]: Picks a command at random\n"));
return;
}
COM_BufAddText(COM_Argv(M_RandomKey(na - 1) + 1));
COM_BufAddText("\n");
}
static void CONS_ChooseWeighted_f(void)
{
size_t na = COM_Argc();
size_t i, cmd;
const char *commands[40];
INT32 weights[40];
INT32 totalWeight = 0;
INT32 roll;
if (na < 3)
{
CONS_Printf(M_GetText("chooseweighted <option1> <weight1> [<option2> <weight2>] [<option3> <weight3>] [...]: Picks a command with weighted randomization\n"));
return;
}
memset(weights, 0, sizeof(weights));
i = 1;
cmd = 0;
while (i < na)
{
commands[cmd] = COM_Argv(i);
i++;
if (i >= na)
{
break;
}
weights[cmd] = atoi(COM_Argv(i));
totalWeight += weights[cmd];
i++;
cmd++;
}
if (cmd == 0 || totalWeight <= 0)
{
return;
}
roll = M_RandomRange(1, totalWeight);
for (i = 0; i < cmd; i++)
{
if (roll <= weights[i])
{
if (commands[i] == NULL)
{
break;
}
COM_BufAddText(commands[i]);
COM_BufAddText("\n");
break;
}
roll -= weights[i];
}
}
//======================================================================
// CONSOLE SETUP
//======================================================================
@ -445,6 +521,8 @@ void CON_Init(void)
CV_RegisterVar(&cons_backpic);
CV_RegisterVar(&cons_backcolor);
COM_AddCommand("bind", CONS_Bind_f);
COM_AddCommand("choose", CONS_Choose_f);
COM_AddCommand("chooseweighted", CONS_ChooseWeighted_f);
}
else
{

View file

@ -122,7 +122,7 @@ SINT8 nodetoplayer4[MAXNETNODES]; // say the numplayer for this node if any (spl
UINT8 playerpernode[MAXNETNODES]; // used specialy for splitscreen
boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
tic_t servermaxping = 800; // server's max ping. Defaults to 800
tic_t servermaxping = 20; // server's max delay, in frames. Defaults to 20
static tic_t nettics[MAXNETNODES]; // what tic the client have received
static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet
static UINT8 nodewaiting[MAXNETNODES];
@ -189,6 +189,8 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c
consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL);
consvar_t cv_kicktime = CVAR_INIT ("kicktime", "10", CV_SAVE, CV_Unsigned, NULL);
static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
{
const size_t d = n / sizeof(ticcmd_t);
@ -890,6 +892,9 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
UINT8 *p;
size_t mirror_length;
const char *httpurl = cv_httpsource.string;
UINT8 prefgametype = (cv_kartgametypepreference.value == -1)
? gametype
: cv_kartgametypepreference.value;
netbuffer->packettype = PT_SERVERINFO;
netbuffer->u.serverinfo._255 = 255;
@ -914,7 +919,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
else
netbuffer->u.serverinfo.refusereason = 0;
strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[prefgametype],
sizeof netbuffer->u.serverinfo.gametypename);
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
@ -1348,7 +1353,7 @@ static void SendAskInfo(INT32 node)
if (node != 0 && node != BROADCASTADDR &&
cv_rendezvousserver.string[0])
{
I_NetRequestHolePunch();
I_NetRequestHolePunch(node);
}
asktime = I_GetTime();
@ -2028,7 +2033,10 @@ static void CL_ConnectToServer(void)
wipegamestate = GS_WAITINGPLAYERS;
ClearAdminPlayers();
Schedule_Clear();
Automate_Clear();
K_ClearClientPowerLevels();
pnumnodes = 1;
oldtic = 0;
#ifndef NONET
@ -2092,57 +2100,83 @@ static void CL_ConnectToServer(void)
}
#ifndef NONET
typedef struct banreason_s
{
char *reason;
struct banreason_s *prev; //-1
struct banreason_s *next; //+1
} banreason_t;
static banreason_t *reasontail = NULL; //last entry, use prev
static banreason_t *reasonhead = NULL; //1st entry, use next
static void Command_ShowBan(void) //Print out ban list
{
size_t i;
const char *address, *mask;
banreason_t *reasonlist = reasonhead;
const char *address, *mask, *reason, *username;
time_t unbanTime = NO_BAN_TIME;
const time_t curTime = time(NULL);
if (I_GetBanAddress)
CONS_Printf(M_GetText("Ban List:\n"));
else
return;
for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++)
{
unbanTime = NO_BAN_TIME;
if (I_GetUnbanTime)
unbanTime = I_GetUnbanTime(i);
if (unbanTime != NO_BAN_TIME && curTime >= unbanTime)
continue;
CONS_Printf("%s: ", sizeu1(i+1));
if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL)
CONS_Printf("%s - ", username);
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
CONS_Printf("%s: %s ", sizeu1(i+1), address);
CONS_Printf("%s", address);
else
CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask);
CONS_Printf("%s/%s", address, mask);
if (reasonlist && reasonlist->reason)
CONS_Printf("(%s)\n", reasonlist->reason);
else
CONS_Printf("\n");
if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
CONS_Printf(" - %s", reason);
if (reasonlist) reasonlist = reasonlist->next;
if (unbanTime != NO_BAN_TIME)
{
// these are fudged a little to match what a joiner sees
int minutes = ((unbanTime - curTime) + 30) / 60;
int hours = (minutes + 1) / 60;
int days = (hours + 1) / 24;
if (days)
CONS_Printf(" (%d day%s)", days, days > 1 ? "s" : "");
else if (hours)
CONS_Printf(" (%d hour%s)", hours, hours > 1 ? "s" : "");
else if (minutes)
CONS_Printf(" (%d minute%s)", minutes, minutes > 1 ? "s" : "");
else
CONS_Printf(" (<1 minute)");
}
CONS_Printf("\n");
}
if (i == 0 && !address)
CONS_Printf(M_GetText("(empty)\n"));
}
static boolean bansLoaded = false;
// If you're a community contributor looking to improve how bans are written, please
// offer your changes back to our Git repository. Kart Krew reserve the right to
// utilise format numbers in use by community builds for different layouts.
#define BANFORMAT 1
void D_SaveBan(void)
{
FILE *f;
size_t i;
banreason_t *reasonlist = reasonhead;
const char *address, *mask;
const char *username, *reason;
const time_t curTime = time(NULL);
time_t unbanTime = NO_BAN_TIME;
const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
if (!reasonhead)
if (bansLoaded != true)
{
remove(path);
// You didn't even get to ATTEMPT to load bans.txt.
// Don't immediately save nothing over it.
return;
}
@ -2154,78 +2188,79 @@ void D_SaveBan(void)
return;
}
for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
// Add header.
fprintf(f, "BANFORMAT %d\n", BANFORMAT);
for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++)
{
if (I_GetUnbanTime)
{
unbanTime = I_GetUnbanTime(i);
}
else
{
unbanTime = NO_BAN_TIME;
}
if (unbanTime != NO_BAN_TIME && curTime >= unbanTime)
{
// This one has served their sentence.
// We don't need to save them in the file anymore.
continue;
}
mask = NULL;
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
fprintf(f, "%s 0", address);
fprintf(f, "%s/0", address);
else
fprintf(f, "%s %s", address, mask);
fprintf(f, "%s/%s", address, mask);
if (reasonlist && reasonlist->reason)
fprintf(f, " %s\n", reasonlist->reason);
// TODO: it'd be nice to convert this to an actual date-time,
// so it'd be easier to edit outside of the game.
fprintf(f, " %ld", (long)unbanTime);
username = NULL;
if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL)
fprintf(f, " \"%s\"", username);
else
fprintf(f, " %s\n", "NA");
fprintf(f, " \"%s\"", "Direct IP ban");
if (reasonlist) reasonlist = reasonlist->next;
reason = NULL;
if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
fprintf(f, " \"%s\"\n", reason);
else
fprintf(f, " \"%s\"\n", "No reason given");
}
fclose(f);
}
static void Ban_Add(const char *reason)
{
banreason_t *reasonlist = malloc(sizeof(*reasonlist));
if (!reasonlist)
return;
if (!reason)
reason = "NA";
reasonlist->next = NULL;
reasonlist->reason = Z_StrDup(reason);
if ((reasonlist->prev = reasontail) == NULL)
reasonhead = reasonlist;
else
reasontail->next = reasonlist;
reasontail = reasonlist;
}
static void Ban_Clear(void)
{
banreason_t *temp;
I_ClearBans();
reasontail = NULL;
while (reasonhead)
{
temp = reasonhead->next;
Z_Free(reasonhead->reason);
free(reasonhead);
reasonhead = temp;
}
}
static void Command_ClearBans(void)
{
if (!I_ClearBans)
return;
Ban_Clear();
I_ClearBans();
D_SaveBan();
}
static void Ban_Load_File(boolean warning)
void D_LoadBan(boolean warning)
{
FILE *f;
size_t i;
const char *address, *mask;
size_t i, j;
char *address, *mask;
char *username, *reason;
time_t unbanTime = NO_BAN_TIME;
char buffer[MAX_WADPATH];
UINT8 banmode = 0;
boolean malformed = false;
if (!I_ClearBans)
return;
// We at least attempted loading bans.txt
bansLoaded = true;
f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
if (!f)
@ -2235,24 +2270,115 @@ static void Ban_Load_File(boolean warning)
return;
}
Ban_Clear();
I_ClearBans();
for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
for (i = 0; fgets(buffer, (int)sizeof(buffer), f); i++)
{
address = strtok(buffer, " \t\r\n");
address = strtok(buffer, " /\t\r\n");
mask = strtok(NULL, " \t\r\n");
I_SetBanAddress(address, mask);
if (i == 0 && !strncmp(address, "BANFORMAT", 9))
{
if (mask)
{
banmode = atoi(mask);
}
switch (banmode)
{
case BANFORMAT: // currently supported format
//case 0: -- permitted only when BANFORMAT string not present
break;
default:
{
fclose(f);
CONS_Alert(CONS_WARNING, "Could not load unknown ban.txt for ban list (BANFORMAT %s, expected %d)\n", mask, BANFORMAT);
return;
}
}
continue;
}
Ban_Add(strtok(NULL, "\r\n"));
if (I_SetBanAddress(address, mask) == false) // invalid IP input?
{
CONS_Alert(CONS_WARNING, "\"%s/%s\" is not a valid IP address, discarding...\n", address, mask);
continue;
}
// One-way legacy format conversion -- the game will crash otherwise
if (banmode == 0)
{
unbanTime = NO_BAN_TIME;
username = NULL; // not guaranteed to be accurate, but only sane substitute
reason = strtok(NULL, "\r\n");
if (reason && reason[0] == 'N' && reason[1] == 'A' && reason[2] == '\0')
{
reason = NULL;
}
}
else
{
reason = strtok(NULL, " \"\t\r\n");
if (reason)
{
unbanTime = atoi(reason);
reason = NULL;
}
else
{
unbanTime = NO_BAN_TIME;
malformed = true;
}
username = strtok(NULL, "\"\t\r\n"); // go until next "
if (!username)
{
malformed = true;
}
strtok(NULL, "\"\t\r\n"); // remove first "
reason = strtok(NULL, "\"\r\n"); // go until next "
if (!reason)
{
malformed = true;
}
}
// Enforce MAX_REASONLENGTH.
if (reason)
{
j = 0;
while (reason[j] != '\0')
{
if ((j++) < MAX_REASONLENGTH)
continue;
reason[j] = '\0';
break;
}
}
if (I_SetUnbanTime)
I_SetUnbanTime(unbanTime);
if (I_SetBanUsername)
I_SetBanUsername(username);
if (I_SetBanReason)
I_SetBanReason(reason);
}
if (malformed)
{
CONS_Alert(CONS_WARNING, "One or more lines of ban.txt are malformed. The game can correct for this, but some data may be lost.\n");
}
fclose(f);
}
#undef BANFORMAT
static void Command_ReloadBan(void) //recheck ban.txt
{
Ban_Load_File(true);
D_LoadBan(true);
}
static void Command_connect(void)
@ -2397,6 +2523,8 @@ void CL_ClearPlayer(INT32 playernum)
splitscreen_original_party_size[playernum] = 0;
memset(&players[playernum], 0, sizeof (player_t));
RemoveAdminPlayer(playernum); // don't stay admin after you're gone
}
//
@ -2426,14 +2554,14 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
K_CalculateBattleWanted();
LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting
LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting
// don't look through someone's view who isn't there
if (playernum == displayplayers[0] && !demo.playback)
{
// Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true);
LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true);
displayplayers[0] = consoleplayer;
}
@ -2454,11 +2582,6 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
player_name_changes[playernum] = 0;
if (IsPlayerAdmin(playernum))
{
RemoveAdminPlayer(playernum); // don't stay admin after you're gone
}
LUA_InvalidatePlayer(&players[playernum]);
K_CheckBumpers();
@ -2602,7 +2725,7 @@ static void Command_Ban(void)
{
if (COM_Argc() < 2)
{
CONS_Printf(M_GetText("Ban <playername/playernum> <reason>: ban and kick a player\n"));
CONS_Printf(M_GetText("ban <playername/playernum> <reason>: ban and kick a player\n"));
return;
}
@ -2617,83 +2740,95 @@ static void Command_Ban(void)
UINT8 buf[3 + MAX_REASONLENGTH];
UINT8 *p = buf;
const SINT8 pn = nametonum(COM_Argv(1));
const INT32 node = playernode[(INT32)pn];
if (pn == -1 || pn == 0)
return;
WRITEUINT8(p, pn);
if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now
if (COM_Argc() == 2)
{
CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
WRITEUINT8(p, KICK_MSG_GO_AWAY);
WRITEUINT8(p, KICK_MSG_BANNED);
SendNetXCmd(XD_KICK, &buf, 2);
}
else
{
if (server) // only the server is allowed to do this right now
size_t i, j = COM_Argc();
char message[MAX_REASONLENGTH];
//Steal from the motd code so you don't have to put the reason in quotes.
strlcpy(message, COM_Argv(2), sizeof message);
for (i = 3; i < j; i++)
{
Ban_Add(COM_Argv(2));
D_SaveBan(); // save the ban list
strlcat(message, " ", sizeof message);
strlcat(message, COM_Argv(i), sizeof message);
}
if (COM_Argc() == 2)
{
WRITEUINT8(p, KICK_MSG_BANNED);
SendNetXCmd(XD_KICK, &buf, 2);
}
else
{
size_t i, j = COM_Argc();
char message[MAX_REASONLENGTH];
//Steal from the motd code so you don't have to put the reason in quotes.
strlcpy(message, COM_Argv(2), sizeof message);
for (i = 3; i < j; i++)
{
strlcat(message, " ", sizeof message);
strlcat(message, COM_Argv(i), sizeof message);
}
WRITEUINT8(p, KICK_MSG_CUSTOM_BAN);
WRITESTRINGN(p, message, MAX_REASONLENGTH);
SendNetXCmd(XD_KICK, &buf, p - buf);
}
WRITEUINT8(p, KICK_MSG_CUSTOM_BAN);
WRITESTRINGN(p, message, MAX_REASONLENGTH);
SendNetXCmd(XD_KICK, &buf, p - buf);
}
}
else
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
}
static void Command_BanIP(void)
{
if (COM_Argc() < 2)
size_t ac = COM_Argc();
if (ac < 2)
{
CONS_Printf(M_GetText("banip <ip> <reason>: ban an ip address\n"));
CONS_Printf(M_GetText("banip <ip> [<reason>]: ban an ip address\n"));
return;
}
if (server) // Only the server can use this, otherwise does nothing.
{
const char *address = (COM_Argv(1));
const char *reason;
char *addressInput = Z_StrDup(COM_Argv(1));
if (COM_Argc() == 2)
reason = NULL;
else
const char *address = NULL;
const char *mask = NULL;
const char *reason = NULL;
address = strtok(addressInput, "/");
mask = strtok(NULL, "");
if (ac > 2)
{
reason = COM_Argv(2);
}
if (I_SetBanAddress && I_SetBanAddress(address, NULL))
if (I_SetBanAddress && I_SetBanAddress(address, mask))
{
if (reason)
CONS_Printf("Banned IP address %s for: %s\n", address, reason);
{
CONS_Printf(
"Banned IP address %s%s for: %s\n",
address,
(mask && (strlen(mask) > 0)) ? va("/%s", mask) : "",
reason
);
}
else
CONS_Printf("Banned IP address %s\n", address);
{
CONS_Printf(
"Banned IP address %s%s\n",
address,
(mask && (strlen(mask) > 0)) ? va("/%s", mask) : ""
);
}
if (I_SetUnbanTime)
I_SetUnbanTime(NO_BAN_TIME);
if (I_SetBanUsername)
I_SetBanUsername(NULL);
if (I_SetBanReason)
I_SetBanReason(reason);
Ban_Add(reason);
D_SaveBan();
}
else
@ -2771,6 +2906,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
char buf[3 + MAX_REASONLENGTH];
char *reason = buf;
kickreason_t kickreason = KR_KICK;
UINT32 banMinutes = 0;
pnum = READUINT8(*p);
msg = READUINT8(*p);
@ -2829,18 +2965,49 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
msg = KICK_MSG_CON_FAIL;
}
if (msg == KICK_MSG_CUSTOM_BAN || msg == KICK_MSG_CUSTOM_KICK)
{
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
}
//CONS_Printf("\x82%s ", player_names[pnum]);
// If a verified admin banned someone, the server needs to know about it.
// If the playernum isn't zero (the server) then the server needs to record the ban.
if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN))
// Save bans here. Used to be split between here and the actual command, depending on
// whenever the server did it or a remote admin did it, but it's simply more convenient
// to keep it all in one place.
if (server)
{
if (I_Ban && !I_Ban(playernode[(INT32)pnum]))
CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
#ifndef NONET
else
Ban_Add(reason);
#endif
if (msg == KICK_MSG_GO_AWAY || msg == KICK_MSG_CUSTOM_KICK)
{
// Kick as a temporary ban.
banMinutes = cv_kicktime.value;
}
if (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN || banMinutes)
{
if (I_Ban && !I_Ban(playernode[(INT32)pnum]))
{
CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n"));
}
else
{
if (I_SetBanUsername)
I_SetBanUsername(player_names[pnum]);
if (I_SetBanReason)
I_SetBanReason(reason);
if (I_SetUnbanTime)
{
if (banMinutes)
I_SetUnbanTime(time(NULL) + (banMinutes * 60));
else
I_SetUnbanTime(NO_BAN_TIME);
}
D_SaveBan();
}
}
}
if (msg == KICK_MSG_PLAYER_QUIT)
@ -2855,7 +3022,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
kickreason = KR_KICK;
break;
case KICK_MSG_PING_HIGH:
HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false);
HU_AddChatText(va("\x82*%s left the game (Broke delay limit)", player_names[pnum]), false);
kickreason = KR_PINGLIMIT;
break;
case KICK_MSG_CON_FAIL:
@ -2909,12 +3076,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
kickreason = KR_BAN;
break;
case KICK_MSG_CUSTOM_KICK:
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false);
kickreason = KR_KICK;
break;
case KICK_MSG_CUSTOM_BAN:
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false);
kickreason = KR_BAN;
break;
@ -2937,23 +3102,25 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
if (playernode[pnum] == playernode[consoleplayer])
{
LUAh_GameQuit(false);
#ifdef DUMPCONSISTENCY
if (msg == KICK_MSG_CON_FAIL) SV_SavedGame();
#endif
LUA_HookBool(false, HOOK(GameQuit)); //Lua hooks handled differently now
D_QuitNetGame();
CL_Reset();
D_StartTitle();
if (msg == KICK_MSG_CON_FAIL)
M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING);
else if (msg == KICK_MSG_PING_HIGH)
M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING);
M_StartMessage(M_GetText("Server closed connection\n(Broke delay limit)\nPress ESC\n"), NULL, MM_NOTHING);
else if (msg == KICK_MSG_BANNED)
M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
else if (msg == KICK_MSG_CUSTOM_KICK)
M_StartMessage(va(M_GetText("You have been kicked\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING);
M_StartMessage(va(M_GetText("You have been kicked\n(%s)\n\nPress ESC\n"), reason), NULL, MM_NOTHING);
else if (msg == KICK_MSG_CUSTOM_BAN)
M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING);
M_StartMessage(va(M_GetText("You have been banned\n(%s)\n\nPress ESC\n"), reason), NULL, MM_NOTHING);
else
M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
}
@ -3109,17 +3276,17 @@ consvar_t cv_discordinvites = CVAR_INIT ("discordinvites", "Everyone", CV_SAVE|C
static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}};
consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL);
consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "2", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL);
consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
// max file size to send to a player (in kilobytes)
static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}};
consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL);
consvar_t cv_maxsend = CVAR_INIT ("maxsend", "51200", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL);
consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
// Speed of file downloading (in packets per tic)
static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {32, "MAX"}, {0, NULL}};
consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL);
static CV_PossibleValue_t downloadspeed_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}};
consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "32", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL);
static void Got_AddPlayer(UINT8 **p, INT32 playernum);
static void Got_RemovePlayer(UINT8 **p, INT32 playernum);
@ -3181,7 +3348,7 @@ void D_ClientServerInit(void)
#ifdef DUMPCONSISTENCY
CV_RegisterVar(&cv_dumpconsistency);
#endif
Ban_Load_File(false);
D_LoadBan(false);
#endif
gametic = 0;
@ -3211,6 +3378,9 @@ static void ResetNode(INT32 node)
sendingsavegame[node] = false;
resendingsavegame[node] = false;
savegameresendcooldown[node] = 0;
bannednode[node].banid = SIZE_MAX;
bannednode[node].timeleft = NO_BAN_TIME;
}
void SV_ResetServer(void)
@ -3233,14 +3403,18 @@ void SV_ResetServer(void)
for (i = 0; i < MAXPLAYERS; i++)
{
LUA_InvalidatePlayer(&players[i]);
playeringame[i] = false;
playernode[i] = UINT8_MAX;
sprintf(player_names[i], "Player %c", 'A' + i);
adminplayers[i] = -1; // Populate the entire adminplayers array with -1.
K_ClearClientPowerLevels();
splitscreen_invitations[i] = -1;
}
memset(playeringame, false, sizeof playeringame);
memset(playernode, UINT8_MAX, sizeof playernode);
ClearAdminPlayers();
Schedule_Clear();
Automate_Clear();
K_ClearClientPowerLevels();
memset(splitscreen_invitations, -1, sizeof splitscreen_invitations);
memset(splitscreen_partied, 0, sizeof splitscreen_partied);
memset(player_name_changes, 0, sizeof player_name_changes);
@ -3325,6 +3499,8 @@ void D_QuitNetGame(void)
D_CloseConnection();
ClearAdminPlayers();
Schedule_Clear();
Automate_Clear();
K_ClearClientPowerLevels();
DEBFILE("===========================================================================\n"
@ -3455,7 +3631,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
if (server && multiplayer && motd[0] != '\0')
COM_BufAddText(va("sayto %d %s\n", newplayernum, motd));
LUAh_PlayerJoin(newplayernum);
LUA_HookInt(newplayernum, HOOK(PlayerJoin));
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
@ -3537,7 +3713,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false);
}
LUAh_PlayerJoin(newplayernum);
LUA_HookInt(newplayernum, HOOK(PlayerJoin));
}
static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *name2, const char *name3, const char *name4)
@ -3788,32 +3964,87 @@ static void HandleConnect(SINT8 node)
// It's too much effort to legimately fix right now. Just prevent it from reaching that state.
UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value);
if (bannednode && bannednode[node])
SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server."));
if (bannednode && bannednode[node].banid != SIZE_MAX)
{
const char *reason = NULL;
// Get the reason...
if (!I_GetBanReason || (reason = I_GetBanReason(bannednode[node].banid)) == NULL)
reason = "No reason given";
if (bannednode[node].timeleft != NO_BAN_TIME)
{
// these are fudged a little to allow it to sink in for impatient rejoiners
int minutes = (bannednode[node].timeleft + 30) / 60;
int hours = (minutes + 1) / 60;
int days = (hours + 1) / 24;
if (days)
{
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d day%s)", reason, days, days > 1 ? "s" : ""));
}
else if (hours)
{
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d hour%s)", reason, hours, hours > 1 ? "s" : ""));
}
else if (minutes)
{
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d minute%s)", reason, minutes, minutes > 1 ? "s" : ""));
}
else
{
SV_SendRefuse(node, va("K|%s\n(Time remaining: <1 minute)", reason));
}
}
else
{
SV_SendRefuse(node, va("B|%s", reason));
}
}
else if (netbuffer->u.clientcfg._255 != 255 ||
netbuffer->u.clientcfg.packetversion != PACKETVERSION)
{
SV_SendRefuse(node, "Incompatible packet formats.");
}
else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION,
sizeof netbuffer->u.clientcfg.application))
{
SV_SendRefuse(node, "Different Ring Racers modifications\nare not compatible.");
}
else if (netbuffer->u.clientcfg.version != VERSION
|| netbuffer->u.clientcfg.subversion != SUBVERSION)
{
SV_SendRefuse(node, va(M_GetText("Different Ring Racers versions cannot\nplay a netgame!\n(server version %d.%d)"), VERSION, SUBVERSION));
}
else if (!cv_allownewplayer.value && node)
{
SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment."));
}
else if (D_NumPlayers() >= maxplayers)
{
SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), maxplayers));
}
else if (netgame && netbuffer->u.clientcfg.localplayers > MAXSPLITSCREENPLAYERS) // Hacked client?
{
SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
}
else if (netgame && D_NumPlayers() + netbuffer->u.clientcfg.localplayers > maxplayers)
{
SV_SendRefuse(node, va(M_GetText("Number of local players\nwould exceed maximum: %d"), maxplayers));
}
else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join?
{
SV_SendRefuse(node, M_GetText("No players from\nthis node."));
}
else if (luafiletransfers)
{
SV_SendRefuse(node, M_GetText("The server is broadcasting a file\nrequested by a Lua script.\nPlease wait a bit and then\ntry rejoining."));
}
else if (netgame && joindelay > 2 * (tic_t)cv_joindelay.value * TICRATE)
{
SV_SendRefuse(node, va(M_GetText("Too many people are connecting.\nPlease wait %d seconds and then\ntry rejoining."),
(joindelay - 2 * cv_joindelay.value * TICRATE) / TICRATE));
}
else
{
#ifndef NONET
@ -3883,7 +4114,7 @@ static void HandleConnect(SINT8 node)
static void HandleShutdown(SINT8 node)
{
(void)node;
LUAh_GameQuit(false);
LUA_HookBool(false, HOOK(GameQuit));
D_QuitNetGame();
CL_Reset();
D_StartTitle();
@ -3898,7 +4129,7 @@ static void HandleShutdown(SINT8 node)
static void HandleTimeout(SINT8 node)
{
(void)node;
LUAh_GameQuit(false);
LUA_HookBool(false, HOOK(GameQuit));
D_QuitNetGame();
CL_Reset();
D_StartTitle();
@ -4087,13 +4318,22 @@ static void HandlePacketFromAwayNode(SINT8 node)
break;
}
M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
reason), NULL, MM_NOTHING);
D_QuitNetGame();
CL_Reset();
D_StartTitle();
if (reason[1] == '|')
{
M_StartMessage(va("You have been %sfrom the server\n\nReason:\n%s",
(reason[0] == 'B') ? "banned\n" : "temporarily\nkicked ",
reason+2), NULL, MM_NOTHING);
}
else
{
M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
reason), NULL, MM_NOTHING);
}
free(reason);
// Will be reset by caller. Signals refusal.
@ -4704,7 +4944,10 @@ static void GetPackets(void)
if (netbuffer->packettype == PT_CLIENTJOIN && server)
{
HandleConnect(node);
if (!levelloading) // Otherwise just ignore
{
HandleConnect(node);
}
continue;
}
if (node == servernode && client && cl_mode != CL_SEARCHING)
@ -5291,7 +5534,18 @@ boolean TryRunTics(tic_t realtics)
ps_tictime = I_GetPreciseTime();
G_Ticker((gametic % NEWTICRATERATIO) == 0);
if (Playing() && netgame && (gametic % TICRATE == 0))
{
Schedule_Run();
if (cv_livestudioaudience.value)
{
LiveStudioAudience();
}
}
ExtraDataTicker();
gametic++;
consistancy[gametic%BACKUPTICS] = Consistancy();
@ -5421,7 +5675,7 @@ static void UpdatePingTable(void)
if (! server_lagless && playernode[i] > 0 && !players[i].spectator)
{
lag = GetLag(playernode[i]);
realpingtable[i] += (INT32)(lag * (1000.00f/TICRATE));
realpingtable[i] += lag;
if (! fastest || lag < fastest)
fastest = lag;
@ -5429,7 +5683,7 @@ static void UpdatePingTable(void)
else
{
// TicsToMilliseconds can't handle pings over 1000ms lol
realpingtable[i] += (INT32)(GetLag(playernode[i]) * (1000.00f/TICRATE));
realpingtable[i] += GetLag(playernode[i]);
}
}
}
@ -5447,7 +5701,7 @@ static void UpdatePingTable(void)
else
lag = GetLag(0);
lag = ( realpingtable[0] + G_TicsToMilliseconds(lag) );
lag = ( realpingtable[0] + lag );
switch (playerpernode[0])
{

View file

@ -394,6 +394,7 @@ extern INT32 mapchangepending;
extern doomdata_t *netbuffer;
extern consvar_t cv_stunserver;
extern consvar_t cv_httpsource;
extern consvar_t cv_kicktime;
extern consvar_t cv_showjoinaddress;
extern consvar_t cv_playbackspeed;

View file

@ -75,7 +75,7 @@ boolean (*I_NetCanGet)(void) = NULL;
void (*I_NetCloseSocket)(void) = NULL;
void (*I_NetFreeNodenum)(INT32 nodenum) = NULL;
SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL;
void (*I_NetRequestHolePunch)(void) = NULL;
void (*I_NetRequestHolePunch)(INT32 node) = NULL;
void (*I_NetRegisterHolePunch)(void) = NULL;
boolean (*I_NetOpenSocket)(void) = NULL;
boolean (*I_Ban) (INT32 node) = NULL;
@ -83,8 +83,14 @@ void (*I_ClearBans)(void) = NULL;
const char *(*I_GetNodeAddress) (INT32 node) = NULL;
const char *(*I_GetBanAddress) (size_t ban) = NULL;
const char *(*I_GetBanMask) (size_t ban) = NULL;
const char *(*I_GetBanUsername) (size_t ban) = NULL;
const char *(*I_GetBanReason) (size_t ban) = NULL;
time_t (*I_GetUnbanTime) (size_t ban) = NULL;
boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL;
boolean *bannednode = NULL;
boolean (*I_SetBanUsername) (const char *username) = NULL;
boolean (*I_SetBanReason) (const char *reason) = NULL;
boolean (*I_SetUnbanTime) (time_t timestamp) = NULL;
bannednode_t *bannednode = NULL;
// network stats
@ -1389,6 +1395,7 @@ struct pingcell
{
INT32 num;
INT32 ms;
INT32 f;
};
static int pingcellcmp(const void *va, const void *vb)
@ -1412,6 +1419,7 @@ void Command_Ping_f(void)
INT32 pingc;
int name_width = 0;
int f_width = 0;
int ms_width = 0;
int n;
@ -1419,21 +1427,35 @@ void Command_Ping_f(void)
pingc = 0;
for (i = 1; i < MAXPLAYERS; ++i)
if (playeringame[i])
{
n = strlen(player_names[i]);
if (n > name_width)
name_width = n;
if (playeringame[i])
{
INT32 ms;
n = playerpingtable[i];
if (n > ms_width)
ms_width = n;
n = strlen(player_names[i]);
if (n > name_width)
name_width = n;
pingv[pingc].num = i;
pingv[pingc].ms = playerpingtable[i];
pingc++;
n = playerpingtable[i];
if (n > f_width)
f_width = n;
ms = (INT32)(playerpingtable[i] * (1000.00f / TICRATE));
n = ms;
if (n > ms_width)
ms_width = n;
pingv[pingc].num = i;
pingv[pingc].f = playerpingtable[i];
pingv[pingc].ms = ms;
pingc++;
}
}
if (f_width < 10) f_width = 1;
else if (f_width < 100) f_width = 2;
else f_width = 3;
if (ms_width < 10) ms_width = 1;
else if (ms_width < 100) ms_width = 2;
else ms_width = 3;
@ -1442,15 +1464,16 @@ void Command_Ping_f(void)
for (i = 0; i < pingc; ++i)
{
CONS_Printf("%02d : %-*s %*d ms\n",
CONS_Printf("%02d : %-*s %*d frames (%*d ms)\n",
pingv[i].num,
name_width, player_names[pingv[i].num],
f_width, pingv[i].f,
ms_width, pingv[i].ms);
}
if (!server && playeringame[consoleplayer])
{
CONS_Printf("\nYour ping is %d ms\n", playerpingtable[consoleplayer]);
CONS_Printf("\nYour ping is %d frames (%d ms)\n", playerpingtable[consoleplayer], (INT32)(playerpingtable[i] * (1000.00f / TICRATE)));
}
}

View file

@ -55,6 +55,7 @@ boolean HGetPacket(void);
void D_SetDoomcom(void);
#ifndef NONET
void D_SaveBan(void);
void D_LoadBan(boolean warning);
#endif
boolean D_CheckNetGame(void);
void D_CloseConnection(void);

View file

@ -99,6 +99,9 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum);
static void Got_Teamchange(UINT8 **cp, INT32 playernum);
static void Got_Clearscores(UINT8 **cp, INT32 playernum);
static void Got_DiscordInfo(UINT8 **cp, INT32 playernum);
static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum);
static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum);
static void Got_Automatecmd(UINT8 **cp, INT32 playernum);
static void PointLimit_OnChange(void);
static void TimeLimit_OnChange(void);
@ -147,6 +150,9 @@ static void KartEncore_OnChange(void);
static void KartComeback_OnChange(void);
static void KartEliminateLast_OnChange(void);
static void Schedule_OnChange(void);
static void LiveStudioAudience_OnChange(void);
#ifdef NETGAME_DEVMODE
static void Fishcake_OnChange(void);
#endif
@ -157,6 +163,8 @@ static void Command_Stopdemo_f(void);
static void Command_StartMovie_f(void);
static void Command_StopMovie_f(void);
static void Command_Map_f(void);
static void Command_RandomMap(void);
static void Command_RestartLevel(void);
static void Command_ResetCamera_f(void);
static void Command_View_f (void);
@ -220,6 +228,12 @@ static void Command_Archivetest_f(void);
static void Command_KartGiveItem_f(void);
static void Command_Schedule_Add(void);
static void Command_Schedule_Clear(void);
static void Command_Schedule_List(void);
static void Command_Automate_Set(void);
// =========================================================================
// CLIENT VARIABLES
// =========================================================================
@ -406,6 +420,8 @@ static CV_PossibleValue_t kartencore_cons_t[] = {{-1, "Auto"}, {0, "Off"}, {1, "
consvar_t cv_kartencore = CVAR_INIT ("kartencore", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartencore_cons_t, KartEncore_OnChange);
static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}};
consvar_t cv_kartvoterulechanges = CVAR_INIT ("kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL);
static CV_PossibleValue_t kartgametypepreference_cons_t[] = {{-1, "None"}, {GT_RACE, "Race"}, {GT_BATTLE, "Battle"}, {0, NULL}};
consvar_t cv_kartgametypepreference = CVAR_INIT ("kartgametypepreference", "None", CV_NETVAR, kartgametypepreference_cons_t, NULL);
static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}};
consvar_t cv_kartspeedometer = CVAR_INIT ("kartdisplayspeed", "Percentage", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display
static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}};
@ -503,20 +519,23 @@ consvar_t cv_allowexitlevel = CVAR_INIT ("allowexitlevel", "No", CV_NETVAR, CV_Y
consvar_t cv_netstat = CVAR_INIT ("netstat", "Off", 0, CV_OnOff, NULL); // show bandwidth statistics
static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
consvar_t cv_nettimeout = CVAR_INIT ("nettimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange);
consvar_t cv_nettimeout = CVAR_INIT ("nettimeout", "210", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange);
//static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
consvar_t cv_jointimeout = CVAR_INIT ("jointimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, JoinTimeout_OnChange);
consvar_t cv_maxping = CVAR_INIT ("maxping", "800", CV_SAVE, CV_Unsigned, NULL);
consvar_t cv_jointimeout = CVAR_INIT ("jointimeout", "210", CV_CALL|CV_SAVE, nettimeout_cons_t, JoinTimeout_OnChange);
consvar_t cv_maxping = CVAR_INIT ("maxdelay", "20", CV_SAVE, CV_Unsigned, NULL);
consvar_t cv_lagless = CVAR_INIT ("lagless", "Off", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, Lagless_OnChange);
static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}};
consvar_t cv_pingtimeout = CVAR_INIT ("pingtimeout", "10", CV_SAVE|CV_NETVAR, pingtimeout_cons_t, NULL);
consvar_t cv_pingtimeout = CVAR_INIT ("maxdelaytimeout", "10", CV_SAVE|CV_NETVAR, pingtimeout_cons_t, NULL);
// show your ping on the HUD next to framerate. Defaults to warning only (shows up if your ping is > maxping)
static CV_PossibleValue_t showping_cons_t[] = {{0, "Off"}, {1, "Always"}, {2, "Warning"}, {0, NULL}};
consvar_t cv_showping = CVAR_INIT ("showping", "Always", CV_SAVE, showping_cons_t, NULL);
static CV_PossibleValue_t pingmeasurement_cons_t[] = {{0, "Frames"}, {1, "Milliseconds"}, {0, NULL}};
consvar_t cv_pingmeasurement = CVAR_INIT ("pingmeasurement", "Frames", CV_SAVE, pingmeasurement_cons_t, NULL);
consvar_t cv_showviewpointtext = CVAR_INIT ("showviewpointtext", "On", CV_SAVE, CV_OnOff, NULL);
// Intermission time Tails 04-19-2002
@ -545,6 +564,16 @@ consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", 0, perfstats_cons_t, NUL
consvar_t cv_director = CVAR_INIT ("director", "Off", 0, CV_OnOff, NULL);
consvar_t cv_schedule = CVAR_INIT ("schedule", "On", CV_NETVAR|CV_CALL, CV_OnOff, Schedule_OnChange);
consvar_t cv_automate = CVAR_INIT ("automate", "On", CV_NETVAR, CV_OnOff, NULL);
#ifdef DEVELOP
consvar_t cv_livestudioaudience = CVAR_INIT ("livestudioaudience", "On", CV_NETVAR|CV_CALL, CV_OnOff, LiveStudioAudience_OnChange);
#else
consvar_t cv_livestudioaudience = CVAR_INIT ("livestudioaudience", "Off", CV_NETVAR|CV_CALL, CV_OnOff, LiveStudioAudience_OnChange);
#endif
char timedemo_name[256];
boolean timedemo_csv;
char timedemo_csv_id[256];
@ -560,44 +589,66 @@ UINT8 splitscreen = 0;
boolean circuitmap = false;
INT32 adminplayers[MAXPLAYERS];
// Scheduled commands.
scheduleTask_t **schedule = NULL;
size_t schedule_size = 0;
size_t schedule_len = 0;
// Automation commands
char *automate_commands[AEV__MAX];
const char *automate_names[AEV__MAX] =
{
"RoundStart", // AEV_ROUNDSTART
"IntermissionStart", // AEV_INTERMISSIONSTART
"VoteStart" // AEV_VOTESTART
};
UINT32 livestudioaudience_timer = 90;
/// \warning Keep this up-to-date if you add/remove/rename net text commands
const char *netxcmdnames[MAXNETXCMD - 1] =
{
"NAMEANDCOLOR",
"WEAPONPREF",
"KICK",
"NETVAR",
"SAY",
"MAP",
"EXITLEVEL",
"ADDFILE",
"PAUSE",
"ADDPLAYER",
"TEAMCHANGE",
"CLEARSCORES",
"VERIFIED",
"RANDOMSEED",
"RUNSOC",
"REQADDFILE",
"SETMOTD",
"RESPAWN",
"DEMOTED",
"LUACMD",
"LUAVAR",
"LUAFILE",
"NAMEANDCOLOR", // XD_NAMEANDCOLOR
"WEAPONPREF", // XD_WEAPONPREF
"KICK", // XD_KICK
"NETVAR", // XD_NETVAR
"SAY", // XD_SAY
"MAP", // XD_MAP
"EXITLEVEL", // XD_EXITLEVEL
"ADDFILE", // XD_ADDFILE
"PAUSE", // XD_PAUSE
"ADDPLAYER", // XD_ADDPLAYER
"TEAMCHANGE", // XD_TEAMCHANGE
"CLEARSCORES", // XD_CLEARSCORES
"VERIFIED", // XD_VERIFIED
"RANDOMSEED", // XD_RANDOMSEED
"RUNSOC", // XD_RUNSOC
"REQADDFILE", // XD_REQADDFILE
"SETMOTD", // XD_SETMOTD
"RESPAWN", // XD_RESPAWN
"DEMOTED", // XD_DEMOTED
"LUACMD", // XD_LUACMD
"LUAVAR", // XD_LUAVAR
"LUAFILE", // XD_LUAFILE
// SRB2Kart
"SETUPVOTE",
"MODIFYVOTE",
"PICKVOTE",
"REMOVEPLAYER",
"POWERLEVEL",
"PARTYINVITE",
"ACCEPTPARTYINVITE",
"LEAVEPARTY",
"CANCELPARTYINVITE",
"GIVEITEM",
"ADDBOT"
"SETUPVOTE", // XD_SETUPVOTE
"MODIFYVOTE", // XD_MODIFYVOTE
"PICKVOTE", // XD_PICKVOTE
"REMOVEPLAYER", // XD_REMOVEPLAYER
"POWERLEVEL", // XD_POWERLEVEL
"PARTYINVITE", // XD_PARTYINVITE
"ACCEPTPARTYINVITE", // XD_ACCEPTPARTYINVITE
"LEAVEPARTY", // XD_LEAVEPARTY
"CANCELPARTYINVITE", // XD_CANCELPARTYINVITE
"GIVEITEM", // XD_GIVEITEM
"ADDBOT", // XD_ADDBOT
"DISCORD", // XD_DISCORD
"PLAYSOUND", // XD_PLAYSOUND
"SCHEDULETASK", // XD_SCHEDULETASK
"SCHEDULECLEAR", // XD_SCHEDULECLEAR
"AUTOMATE" // XD_AUTOMATE
};
// =========================================================================
@ -646,6 +697,10 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_GIVEITEM, Got_GiveItemcmd);
RegisterNetXCmd(XD_SCHEDULETASK, Got_ScheduleTaskcmd);
RegisterNetXCmd(XD_SCHEDULECLEAR, Got_ScheduleClearcmd);
RegisterNetXCmd(XD_AUTOMATE, Got_Automatecmd);
// Remote Administration
COM_AddCommand("password", Command_Changepassword_f);
COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
@ -663,6 +718,8 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores);
COM_AddCommand("clearscores", Command_Clearscores_f);
COM_AddCommand("map", Command_Map_f);
COM_AddCommand("randommap", Command_RandomMap);
COM_AddCommand("restartlevel", Command_RestartLevel);
COM_AddCommand("exitgame", Command_ExitGame_f);
COM_AddCommand("retry", Command_Retry_f);
@ -701,6 +758,12 @@ void D_RegisterServerCommands(void)
COM_AddCommand("kartgiveitem", Command_KartGiveItem_f);
COM_AddCommand("schedule_add", Command_Schedule_Add);
COM_AddCommand("schedule_clear", Command_Schedule_Clear);
COM_AddCommand("schedule_list", Command_Schedule_List);
COM_AddCommand("automate_set", Command_Automate_Set);
// for master server connection
AddMServCommands();
@ -760,17 +823,22 @@ void D_RegisterServerCommands(void)
COM_AddCommand("ping", Command_Ping_f);
CV_RegisterVar(&cv_nettimeout);
CV_RegisterVar(&cv_jointimeout);
CV_RegisterVar(&cv_kicktime);
CV_RegisterVar(&cv_skipmapcheck);
CV_RegisterVar(&cv_sleep);
CV_RegisterVar(&cv_maxping);
CV_RegisterVar(&cv_lagless);
CV_RegisterVar(&cv_pingtimeout);
CV_RegisterVar(&cv_showping);
CV_RegisterVar(&cv_pingmeasurement);
CV_RegisterVar(&cv_showviewpointtext);
CV_RegisterVar(&cv_director);
CV_RegisterVar(&cv_schedule);
CV_RegisterVar(&cv_automate);
CV_RegisterVar(&cv_livestudioaudience);
CV_RegisterVar(&cv_dummyconsvar);
#ifdef USE_STUN
@ -961,6 +1029,11 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_consolechat);
CV_RegisterVar(&cv_chatnotifications);
CV_RegisterVar(&cv_chatbacktint);
CV_RegisterVar(&cv_shoutname);
CV_RegisterVar(&cv_shoutcolor);
CV_RegisterVar(&cv_autoshout);
CV_RegisterVar(&cv_songcredits);
CV_RegisterVar(&cv_tutorialprompt);
CV_RegisterVar(&cv_showfocuslost);
@ -1008,6 +1081,7 @@ void D_RegisterClientCommands(void)
// screen.c
CV_RegisterVar(&cv_fullscreen);
CV_RegisterVar(&cv_renderview);
CV_RegisterVar(&cv_renderhitbox);
CV_RegisterVar(&cv_vhseffect);
CV_RegisterVar(&cv_shittyscreen);
CV_RegisterVar(&cv_renderer);
@ -1076,6 +1150,15 @@ void D_RegisterClientCommands(void)
* \sa CleanupPlayerName, SetPlayerName, Got_NameAndColor
* \author Graue <graue@oceanbase.org>
*/
static boolean AllowedPlayerNameChar(char ch)
{
if (!isprint(ch) || ch == ';' || ch == '"' || (UINT8)(ch) >= 0x80)
return false;
return true;
}
boolean EnsurePlayerNameIsGood(char *name, INT32 playernum)
{
INT32 ix;
@ -1097,7 +1180,7 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum)
// Also, anything over 0x80 is disallowed too, since compilers love to
// differ on whether they're printable characters or not.
for (ix = 0; name[ix] != '\0'; ix++)
if (!isprint(name[ix]) || name[ix] == ';' || (UINT8)(name[ix]) >= 0x80)
if (!AllowedPlayerNameChar(name[ix]))
return false;
// Check if a player is currently using the name, case-insensitively.
@ -1188,8 +1271,7 @@ void CleanupPlayerName(INT32 playernum, const char *newname)
do
{
/* from EnsurePlayerNameIsGood */
if (!isprint(*p) || *p == ';' || (UINT8)*p >= 0x80)
if (!AllowedPlayerNameChar(*p))
break;
}
while (*++p) ;
@ -2475,27 +2557,28 @@ void D_SetupVote(void)
UINT8 buf[5*2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes
UINT8 *p = buf;
INT32 i;
UINT8 secondgt = G_SometimesGetDifferentGametype();
UINT8 gt = (cv_kartgametypepreference.value == -1) ? gametype : cv_kartgametypepreference.value;
UINT8 secondgt = G_SometimesGetDifferentGametype(gt);
INT16 votebuffer[4] = {-1,-1,-1,0};
if ((cv_kartencore.value == 1) && (gametyperules & GTR_CIRCUIT))
WRITEUINT8(p, (gametype|0x80));
if ((cv_kartencore.value == 1) && (gametypedefaultrules[gt] & GTR_CIRCUIT))
WRITEUINT8(p, (gt|VOTEMODIFIER_ENCORE));
else
WRITEUINT8(p, gametype);
WRITEUINT8(p, gt);
WRITEUINT8(p, secondgt);
secondgt &= ~0x80;
secondgt &= ~VOTEMODIFIER_ENCORE;
for (i = 0; i < 4; i++)
{
UINT16 m;
if (i == 2) // sometimes a different gametype
m = G_RandMap(G_TOLFlag(secondgt), prevmap, ((secondgt != gametype) ? 2 : 0), 0, true, votebuffer);
else if (i >= 3) // unknown-random and force-unknown MAP HELL
m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, (i-2), (i < 4), votebuffer);
else if (i >= 3) // unknown-random and formerly force-unknown MAP HELL
m = G_RandMap(G_TOLFlag(gt), prevmap, 0, (i-2), (i < 4), votebuffer);
else
m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, true, votebuffer);
m = G_RandMap(G_TOLFlag(gt), prevmap, 0, 0, true, votebuffer);
if (i < 3)
votebuffer[i] = m; // min() is a dumb workaround for gcc 4.4 array-bounds error
votebuffer[i] = m;
WRITEUINT16(p, m);
}
@ -2951,10 +3034,6 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING;
demo.savebutton = 0;
// Sal: Is this needed?
// From experimenting with Lua scripts in vanilla I found a lot of annoying & potentially desync-y things with MapChange.
LUAh_MapChange(mapnumber);
G_InitNew(pencoremode, mapnumber, resetplayer, skipprecutscene, FLS);
if (demo.playback && !demo.timing)
precache = true;
@ -2972,6 +3051,67 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
#endif
}
static void Command_RandomMap(void)
{
INT32 oldmapnum;
INT32 newmapnum;
INT32 newgametype;
boolean newencoremode;
boolean newresetplayers;
if (client && !IsPlayerAdmin(consoleplayer))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
// TODO: Handle singleplayer conditions.
// The existing ones are way too annoyingly complicated and "anti-cheat" for my tastes.
if (Playing())
{
newgametype = gametype;
newencoremode = encoremode;
newresetplayers = false;
if (gamestate == GS_LEVEL)
{
oldmapnum = gamemap-1;
}
else
{
oldmapnum = prevmap;
}
}
else
{
newgametype = cv_dummygametype.value; // Changed from cv_newgametype to match newmenus
newencoremode = false;
newresetplayers = true;
oldmapnum = -1;
}
newmapnum = G_RandMap(G_TOLFlag(newgametype), oldmapnum, 0, 0, false, NULL) + 1;
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
}
static void Command_RestartLevel(void)
{
if (client && !IsPlayerAdmin(consoleplayer))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
if (!Playing())
{
CONS_Printf(M_GetText("You must be in a game to use this.\n"));
return;
}
D_MapChange(gamemap, gametype, encoremode, false, 0, false, false);
}
static void Command_Pause(void)
{
UINT8 buf[2];
@ -3438,7 +3578,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
}
// Don't switch team, just go away, please, go awaayyyy, aaauuauugghhhghgh
if (!LUAh_TeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled))
if (!LUA_HookTeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled))
return;
//Make sure that the right team number is sent. Keep in mind that normal clients cannot change to certain teams in certain gametypes.
@ -3534,7 +3674,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
{
if (localplayertable[i] == playernum)
{
LUAh_ViewpointSwitch(players+playernum, players+playernum, true);
LUA_HookViewpointSwitch(players+playernum, players+playernum, true);
displayplayers[i] = playernum;
break;
}
@ -3688,9 +3828,7 @@ void SetAdminPlayer(INT32 playernum)
void ClearAdminPlayers(void)
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
adminplayers[i] = -1;
memset(adminplayers, -1, sizeof(adminplayers));
}
void RemoveAdminPlayer(INT32 playernum)
@ -3807,6 +3945,218 @@ static void Got_Removal(UINT8 **cp, INT32 playernum)
CONS_Printf(M_GetText("You are no longer a server administrator.\n"));
}
void Schedule_Run(void)
{
size_t i;
if (schedule_len == 0)
{
// No scheduled tasks to run.
return;
}
if (!cv_schedule.value)
{
// We don't WANT to run tasks.
return;
}
if (K_CanChangeRules() == false)
{
// Don't engage in automation while in a restricted context.
return;
}
for (i = 0; i < schedule_len; i++)
{
scheduleTask_t *task = schedule[i];
if (task == NULL)
{
// Shouldn't happen.
break;
}
if (task->timer > 0)
{
task->timer--;
}
if (task->timer == 0)
{
// Reset timer
task->timer = task->basetime;
// Run command for server
if (server)
{
CONS_Printf(
"%d seconds elapsed, running \"" "\x82" "%s" "\x80" "\".\n",
task->basetime,
task->command
);
COM_BufAddText(task->command);
COM_BufAddText("\n");
}
}
}
}
void Schedule_Insert(scheduleTask_t *addTask)
{
if (schedule_len >= schedule_size)
{
if (schedule_size == 0)
{
schedule_size = 8;
}
else
{
schedule_size *= 2;
}
schedule = Z_ReallocAlign(
(void*) schedule,
sizeof(scheduleTask_t*) * schedule_size,
PU_STATIC,
NULL,
sizeof(scheduleTask_t*) * 8
);
}
schedule[schedule_len] = addTask;
schedule_len++;
}
void Schedule_Add(INT16 basetime, INT16 timeleft, const char *command)
{
scheduleTask_t *task = (scheduleTask_t*) Z_CallocAlign(
sizeof(scheduleTask_t),
PU_STATIC,
NULL,
sizeof(scheduleTask_t) * 8
);
task->basetime = basetime;
task->timer = timeleft;
task->command = Z_StrDup(command);
Schedule_Insert(task);
}
void Schedule_Clear(void)
{
size_t i;
for (i = 0; i < schedule_len; i++)
{
scheduleTask_t *task = schedule[i];
if (task->command)
Z_Free(task->command);
}
schedule_len = 0;
schedule_size = 0;
schedule = NULL;
}
void Automate_Set(automateEvents_t type, const char *command)
{
if (!server)
{
// Since there's no list command or anything for this,
// we don't need this code to run for anyone but the server.
return;
}
if (automate_commands[type] != NULL)
{
// Free the old command.
Z_Free(automate_commands[type]);
}
if (command == NULL || strlen(command) == 0)
{
// Remove the command.
automate_commands[type] = NULL;
}
else
{
// New command.
automate_commands[type] = Z_StrDup(command);
}
}
void Automate_Run(automateEvents_t type)
{
if (!server)
{
// Only the server should be doing this.
return;
}
if (K_CanChangeRules() == false)
{
// Don't engage in automation while in a restricted context.
return;
}
#ifdef PARANOIA
if (type >= AEV__MAX)
{
// Shouldn't happen.
I_Error("Attempted to run invalid automation type.");
return;
}
#endif
if (!cv_automate.value)
{
// We don't want to run automation.
return;
}
if (automate_commands[type] == NULL)
{
// No command to run.
return;
}
CONS_Printf(
"Running %s automate command \"" "\x82" "%s" "\x80" "\"...\n",
automate_names[type],
automate_commands[type]
);
COM_BufAddText(automate_commands[type]);
COM_BufAddText("\n");
}
void Automate_Clear(void)
{
size_t i;
for (i = 0; i < AEV__MAX; i++)
{
Automate_Set(i, NULL);
}
}
void LiveStudioAudience(void)
{
if (livestudioaudience_timer == 0)
{
S_StartSound(NULL, sfx_mbv91);
livestudioaudience_timer = 90;
}
else
{
livestudioaudience_timer--;
}
}
static void Command_MotD_f(void)
{
size_t i, j;
@ -4454,7 +4804,7 @@ static void Command_Playintro_f(void)
*/
FUNCNORETURN static ATTRNORETURN void Command_Quit_f(void)
{
LUAh_GameQuit(true);
LUA_HookBool(true, HOOK(GameQuit));
I_Quit();
}
@ -4918,6 +5268,13 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
gt = (UINT8)READUINT8(*cp);
secondgt = (UINT8)READUINT8(*cp);
// Strip illegal Encore flag.
if ((gt & VOTEMODIFIER_ENCORE)
&& !(gametypedefaultrules[(gt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT))
{
gt &= ~VOTEMODIFIER_ENCORE;
}
for (i = 0; i < 4; i++)
{
tempvotelevels[i][0] = (UINT16)READUINT16(*cp);
@ -4931,6 +5288,19 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
return;
}
// If third entry has an illelegal Encore flag... (illelegal!?)
if ((secondgt & VOTEMODIFIER_ENCORE)
&& !(gametypedefaultrules[(secondgt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT))
{
secondgt &= ~VOTEMODIFIER_ENCORE;
// Apply it to the second entry instead, gametype permitting!
if (gametypedefaultrules[gt] & GTR_CIRCUIT)
{
tempvotelevels[1][1] |= VOTEMODIFIER_ENCORE;
}
}
// Finally, set third entry's gametype/Encore status.
tempvotelevels[2][1] = secondgt;
memcpy(votelevels, tempvotelevels, sizeof(votelevels));
@ -4989,6 +5359,101 @@ static void Got_GiveItemcmd(UINT8 **cp, INT32 playernum)
players[playernum].itemamount = amt;
}
static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum)
{
char command[MAXTEXTCMD];
INT16 seconds;
seconds = READINT16(*cp);
READSTRING(*cp, command);
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING,
M_GetText ("Illegal schedule task received from %s\n"),
player_names[playernum]);
if (server)
SendKick(playernum, KICK_MSG_CON_FAIL);
return;
}
Schedule_Add(seconds, seconds, (const char *)command);
if (server || consoleplayer == playernum)
{
CONS_Printf(
"OK! Running \"" "\x82" "%s" "\x80" "\" every " "\x82" "%d" "\x80" " seconds.\n",
command,
seconds
);
}
}
static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum)
{
(void)cp;
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING,
M_GetText ("Illegal schedule clear received from %s\n"),
player_names[playernum]);
if (server)
SendKick(playernum, KICK_MSG_CON_FAIL);
return;
}
Schedule_Clear();
if (server || consoleplayer == playernum)
{
CONS_Printf("All scheduled tasks have been cleared.\n");
}
}
static void Got_Automatecmd(UINT8 **cp, INT32 playernum)
{
UINT8 eventID;
char command[MAXTEXTCMD];
eventID = READUINT8(*cp);
READSTRING(*cp, command);
if (
(playernum != serverplayer && !IsPlayerAdmin(playernum))
|| (eventID >= AEV__MAX)
)
{
CONS_Alert(CONS_WARNING,
M_GetText ("Illegal automate received from %s\n"),
player_names[playernum]);
if (server)
SendKick(playernum, KICK_MSG_CON_FAIL);
return;
}
Automate_Set(eventID, command);
if (server || consoleplayer == playernum)
{
if (command[0] == '\0')
{
CONS_Printf(
"Removed the %s automate command.\n",
automate_names[eventID]
);
}
else
{
CONS_Printf(
"Set the %s automate command to \"" "\x82" "%s" "\x80" "\".\n",
automate_names[eventID],
command
);
}
}
}
/** Prints the number of displayplayers[0].
*
* \todo Possibly remove this; it was useful for debugging at one point.
@ -5027,7 +5492,7 @@ void Command_ExitGame_f(void)
{
INT32 i;
LUAh_GameQuit(false);
LUA_HookBool(false, HOOK(GameQuit));
D_QuitNetGame();
CL_Reset();
@ -5253,6 +5718,135 @@ static void Command_KartGiveItem_f(void)
}
}
static void Command_Schedule_Add(void)
{
UINT8 buf[MAXTEXTCMD];
UINT8 *buf_p = buf;
size_t ac;
INT16 seconds;
const char *command;
if (!(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Printf("Only the server or a remote admin can use this.\n");
return;
}
ac = COM_Argc();
if (ac < 3)
{
CONS_Printf("schedule <seconds> <...>: runs the specified commands on a recurring timer\n");
return;
}
seconds = atoi(COM_Argv(1));
if (seconds <= 0)
{
CONS_Printf("Timer must be at least 1 second.\n");
return;
}
command = COM_Argv(2);
WRITEINT16(buf_p, seconds);
WRITESTRING(buf_p, command);
SendNetXCmd(XD_SCHEDULETASK, buf, buf_p - buf);
}
static void Command_Schedule_Clear(void)
{
if (!(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Printf("Only the server or a remote admin can use this.\n");
return;
}
SendNetXCmd(XD_SCHEDULECLEAR, NULL, 0);
}
static void Command_Schedule_List(void)
{
size_t i;
if (!(server || IsPlayerAdmin(consoleplayer)))
{
// I set it up in a way that this information could be available
// to everyone, but HOSTMOD has it server/admin-only too, so eh?
CONS_Printf("Only the server or a remote admin can use this.\n");
return;
}
if (schedule_len == 0)
{
CONS_Printf("No tasks are scheduled.\n");
return;
}
for (i = 0; i < schedule_len; i++)
{
scheduleTask_t *task = schedule[i];
CONS_Printf(
"In " "\x82" "%d" "\x80" " second%s: " "\x82" "%s" "\x80" "\n",
task->timer,
(task->timer > 1) ? "s" : "",
task->command
);
}
}
static void Command_Automate_Set(void)
{
UINT8 buf[MAXTEXTCMD];
UINT8 *buf_p = buf;
size_t ac;
const char *event;
size_t eventID;
const char *command;
if (!(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Printf("Only the server or a remote admin can use this.\n");
return;
}
ac = COM_Argc();
if (ac < 3)
{
CONS_Printf("automate_set <event> <command>: sets the command to run each time a event triggers\n");
return;
}
event = COM_Argv(1);
for (eventID = 0; eventID < AEV__MAX; eventID++)
{
if (strcasecmp(event, automate_names[eventID]) == 0)
{
break;
}
}
if (eventID == AEV__MAX)
{
CONS_Printf("Unknown event type \"%s\".\n", event);
return;
}
command = COM_Argv(2);
WRITEUINT8(buf_p, eventID);
WRITESTRING(buf_p, command);
SendNetXCmd(XD_AUTOMATE, buf, buf_p - buf);
}
/** Makes a change to ::cv_forceskin take effect immediately.
*
* \sa Command_SetForcedSkin_f, cv_forceskin, forcedskin
@ -5871,6 +6465,33 @@ static void KartEliminateLast_OnChange(void)
P_CheckRacers();
}
static void Schedule_OnChange(void)
{
size_t i;
if (cv_schedule.value)
{
return;
}
if (schedule_len == 0)
{
return;
}
// Reset timers when turning off.
for (i = 0; i < schedule_len; i++)
{
scheduleTask_t *task = schedule[i];
task->timer = task->basetime;
}
}
static void LiveStudioAudience_OnChange(void)
{
livestudioaudience_timer = 90;
}
void Got_DiscordInfo(UINT8 **p, INT32 playernum)
{
if (playernum != serverplayer /*&& !IsPlayerAdmin(playernum)*/)

View file

@ -90,6 +90,7 @@ extern consvar_t cv_kartfrantic;
extern consvar_t cv_kartcomeback;
extern consvar_t cv_kartencore;
extern consvar_t cv_kartvoterulechanges;
extern consvar_t cv_kartgametypepreference;
extern consvar_t cv_kartspeedometer;
extern consvar_t cv_kartvoices;
extern consvar_t cv_kartbot;
@ -117,6 +118,7 @@ extern consvar_t cv_maxping;
extern consvar_t cv_lagless;
extern consvar_t cv_pingtimeout;
extern consvar_t cv_showping;
extern consvar_t cv_pingmeasurement;
extern consvar_t cv_showviewpointtext;
extern consvar_t cv_skipmapcheck;
@ -127,6 +129,10 @@ extern consvar_t cv_perfstats;
extern consvar_t cv_director;
extern consvar_t cv_schedule;
extern consvar_t cv_livestudioaudience;
extern char timedemo_name[256];
extern boolean timedemo_csv;
extern char timedemo_csv_id[256];
@ -170,6 +176,10 @@ typedef enum
XD_GIVEITEM, // 32
XD_ADDBOT, // 33
XD_DISCORD, // 34
XD_PLAYSOUND, // 35
XD_SCHEDULETASK, // 36
XD_SCHEDULECLEAR, // 37
XD_AUTOMATE, // 38
MAXNETXCMD
} netxcmd_t;
@ -238,6 +248,37 @@ void RemoveAdminPlayer(INT32 playernum);
void ItemFinder_OnChange(void);
void D_SetPassword(const char *pw);
typedef struct
{
UINT16 basetime;
UINT16 timer;
char *command;
} scheduleTask_t;
extern scheduleTask_t **schedule;
extern size_t schedule_size;
extern size_t schedule_len;
void Schedule_Run(void);
void Schedule_Insert(scheduleTask_t *addTask);
void Schedule_Add(INT16 basetime, INT16 timeleft, const char *command);
void Schedule_Clear(void);
typedef enum
{
AEV_ROUNDSTART,
AEV_INTERMISSIONSTART,
AEV_VOTESTART,
AEV__MAX
} automateEvents_t;
void Automate_Run(automateEvents_t type);
void Automate_Set(automateEvents_t type, const char *command);
void Automate_Clear(void);
extern UINT32 livestudioaudience_timer;
void LiveStudioAudience(void);
// used for the player setup menu
UINT8 CanChangeSkin(INT32 playernum);

View file

@ -1007,7 +1007,6 @@ static void SV_EndFileSend(INT32 node)
filestosend--;
}
#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
#define FILEFRAGMENTSIZE (software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE))
/** Handles file transmission
@ -1040,14 +1039,7 @@ void FileSendTicker(void)
if (!filestosend) // No file to send
return;
if (cv_downloadspeed.value) // New behavior
packetsent = cv_downloadspeed.value;
else // Old behavior
{
packetsent = PACKETPERTIC;
if (!packetsent)
packetsent = 1;
}
packetsent = cv_downloadspeed.value;
netbuffer->packettype = PT_FILEFRAGMENT;

View file

@ -584,6 +584,8 @@ typedef struct player_s
UINT8 stairjank;
UINT8 shrinkLaserDelay;
#ifdef HWRENDER
fixed_t fovadd; // adjust FOV for hw rendering
#endif

View file

@ -3755,6 +3755,15 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
// Caked-Up Booty-Sheet Ghost
"S_HYUDORO",
// Grow
"S_GROW_PARTICLE",
// Shrink
"S_SHRINK_GUN",
"S_SHRINK_CHAIN",
"S_SHRINK_LASER",
"S_SHRINK_PARTICLE",
// The legend
"S_SINK",
"S_SINK_SHIELD",
@ -4506,6 +4515,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
// because sadly no one remembers this place while searching for full state names.
const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity testing later.
"MT_NULL",
"MT_RAY",
"MT_UNKNOWN",
"MT_THOK", // Thok! mobj
@ -5336,6 +5346,14 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_HYUDORO",
"MT_HYUDORO_CENTER",
"MT_GROW_PARTICLE",
"MT_SHRINK_POHBEE",
"MT_SHRINK_GUN",
"MT_SHRINK_CHAIN",
"MT_SHRINK_LASER",
"MT_SHRINK_PARTICLE",
"MT_SINK", // Kitchen Sink Stuff
"MT_SINK_SHIELD",
"MT_SINKTRAIL",

View file

@ -817,7 +817,6 @@ extern consvar_t cv_forceskin; // force clients to use the server's skin
extern consvar_t cv_downloading; // allow clients to downloading WADs.
extern consvar_t cv_nettimeout; // SRB2Kart: Advanced server options menu
extern consvar_t cv_jointimeout;
extern consvar_t cv_maxping;
extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
extern INT32 serverplayer;
extern INT32 adminplayers[MAXPLAYERS];

View file

@ -368,6 +368,8 @@ typedef UINT32 tic_t;
#define UINT2RGBA(a) (UINT32)((a&0xff)<<24)|((a&0xff00)<<8)|((a&0xff0000)>>8)|(((UINT32)a&0xff000000)>>24)
#endif
#define TOSTR(x) #x
/* preprocessor dumb and needs second macro to expand input */
#define WSTRING2(s) L ## s
#define WSTRING(s) WSTRING2 (s)

View file

@ -40,6 +40,7 @@
#include "fastcmp.h"
#include "lua_hud.h"
#include "lua_hook.h"
// SRB2Kart
#include "k_menu.h"
@ -2103,7 +2104,7 @@ luahook:
if (renderisnewtic)
{
LUA_HUD_ClearDrawList(luahuddrawlist_title);
LUAh_TitleHUD(luahuddrawlist_title);
LUA_HookHUD(luahuddrawlist_title, HUD_HOOK(title));
}
LUA_HUD_DrawList(luahuddrawlist_title);
}

View file

@ -380,6 +380,36 @@ consvar_t cv_chatbacktint = CVAR_INIT ("chatbacktint", "On", CV_SAVE, CV_OnOff,
static CV_PossibleValue_t consolechat_cons_t[] = {{0, "Window"}, {1, "Console"}, {2, "Window (Hidden)"}, {0, NULL}};
consvar_t cv_consolechat = CVAR_INIT ("chatmode", "Window", CV_SAVE, consolechat_cons_t, NULL);
// Shout settings
// The relevant ones are CV_NETVAR because too lazy to send them any other way
consvar_t cv_shoutname = CVAR_INIT ("shout_name", "SERVER", CV_NETVAR, NULL, NULL);
static CV_PossibleValue_t shoutcolor_cons_t[] =
{
{-1, "Player color"},
{0, "White"},
{1, "Yellow"},
{2, "Purple"},
{3, "Green"},
{4, "Blue"},
{5, "Red"},
{6, "Gray"},
{7, "Orange"},
{8, "Sky-blue"},
{9, "Gold"},
{10, "Lavender"},
{11, "Aqua-green"},
{12, "Magenta"},
{13, "Pink"},
{14, "Brown"},
{15, "Tan"},
{0, NULL}
};
consvar_t cv_shoutcolor = CVAR_INIT ("shout_color", "Red", CV_NETVAR, shoutcolor_cons_t, NULL);
// If on and you're an admin, your messages will automatically become shouts.
consvar_t cv_autoshout = CVAR_INIT ("autoshout", "Off", CV_NETVAR, CV_OnOff, NULL);
// Pause game upon window losing focus
consvar_t cv_pauseifunfocused = CVAR_INIT ("pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL);
@ -1172,7 +1202,7 @@ aftercmdinput:
*/
if (addedtogame && gamestate == GS_LEVEL)
{
LUAh_PlayerCmd(player, cmd);
LUA_HookTiccmd(player, cmd, HOOK(PlayerCmd));
// Send leveltime when this tic was generated to the server for control lag calculations.
// Only do this when in a level. Also do this after the hook, so that it can't overwrite this.
@ -1202,7 +1232,7 @@ aftercmdinput:
{
// Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
LUA_HookViewpointSwitch(player, &players[consoleplayer], true);
displayplayers[0] = consoleplayer;
}
}
@ -1253,6 +1283,7 @@ static void weaponPrefChange4(void)
//
void G_DoLoadLevel(boolean resetplayer)
{
boolean doAutomate = false;
INT32 i;
// Make sure objectplace is OFF when you first start the level!
@ -1286,6 +1317,10 @@ void G_DoLoadLevel(boolean resetplayer)
else
titlemapinaction = TITLEMAP_OFF;
// Doing this matches HOSTMOD behavior.
// Is that desired? IDK
doAutomate = (gamestate != GS_LEVEL);
G_SetGamestate(GS_LEVEL);
if (wipegamestate == GS_MENU)
M_ClearMenus(true);
@ -1324,6 +1359,11 @@ void G_DoLoadLevel(boolean resetplayer)
CON_ClearHUD();
server_lagless = cv_lagless.value;
if (doAutomate == true)
{
Automate_Run(AEV_ROUNDSTART);
}
}
//
@ -2475,7 +2515,7 @@ void G_SpawnPlayer(INT32 playernum)
P_SpawnPlayer(playernum);
G_MovePlayerToSpawnOrStarpost(playernum);
LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :)
LUA_HookPlayer(&players[playernum], HOOK(PlayerSpawn)); // Lua hook for player spawning :)
}
void G_MovePlayerToSpawnOrStarpost(INT32 playernum)
@ -3202,51 +3242,53 @@ boolean G_GametypeHasSpectators(void)
//
// Oh, yeah, and we sometimes flip encore mode on here too.
//
INT16 G_SometimesGetDifferentGametype(void)
INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype)
{
boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1) && (gametyperules & GTR_CIRCUIT));
// Most of the gametype references in this condition are intentionally not prefgametype.
// This is so a server CAN continue playing a gametype if they like the taste of it.
// The encore check needs prefgametype so can't use G_RaceGametype...
boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1)
&& ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT));
UINT8 encoremodifier = 0;
if (!cv_kartvoterulechanges.value // never
&& encorescramble != 1) // destroying the code for this one instance
return gametype;
if (randmapbuffer[NUMMAPS] > 0 && (encorepossible || cv_kartvoterulechanges.value != 3))
if (encorepossible)
{
randmapbuffer[NUMMAPS]--;
if (encorepossible)
if (encorescramble != -1)
{
if (encorescramble != -1)
encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission
else
{
switch (cv_kartvoterulechanges.value)
{
case 3: // always
randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set
break;
case 2: // frequent
encorepossible = M_RandomChance(FRACUNIT>>1);
break;
case 1: // sometimes
default:
encorepossible = M_RandomChance(FRACUNIT>>2);
break;
}
}
if (encorepossible != (cv_kartencore.value == 1))
return (gametype|0x80);
encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission
}
return gametype;
else
{
switch (cv_kartvoterulechanges.value)
{
case 3: // always
encorepossible = true;
break;
case 2: // frequent
encorepossible = M_RandomChance(FRACUNIT>>1);
break;
case 1: // sometimes
encorepossible = M_RandomChance(FRACUNIT>>2);
break;
default:
break;
}
}
if (encorepossible != (cv_kartencore.value == 1))
encoremodifier = VOTEMODIFIER_ENCORE;
}
if (!cv_kartvoterulechanges.value) // never (again)
return gametype;
if (!cv_kartvoterulechanges.value) // never
return (gametype|encoremodifier);
if (randmapbuffer[NUMMAPS] > 0 && (cv_kartvoterulechanges.value != 3))
{
randmapbuffer[NUMMAPS]--;
return (gametype|encoremodifier);
}
switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv?
{
case 3: // always
randmapbuffer[NUMMAPS] = 1; // every other vote (or always if !encorepossible)
break;
case 1: // sometimes
randmapbuffer[NUMMAPS] = 5; // per "cup"
break;
@ -3257,9 +3299,17 @@ INT16 G_SometimesGetDifferentGametype(void)
break;
}
if (gametype == GT_BATTLE)
return GT_RACE;
return GT_BATTLE;
// Only this response is prefgametype-based.
// todo custom gametypes
if (prefgametype == GT_BATTLE)
{
// Intentionally does not use encoremodifier!
if (cv_kartencore.value == 1)
return (GT_RACE|VOTEMODIFIER_ENCORE);
return (GT_RACE);
}
// This might appear wrong HERE, but the game will display the Encore possibility on the second voting choice instead.
return (GT_BATTLE|encoremodifier);
}
//
@ -4627,7 +4677,7 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer);
else
{
LUAh_MapChange(gamemap);
LUA_HookInt(gamemap, HOOK(MapChange));
G_DoLoadLevel(resetplayer);
}

View file

@ -48,6 +48,7 @@ extern boolean promptactive;
extern consvar_t cv_tutorialprompt;
extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection;
extern consvar_t cv_shoutname, cv_shoutcolor, cv_autoshout;
extern consvar_t cv_songcredits;
extern consvar_t cv_pauseifunfocused;
@ -181,7 +182,8 @@ boolean G_IsSpecialStage(INT32 mapnum);
boolean G_GametypeUsesLives(void);
boolean G_GametypeHasTeams(void);
boolean G_GametypeHasSpectators(void);
INT16 G_SometimesGetDifferentGametype(void);
#define VOTEMODIFIER_ENCORE 0x80
INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype);
UINT8 G_GetGametypeColor(INT16 gt);
void G_ExitLevel(void);
void G_NextLevel(void);

View file

@ -499,6 +499,9 @@ INT32 G_KeyStringtoNum(const char *keystr)
{
UINT32 j;
if (!keystr[0])
return 0;
if (!keystr[1] && keystr[0] > ' ' && keystr[0] <= 'z')
return keystr[0];

View file

@ -133,6 +133,7 @@ typedef struct
// Predefined shader types
enum
{
SHADER_NONE = -1,
SHADER_DEFAULT = 0,
SHADER_FLOOR,
@ -235,7 +236,8 @@ enum EPolyFlags
PF_RemoveYWrap = 0x00010000, // Forces clamp texture on Y
PF_ForceWrapX = 0x00020000, // Forces repeat texture on X
PF_ForceWrapY = 0x00040000, // Forces repeat texture on Y
PF_Ripple = 0x00100000 // Water ripple effect. The current backend doesn't use it for anything.
PF_Ripple = 0x00100000, // Water ripple effect. The current backend doesn't use it for anything.
PF_WireFrame = 0x00200000, // Draws vertices as lines instead of triangles
};

View file

@ -83,6 +83,7 @@ typedef struct gl_vissprite_s
boolean flip, vflip;
boolean precip; // Tails 08-25-2002
boolean bbox;
boolean rotated;
UINT8 translucency; //alpha level 0-255

View file

@ -70,6 +70,7 @@ static void HWR_ProjectSprite(mobj_t *thing);
#ifdef HWPRECIP
static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing);
#endif
static void HWR_ProjectBoundingBox(mobj_t *thing);
static void HWR_RollTransform(FTransform *tr, angle_t roll);
void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap);
@ -4109,6 +4110,54 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
HWR_LinkDrawHackAdd(wallVerts, spr);
}
static void HWR_DrawBoundingBox(gl_vissprite_t *vis)
{
FOutVector v[24];
FSurfaceInfo Surf = {0};
//
// create a cube (side view)
//
// 5--4 3
// |
// |
// 0--1 2
//
// repeat this 4 times (overhead)
//
//
// 17 20 21 11
// 16 15 14 10
// 27 22 *--* 07 12
// | |
// 26 23 *--* 06 13
// 24 00 01 02
// 25 05 04 03
//
v[000].x = v[005].x = v[015].x = v[016].x = v[017].x = v[020].x =
v[022].x = v[023].x = v[024].x = v[025].x = v[026].x = v[027].x = vis->x1; // west
v[001].x = v[002].x = v[003].x = v[004].x = v[006].x = v[007].x =
v[010].x = v[011].x = v[012].x = v[013].x = v[014].x = v[021].x = vis->x2; // east
v[000].z = v[001].z = v[002].z = v[003].z = v[004].z = v[005].z =
v[006].z = v[013].z = v[023].z = v[024].z = v[025].z = v[026].z = vis->z1; // south
v[007].z = v[010].z = v[011].z = v[012].z = v[014].z = v[015].z =
v[016].z = v[017].z = v[020].z = v[021].z = v[022].z = v[027].z = vis->z2; // north
v[000].y = v[001].y = v[002].y = v[006].y = v[007].y = v[010].y =
v[014].y = v[015].y = v[016].y = v[022].y = v[023].y = v[024].y = vis->gz; // bottom
v[003].y = v[004].y = v[005].y = v[011].y = v[012].y = v[013].y =
v[017].y = v[020].y = v[021].y = v[025].y = v[026].y = v[027].y = vis->gzt; // top
Surf.PolyColor = V_GetColor(R_GetBoundingBoxColor(vis->mobj));
HWR_ProcessPolygon(&Surf, v, 24, PF_Modulated|PF_NoTexture|PF_WireFrame, SHADER_NONE, false);
}
// -----------------+
// HWR_DrawSprite : Draw flat sprites
// : (monsters, bonuses, weapons, lights, ...)
@ -4562,9 +4611,16 @@ static int CompareVisSprites(const void *p1, const void *p2)
int frame1;
int frame2;
int linkdraw1;
int linkdraw2;
// bbox doesn't need to be sorted
if (spr1->bbox || spr2->bbox)
return 0;
// check for precip first, because then sprX->mobj is actually a precipmobj_t and does not have flags2 or tracer
int linkdraw1 = !spr1->precip && (spr1->mobj->flags2 & MF2_LINKDRAW) && spr1->mobj->tracer;
int linkdraw2 = !spr2->precip && (spr2->mobj->flags2 & MF2_LINKDRAW) && spr2->mobj->tracer;
linkdraw1 = !spr1->precip && (spr1->mobj->flags2 & MF2_LINKDRAW) && spr1->mobj->tracer;
linkdraw2 = !spr2->precip && (spr2->mobj->flags2 & MF2_LINKDRAW) && spr2->mobj->tracer;
// ^ is the XOR operation
// if comparing a linkdraw and non-linkdraw sprite or 2 linkdraw sprites with different tracers, then use
@ -4954,6 +5010,9 @@ static void HWR_DrawSprites(void)
for (i = 0; i < gl_visspritecount; i++)
{
gl_vissprite_t *spr = gl_vsprorder[i];
if (spr->bbox)
HWR_DrawBoundingBox(spr);
else
#ifdef HWPRECIP
if (spr->precip)
HWR_DrawPrecipitationSprite(spr);
@ -5052,8 +5111,15 @@ static void HWR_AddSprites(sector_t *sec)
limit_dist = (fixed_t)(cv_drawdist.value) * mapobjectscale;
for (thing = sec->thinglist; thing; thing = thing->snext)
{
if (R_ThingVisibleWithinDist(thing, limit_dist))
HWR_ProjectSprite(thing);
if (R_ThingWithinDist(thing, limit_dist))
{
if (R_ThingVisible(thing))
{
HWR_ProjectSprite(thing);
}
HWR_ProjectBoundingBox(thing);
}
}
#ifdef HWPRECIP
@ -5552,6 +5618,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
vis->vflip = vflip;
vis->precip = false;
vis->bbox = false;
}
#ifdef HWPRECIP
@ -5675,6 +5742,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
vis->gz = vis->gzt - (FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale);
vis->precip = true;
vis->bbox = false;
// okay... this is a hack, but weather isn't networked, so it should be ok
if (!(thing->precipflags & PCF_THUNK))
@ -5685,6 +5753,60 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
}
#endif
static void HWR_ProjectBoundingBox(mobj_t *thing)
{
gl_vissprite_t *vis;
float tr_x, tr_y;
float tz;
float rad;
// uncapped/interpolation
interpmobjstate_t interp = {0};
if (!thing)
return;
if (!R_ThingBoundingBoxVisible(thing))
return;
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(thing, rendertimefrac, &interp);
}
else
{
R_InterpolateMobjState(thing, FRACUNIT, &interp);
}
// transform the origin point
tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx;
tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy;
// rotation around vertical axis
tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin);
// thing is behind view plane?
if (tz < ZCLIP_PLANE)
return;
tr_x += gl_viewx;
tr_y += gl_viewy;
rad = FIXED_TO_FLOAT(thing->radius);
vis = HWR_NewVisSprite();
vis->x1 = tr_x - rad;
vis->x2 = tr_x + rad;
vis->z1 = tr_y - rad;
vis->z2 = tr_y + rad;
vis->gz = FIXED_TO_FLOAT(interp.z);
vis->gzt = vis->gz + FIXED_TO_FLOAT(thing->height);
vis->mobj = thing;
vis->precip = false;
vis->bbox = true;
}
// ==========================================================================
// Sky dome rendering, ported from PrBoom+
// ==========================================================================

View file

@ -713,11 +713,11 @@ static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMi
UINT16 w = gpatch->width, h = gpatch->height;
UINT32 size = w*h;
RGBA_t *image, *blendimage, *cur, blendcolor;
UINT8 translation[16]; // First the color index
UINT8 cutoff[16]; // Brightness cutoff before using the next color
UINT8 translation[17]; // First the color index
UINT8 cutoff[17]; // Brightness cutoff before using the next color
UINT8 translen = 0;
UINT8 i;
UINT8 colorbrightnesses[16];
UINT8 colorbrightnesses[17];
UINT8 color_match_lookup[256]; // optimization attempt
blendcolor = V_GetColor(0); // initialize
@ -788,6 +788,11 @@ static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMi
translen++;
}
if (translen > 0)
translation[translen] = translation[translen-1]; // extended to accomodate secondi if firsti equal to translen-1
if (translen > 1)
cutoff[translen] = cutoff[translen-1] = 0; // as above
if (skinnum == TC_RAINBOW && translen > 0)
{
UINT16 b;
@ -803,7 +808,7 @@ static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMi
{
UINT16 brightdif = 256;
color_match_lookup[i] = 0;
color_match_lookup[b] = 0;
for (i = 0; i < translen; i++)
{
if (b > colorbrightnesses[i]) // don't allow greater matches (because calculating a makeshift gradient for this is already a huge mess as is)
@ -820,6 +825,9 @@ static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMi
}
}
if (translen > 0)
colorbrightnesses[translen] = colorbrightnesses[translen-1];
while (size--)
{
if (skinnum == TC_HITLAG)

View file

@ -1061,6 +1061,12 @@ EXPORT void HWRAPI(LoadCustomShader) (int number, char *code, size_t size, boole
EXPORT void HWRAPI(SetShader) (int type)
{
#ifdef GL_SHADERS
if (type == SHADER_NONE)
{
UnSetShader();
return;
}
if (gl_allowshaders != HWD_SHADEROPTION_OFF)
{
gl_shader_t *shader = gl_shaderstate.current;
@ -2319,7 +2325,7 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUI
pglVertexPointer(3, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].x);
pglTexCoordPointer(2, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].s);
pglDrawArrays(GL_TRIANGLE_FAN, 0, iNumPts);
pglDrawArrays(PolyFlags & PF_WireFrame ? GL_LINES : GL_TRIANGLE_FAN, 0, iNumPts);
if (PolyFlags & PF_RemoveYWrap)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

View file

@ -55,6 +55,8 @@ consvar_t cv_masterserver_token = CVAR_INIT
NULL
);
#define HMS_QUERY_VERSION "?v=2.2"
#ifdef MASTERSERVER
static int hms_started;
@ -174,7 +176,7 @@ HMS_connect (const char *format, ...)
va_start (ap, format);
url = malloc(seek + vsnprintf(0, 0, format, ap) +
sizeof "?v=2" - 1 +
sizeof HMS_QUERY_VERSION - 1 +
token_length + 1);
va_end (ap);
@ -188,8 +190,8 @@ HMS_connect (const char *format, ...)
seek += vsprintf(&url[seek], format, ap);
va_end (ap);
strcpy(&url[seek], "?v=2");
seek += sizeof "?v=2" - 1;
strcpy(&url[seek], HMS_QUERY_VERSION);
seek += sizeof HMS_QUERY_VERSION - 1;
if (quack_token)
sprintf(&url[seek], "&token=%s", quack_token);

View file

@ -64,8 +64,11 @@
#define HU_INPUTX 0
#define HU_INPUTY 0
#define HU_SERVER_SAY 1 // Server message (dedicated).
#define HU_CSAY 2 // Server CECHOes to everyone.
typedef enum
{
HU_SHOUT = 1, // Shout message
HU_CSAY = 1<<1, // Middle-of-screen server message
} sayflags_t;
//-------------------------------------------
// heads up font
@ -75,6 +78,7 @@
// Note: I'd like to adress that at this point we might *REALLY* want to work towards a common drawString function that can take any font we want because this is really turning into a MESS. :V -Lat'
patch_t *pinggfx[5]; // small ping graphic
patch_t *mping[5]; // smaller ping graphic
patch_t *pingmeasure[2]; // ping measurement graphic
patch_t *framecounter;
patch_t *frameslash; // framerate stuff. Used in screen.c
@ -169,6 +173,7 @@ static void Command_Say_f(void);
static void Command_Sayto_f(void);
static void Command_Sayteam_f(void);
static void Command_CSay_f(void);
static void Command_Shout(void);
static void Got_Saycmd(UINT8 **p, INT32 playernum);
#endif
@ -192,6 +197,9 @@ void HU_LoadGraphics(void)
HU_UpdatePatch(&mping[i], "MPING%d", i+1);
}
HU_UpdatePatch(&pingmeasure[0], "PINGD");
HU_UpdatePatch(&pingmeasure[1], "PINGMS");
// fps stuff
HU_UpdatePatch(&framecounter, "FRAMER");
HU_UpdatePatch(&frameslash, "FRAMESL");
@ -209,6 +217,7 @@ void HU_Init(void)
COM_AddCommand("sayto", Command_Sayto_f);
COM_AddCommand("sayteam", Command_Sayteam_f);
COM_AddCommand("csay", Command_CSay_f);
COM_AddCommand("shout", Command_Shout);
RegisterNetXCmd(XD_SAY, Got_Saycmd);
#endif
@ -473,7 +482,7 @@ void HU_AddChatText(const char *text, boolean playsound)
* to -32 to say to everyone on that player's team. Note: This means you
* have to add 1 to the player number, since they are 0 to 31 internally.
*
* The flag HU_SERVER_SAY will be set if it is the dedicated server speaking.
* The flag HU_SHOUT will be set if it is the dedicated server speaking.
*
* This function obtains the message using COM_Argc() and COM_Argv().
*
@ -500,14 +509,17 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
return;
}
// Only servers/admins can CSAY.
if(!server && !(IsPlayerAdmin(consoleplayer)))
flags &= ~HU_CSAY;
// Only servers/admins can shout or CSAY.
if (!server && !IsPlayerAdmin(consoleplayer))
{
flags &= ~(HU_SHOUT|HU_CSAY);
}
// We handle HU_SERVER_SAY, not the caller.
flags &= ~HU_SERVER_SAY;
if(dedicated && !(flags & HU_CSAY))
flags |= HU_SERVER_SAY;
// Enforce shout for the dedicated server.
if (dedicated && !(flags & HU_CSAY))
{
flags |= HU_SHOUT;
}
buf[0] = target;
buf[1] = flags;
@ -581,6 +593,8 @@ static void Command_Say_f(void)
return;
}
// Autoshout is handled by HU_queueChatChar.
// If you're using the say command, you can use the shout command, lol.
DoSayCommand(0, 1, 0);
}
@ -644,7 +658,7 @@ static void Command_CSay_f(void)
return;
}
if(!server && !IsPlayerAdmin(consoleplayer))
if (!server && !IsPlayerAdmin(consoleplayer))
{
CONS_Alert(CONS_NOTICE, M_GetText("Only servers and admins can use csay.\n"));
return;
@ -652,6 +666,24 @@ static void Command_CSay_f(void)
DoSayCommand(0, 1, HU_CSAY);
}
static void Command_Shout(void)
{
if (COM_Argc() < 2)
{
CONS_Printf(M_GetText("shout <message>: send a message with special alert sound, name, and color\n"));
return;
}
if (!server && !IsPlayerAdmin(consoleplayer))
{
CONS_Alert(CONS_NOTICE, M_GetText("Only servers and admins can use shout.\n"));
return;
}
DoSayCommand(0, 1, HU_SHOUT);
}
static tic_t stop_spamming[MAXPLAYERS];
/** Receives a message, processing an ::XD_SAY command.
@ -675,7 +707,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
msg = (char *)*p;
SKIPSTRING(*p);
if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && !(IsPlayerAdmin(playernum)))
if ((cv_mute.value || flags & (HU_CSAY|HU_SHOUT)) && playernum != serverplayer && !(IsPlayerAdmin(playernum)))
{
CONS_Alert(CONS_WARNING, cv_mute.value ?
M_GetText("Illegal say command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"),
@ -704,7 +736,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
// before we do anything, let's verify the guy isn't spamming, get this easier on us.
//if (stop_spamming[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY))
if (stop_spamming[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & HU_CSAY))
if (stop_spamming[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & (HU_CSAY|HU_SHOUT)))
{
CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", playernum+1, player_names[playernum]);
stop_spamming[playernum] = 4;
@ -715,7 +747,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
// run the lua hook even if we were supposed to eat the msg, netgame consistency goes first.
if (LUAh_PlayerMsg(playernum, target, flags, msg, spam_eatmsg))
if (LUA_HookPlayerMsg(playernum, target, flags, msg, spam_eatmsg))
return;
if (spam_eatmsg)
@ -737,8 +769,8 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
action = true;
}
if (flags & HU_SERVER_SAY)
dispname = "SERVER";
if (flags & HU_SHOUT)
dispname = cv_shoutname.zstring;
else
dispname = player_names[playernum];
@ -764,7 +796,30 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
char *tempchar = NULL;
char color_prefix[2];
if (players[playernum].spectator)
if (flags & HU_SHOUT)
{
if (cv_shoutcolor.value == -1)
{
UINT16 chatcolor = skincolors[players[playernum].skincolor].chatcolor;
if (chatcolor > V_TANMAP)
{
sprintf(color_prefix, "%c", '\x80');
}
else
{
sprintf(color_prefix, "%c", '\x80' + (chatcolor >> V_CHARCOLORSHIFT));
}
}
else
{
sprintf(color_prefix, "%c", '\x80' + cv_shoutcolor.value);
}
// Colorize full text
cstart = textcolor = color_prefix;
}
else if (players[playernum].spectator)
{
// grey text
cstart = textcolor = "\x86";
@ -851,7 +906,10 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
fmt2 = "%s<%s%s>\x80%s %s%s";
}*/
HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, textcolor, msg), cv_chatnotifications.value); // add to chat
HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, textcolor, msg), (cv_chatnotifications.value) && !(flags & HU_SHOUT)); // add to chat
if ((cv_chatnotifications.value) && (flags & HU_SHOUT))
S_StartSound(NULL, sfx_sysmsg);
if (tempchar)
Z_Free(tempchar);
@ -1175,7 +1233,7 @@ static void HU_queueChatChar(INT32 c)
else
buf[0] = target;
buf[1] = 0; // flags
buf[1] = ((server || IsPlayerAdmin(consoleplayer)) && cv_autoshout.value) ? HU_SHOUT : 0; // flags
SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1);
}
return;
@ -2154,7 +2212,7 @@ void HU_Drawer(void)
if (renderisnewtic)
{
LUA_HUD_ClearDrawList(luahuddrawlist_scores);
LUAh_ScoresHUD(luahuddrawlist_scores);
LUA_HookHUD(luahuddrawlist_scores, HUD_HOOK(scores));
}
LUA_HUD_DrawList(luahuddrawlist_scores);
}
@ -2277,15 +2335,15 @@ void HU_Erase(void)
//======================================================================
static int
Ping_gfx_num (int ping)
Ping_gfx_num (int lag)
{
if (ping < 76)
if (lag < 2)
return 0;
else if (ping < 137)
else if (lag < 4)
return 1;
else if (ping < 256)
else if (lag < 7)
return 2;
else if (ping < 500)
else if (lag < 10)
return 3;
else
return 4;
@ -2294,22 +2352,37 @@ Ping_gfx_num (int ping)
//
// HU_drawPing
//
void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags)
void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags)
{
INT32 gfxnum; // gfx to draw
UINT8 const *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE);
UINT8 *colormap = NULL;
INT32 measureid = cv_pingmeasurement.value ? 1 : 0;
INT32 gfxnum; // gfx to draw
gfxnum = Ping_gfx_num(ping);
gfxnum = Ping_gfx_num(lag);
V_DrawScaledPatch(x, y, flags, pinggfx[gfxnum]);
if (servermaxping && ping > servermaxping && hu_tick < 4) // flash ping red if too high
V_DrawPingNum(x, y+9, flags, ping, colormap);
else
V_DrawPingNum(x, y+9, flags, ping, NULL);
if (measureid == 1)
V_DrawScaledPatch(x+11 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]);
V_DrawScaledPatch(x+2, y, flags, pinggfx[gfxnum]);
if (servermaxping && lag > servermaxping && hu_tick < 4)
{
// flash ping red if too high
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE);
}
if (cv_pingmeasurement.value)
{
lag = (INT32)(lag * (1000.00f / TICRATE));
}
x = V_DrawPingNum(x + (measureid == 1 ? 11 - pingmeasure[measureid]->width : 10), y+9, flags, lag, colormap);
if (measureid == 0)
V_DrawScaledPatch(x+1 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]);
}
void
HU_drawMiniPing (INT32 x, INT32 y, UINT32 ping, INT32 flags)
HU_drawMiniPing (INT32 x, INT32 y, UINT32 lag, INT32 flags)
{
patch_t *patch;
INT32 w = BASEVIDWIDTH;
@ -2319,7 +2392,7 @@ HU_drawMiniPing (INT32 x, INT32 y, UINT32 ping, INT32 flags)
w /= 2;
}
patch = mping[Ping_gfx_num(ping)];
patch = mping[Ping_gfx_num(lag)];
if (( flags & V_SNAPTORIGHT ))
x += ( w - SHORT (patch->width) );

View file

@ -31,6 +31,8 @@
/// For use on the internet
#define INETPACKETLENGTH 1024
#define NO_BAN_TIME (time_t)(-1)
extern INT16 hardware_MAXPACKETLENGTH;
extern INT32 net_bandwidth; // in byte/s
@ -150,7 +152,7 @@ extern void (*I_NetCloseSocket)(void);
/** \brief send a hole punching request
*/
extern void (*I_NetRequestHolePunch)(void);
extern void (*I_NetRequestHolePunch)(INT32 node);
/** \brief register this machine on the hole punching server
*/
@ -162,8 +164,20 @@ extern void (*I_ClearBans)(void);
extern const char *(*I_GetNodeAddress) (INT32 node);
extern const char *(*I_GetBanAddress) (size_t ban);
extern const char *(*I_GetBanMask) (size_t ban);
extern const char *(*I_GetBanUsername) (size_t ban);
extern const char *(*I_GetBanReason) (size_t ban);
extern time_t (*I_GetUnbanTime) (size_t ban);
extern boolean (*I_SetBanAddress) (const char *address,const char *mask);
extern boolean *bannednode;
extern boolean (*I_SetBanUsername) (const char *username);
extern boolean (*I_SetBanReason) (const char *reason);
extern boolean (*I_SetUnbanTime) (time_t timestamp);
typedef struct
{
size_t banid;
time_t timeleft;
} bannednode_t;
extern bannednode_t *bannednode;
/// \brief Called by D_SRB2Main to be defined by extern network driver
boolean I_InitNetwork(void);

View file

@ -138,8 +138,6 @@
#endif // !NONET
#define MAXBANS 100
#include "i_system.h"
#include "i_time.h"
#include "i_net.h"
@ -148,6 +146,7 @@
#include "i_tcp.h"
#include "m_argv.h"
#include "stun.h"
#include "z_zone.h"
#include "doomstat.h"
@ -189,6 +188,16 @@
#if (defined (WATTCP) && !defined (__libsocket_socklen_t)) || defined (USE_WINSOCK1)
typedef int socklen_t;
#endif
typedef struct
{
mysockaddr_t address;
UINT8 mask;
char *username;
char *reason;
time_t timestamp;
} banned_t;
static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET};
static size_t mysocketses = 0;
static int myfamily[MAXNETNODES+1] = {0};
@ -197,13 +206,14 @@
static mysockaddr_t broadcastaddress[MAXNETNODES+1];
static size_t broadcastaddresses = 0;
static boolean nodeconnected[MAXNETNODES+1];
static mysockaddr_t banned[MAXBANS];
static UINT8 bannedmask[MAXBANS];
static banned_t *banned;
static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11);
#endif
static size_t numbans = 0;
static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
static size_t banned_size = 0;
static bannednode_t SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
static boolean init_tcp_driver = false;
static const char *serverport_name = DEFAULTPORT;
@ -431,7 +441,7 @@ static const char *SOCK_GetBanAddress(size_t ban)
#ifdef NONET
return NULL;
#else
return SOCK_AddrToStr(&banned[ban]);
return SOCK_AddrToStr(&banned[ban].address);
#endif
}
@ -443,12 +453,48 @@ static const char *SOCK_GetBanMask(size_t ban)
static char s[16]; //255.255.255.255 netmask? no, just CDIR for only
if (ban >= numbans)
return NULL;
if (sprintf(s,"%d",bannedmask[ban]) > 0)
if (sprintf(s,"%d",banned[ban].mask) > 0)
return s;
#endif
return NULL;
}
static const char *SOCK_GetBanUsername(size_t ban)
{
#ifdef NONET
(void)ban;
return NULL;
#else
if (ban >= numbans)
return NULL;
return banned[ban].username;
#endif
}
static const char *SOCK_GetBanReason(size_t ban)
{
#ifdef NONET
(void)ban;
return NULL;
#else
if (ban >= numbans)
return NULL;
return banned[ban].reason;
#endif
}
static time_t SOCK_GetUnbanTime(size_t ban)
{
#ifdef NONET
(void)ban;
return NO_BAN_TIME;
#else
if (ban >= numbans)
return NO_BAN_TIME;
return banned[ban].timestamp;
#endif
}
#ifndef NONET
static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
{
@ -622,6 +668,8 @@ static boolean SOCK_Get(void)
j = getfreenode();
if (j > 0)
{
const time_t curTime = time(NULL);
M_Memcpy(&clientaddress[j], &fromaddress, fromlen);
nodesocket[j] = mysockets[n];
DEBFILE(va("New node detected: node:%d address:%s\n", j,
@ -632,15 +680,39 @@ static boolean SOCK_Get(void)
// check if it's a banned dude so we can send a refusal later
for (i = 0; i < numbans; i++)
{
if (SOCK_cmpaddr(&fromaddress, &banned[i], bannedmask[i]))
if (SOCK_cmpaddr(&fromaddress, &banned[i].address, banned[i].mask))
{
SOCK_bannednode[j] = true;
DEBFILE("This dude has been banned\n");
break;
if (banned[i].timestamp != NO_BAN_TIME)
{
if (curTime >= banned[i].timestamp)
{
SOCK_bannednode[j].timeleft = NO_BAN_TIME;
SOCK_bannednode[j].banid = SIZE_MAX;
DEBFILE("This dude was banned, but enough time has passed\n");
break;
}
SOCK_bannednode[j].timeleft = banned[i].timestamp - curTime;
SOCK_bannednode[j].banid = i;
DEBFILE("This dude has been temporarily banned\n");
break;
}
else
{
SOCK_bannednode[j].timeleft = NO_BAN_TIME;
SOCK_bannednode[j].banid = i;
DEBFILE("This dude has been banned\n");
break;
}
}
}
if (i == numbans)
SOCK_bannednode[j] = false;
{
SOCK_bannednode[j].timeleft = NO_BAN_TIME;
SOCK_bannednode[j].banid = SIZE_MAX;
}
return true;
}
else
@ -1382,9 +1454,9 @@ static void rendezvous(int size)
free(addrs);
}
static void SOCK_RequestHolePunch(void)
static void SOCK_RequestHolePunch(INT32 node)
{
mysockaddr_t * addr = &clientaddress[doomcom->remotenode];
mysockaddr_t * addr = &clientaddress[node];
holepunchpacket->addr = addr->ip4.sin_addr.s_addr;
holepunchpacket->port = addr->ip4.sin_port;
@ -1435,30 +1507,116 @@ static boolean SOCK_OpenSocket(void)
#endif
}
static void AddBannedIndex(void)
{
if (numbans >= banned_size)
{
if (banned_size == 0)
{
banned_size = 8;
}
else
{
banned_size *= 2;
}
banned = Z_ReallocAlign(
(void*) banned,
sizeof(banned_t) * banned_size,
PU_STATIC,
NULL,
sizeof(banned_t) * 8
);
}
numbans++;
}
static boolean SOCK_Ban(INT32 node)
{
INT32 ban;
if (node > MAXNETNODES)
return false;
#ifdef NONET
(void)ban;
return false;
#else
if (numbans == MAXBANS)
return false;
M_Memcpy(&banned[numbans], &clientaddress[node], sizeof (mysockaddr_t));
if (banned[numbans].any.sa_family == AF_INET)
ban = numbans;
AddBannedIndex();
M_Memcpy(&banned[ban].address, &clientaddress[node], sizeof (mysockaddr_t));
if (banned[ban].address.any.sa_family == AF_INET)
{
banned[numbans].ip4.sin_port = 0;
bannedmask[numbans] = 32;
banned[ban].address.ip4.sin_port = 0;
banned[ban].mask = 32;
}
#ifdef HAVE_IPV6
else if (banned[numbans].any.sa_family == AF_INET6)
else if (banned[ban].address.any.sa_family == AF_INET6)
{
banned[numbans].ip6.sin6_port = 0;
bannedmask[numbans] = 128;
banned[ban].address.ip6.sin6_port = 0;
banned[ban].mask = 128;
}
#endif
numbans++;
return true;
#endif
}
static boolean SOCK_SetBanUsername(const char *username)
{
#ifdef NONET
(void)username;
return false;
#else
if (username == NULL || strlen(username) == 0)
{
username = "Direct IP ban";
}
if (banned[numbans - 1].username)
{
Z_Free(banned[numbans - 1].username);
banned[numbans - 1].username = NULL;
}
banned[numbans - 1].username = Z_StrDup(username);
return true;
#endif
}
static boolean SOCK_SetBanReason(const char *reason)
{
#ifdef NONET
(void)reason;
return false;
#else
if (reason == NULL || strlen(reason) == 0)
{
reason = "No reason given";
}
if (banned[numbans - 1].reason)
{
Z_Free(banned[numbans - 1].reason);
banned[numbans - 1].reason = NULL;
}
banned[numbans - 1].reason = Z_StrDup(reason);
return true;
#endif
}
static boolean SOCK_SetUnbanTime(time_t timestamp)
{
#ifdef NONET
(void)reason;
return false;
#else
banned[numbans - 1].timestamp = timestamp;
return true;
#endif
}
@ -1473,7 +1631,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
struct my_addrinfo *ai, *runp, hints;
int gaie;
if (numbans == MAXBANS || !address)
if (!address)
return false;
memset(&hints, 0x00, sizeof(hints));
@ -1488,26 +1646,42 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
runp = ai;
while(runp != NULL && numbans != MAXBANS)
while (runp != NULL)
{
memcpy(&banned[numbans], runp->ai_addr, runp->ai_addrlen);
INT32 ban;
UINT8 numericalmask;
ban = numbans;
AddBannedIndex();
memcpy(&banned[ban].address, runp->ai_addr, runp->ai_addrlen);
#ifdef HAVE_IPV6
if (runp->ai_family == AF_INET6)
banned[ban].mask = 128;
else
#endif
banned[ban].mask = 32;
if (mask)
bannedmask[numbans] = (UINT8)atoi(mask);
#ifdef HAVE_IPV6
else if (runp->ai_family == AF_INET6)
bannedmask[numbans] = 128;
#endif
{
numericalmask = (UINT8)atoi(mask);
}
else
bannedmask[numbans] = 32;
{
numericalmask = 0;
}
if (numericalmask > 0 && numericalmask < banned[ban].mask)
{
banned[ban].mask = numericalmask;
}
// Set defaults, in case anything funny happens.
SOCK_SetBanUsername(NULL);
SOCK_SetBanReason(NULL);
SOCK_SetUnbanTime(NO_BAN_TIME);
if (bannedmask[numbans] > 32 && runp->ai_family == AF_INET)
bannedmask[numbans] = 32;
#ifdef HAVE_IPV6
else if (bannedmask[numbans] > 128 && runp->ai_family == AF_INET6)
bannedmask[numbans] = 128;
#endif
numbans++;
runp = runp->ai_next;
}
@ -1520,6 +1694,9 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
static void SOCK_ClearBans(void)
{
numbans = 0;
banned_size = 0;
Z_Free(banned);
banned = NULL;
}
boolean I_InitTcpNetwork(void)
@ -1614,7 +1791,13 @@ boolean I_InitTcpNetwork(void)
I_GetNodeAddress = SOCK_GetNodeAddress;
I_GetBanAddress = SOCK_GetBanAddress;
I_GetBanMask = SOCK_GetBanMask;
I_GetBanUsername = SOCK_GetBanUsername;
I_GetBanReason = SOCK_GetBanReason;
I_GetUnbanTime = SOCK_GetUnbanTime;
I_SetBanAddress = SOCK_SetBanAddress;
I_SetBanUsername = SOCK_SetBanUsername;
I_SetBanReason = SOCK_SetBanReason;
I_SetUnbanTime = SOCK_SetUnbanTime;
bannednode = SOCK_bannednode;
return ret;

View file

@ -573,6 +573,9 @@ char sprnames[NUMSPRITES + 1][5] =
"FLML", // Flame Shield speed lines
"FLMF", // Flame Shield flash
"HYUU", // Hyudoro
"GRWP", // Grow
"POHB", // Shrink Poh-Bee
"SHRG", // Shrink gun / laser
"SINK", // Kitchen Sink
"SITR", // Kitchen Sink Trail
"KBLN", // Battle Mode Bumper
@ -4314,6 +4317,13 @@ state_t states[NUMSTATES] =
{SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO
{SPR_GRWP, FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE
{SPR_SHRG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_GUN
{SPR_POHB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_CHAIN
{SPR_SHRG, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_LASER
{SPR_SHRG, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_PARTICLE
{SPR_SINK, 0, 1, {A_SmokeTrailer}, MT_SINKTRAIL, 0, S_SINK}, // S_SINK
{SPR_SINK, 0|FF_TRANS80|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_SINK_SHIELD}, // S_SINK_SHIELD
{SPR_SITR, 0, 1, {NULL}, 0, 0, S_SINKTRAIL2}, // S_SINKTRAIL1
@ -5120,6 +5130,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_RAY
-1, // doomednum
S_NULL, // spawnstate
0, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
0, // radius
0, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_UNKNOWN
-1, // doomednum
S_UNKNOWN, // spawnstate
@ -24009,6 +24046,168 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_GROW_PARTICLE
-1, // doomednum
S_GROW_PARTICLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
4*FRACUNIT, // radius
8*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_SHRINK_POHBEE
-1, // doomednum
S_HYUDORO, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
32*FRACUNIT, // radius
24*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_SHRINK_GUN
-1, // doomednum
S_SHRINK_GUN, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
52*FRACUNIT, // radius
120*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_SHRINK_CHAIN
-1, // doomednum
S_SHRINK_CHAIN, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
26*FRACUNIT, // radius
26*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_SHRINK_LASER
-1, // doomednum
S_SHRINK_LASER, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16*FRACUNIT, // radius
33*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_SHRINK_PARTICLE
-1, // doomednum
S_SHRINK_PARTICLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
26*FRACUNIT, // radius
26*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_SINK
-1, // doomednum
S_SINK, // spawnstate
@ -24017,7 +24216,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_tossed, // seesound
8, // reactiontime
sfx_None, // attacksound
256*FRACUNIT, // painstate
S_NULL, // painstate
100, // painchance
sfx_None, // painsound
S_NULL, // meleestate

View file

@ -1119,6 +1119,9 @@ typedef enum sprite
SPR_FLML, // Flame Shield speed lines
SPR_FLMF, // Flame Shield flash
SPR_HYUU, // Hyudoro
SPR_GRWP, // Grow
SPR_POHB, // Shrink Poh-Bee
SPR_SHRG, // Shrink gun / laser
SPR_SINK, // Kitchen Sink
SPR_SITR, // Kitchen Sink Trail
SPR_KBLN, // Battle Mode Bumper
@ -4745,6 +4748,15 @@ typedef enum state
// Caked-Up Booty-Sheet Ghost
S_HYUDORO,
// Grow
S_GROW_PARTICLE,
// Shrink
S_SHRINK_GUN,
S_SHRINK_CHAIN,
S_SHRINK_LASER,
S_SHRINK_PARTICLE,
// The legend
S_SINK,
S_SINK_SHIELD,
@ -5532,6 +5544,7 @@ extern playersprite_t free_spr2;
typedef enum mobj_type
{
MT_NULL,
MT_RAY, // General purpose mobj
MT_UNKNOWN,
MT_THOK, // Thok! mobj
@ -6362,6 +6375,14 @@ typedef enum mobj_type
MT_HYUDORO,
MT_HYUDORO_CENTER,
MT_GROW_PARTICLE,
MT_SHRINK_POHBEE,
MT_SHRINK_GUN,
MT_SHRINK_CHAIN,
MT_SHRINK_LASER,
MT_SHRINK_PARTICLE,
MT_SINK, // Kitchen Sink Stuff
MT_SINK_SHIELD,
MT_SINKTRAIL,

View file

@ -298,7 +298,7 @@ boolean K_BotCanTakeCut(player_t *player)
/*--------------------------------------------------
static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed)
Gets the bot's speed value, adjusted for predictions.
What the bot "thinks" their speed is, for predictions.
Mainly to make bots brake earlier when on friction sectors.
Input Arguments:-
@ -312,6 +312,12 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed)
{
fixed_t result = speed;
if (P_IsObjectOnGround(player->mo) == false)
{
// You have no air control, so don't predict too far ahead.
return 0;
}
if (player->mo->movefactor != FRACUNIT)
{
fixed_t moveFactor = player->mo->movefactor;
@ -650,7 +656,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
const fixed_t speed = K_BotSpeedScaled(player, P_AproxDistance(player->mo->momx, player->mo->momy));
const INT32 startDist = (DEFAULT_WAYPOINT_RADIUS * 2 * mapobjectscale) / FRACUNIT;
const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist;
const INT32 maxDist = startDist * 4; // This function gets very laggy when it goes far distances, and going too far isn't very helpful anyway.
const INT32 distance = min(((speed / FRACUNIT) * (INT32)futuresight) + startDist, maxDist);
// Halves radius when encountering a wall on your way to your destination.
fixed_t radreduce = FRACUNIT;
@ -660,7 +667,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
angle_t angletonext = ANGLE_MAX;
INT32 disttonext = INT32_MAX;
waypoint_t *finishLine = K_GetFinishLineWaypoint();
waypoint_t *wp = player->nextwaypoint;
mobj_t *prevwpmobj = player->mo;
@ -676,8 +682,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y);
disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT;
pathfindsuccess = K_PathfindToWaypoint(
player->nextwaypoint, finishLine,
pathfindsuccess = K_PathfindThruCircuit(
player->nextwaypoint, (unsigned)distanceleft,
&pathtofinish,
useshortcuts, huntbackwards
);
@ -720,35 +726,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
// We're done!!
break;
}
if (i == pathtofinish.numnodes-1 && disttonext > 0)
{
// We were pathfinding to the finish, but we want to go past it.
// Set up a new pathfind.
waypoint_t *next = NULL;
if (finishLine->numnextwaypoints == 0)
{
distanceleft = 0;
break;
}
// default to first one
next = wp->nextwaypoints[0];
pathfindsuccess = K_PathfindToWaypoint(
next, finishLine,
&pathtofinish,
useshortcuts, huntbackwards
);
if (pathfindsuccess == false)
{
distanceleft = 0;
break;
}
}
}
Z_Free(pathtofinish.array);
@ -1247,7 +1224,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
}
// Complete override of all ticcmd functionality
if (LUAh_BotTiccmd(player, cmd) == true)
if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)) == true)
{
return;
}

View file

@ -425,6 +425,16 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
case MT_BUBBLESHIELDTRAP:
K_AddDodgeObject(thing, side, 20);
break;
case MT_SHRINK_GUN:
if (thing->target == globalsmuggle.botmo)
{
K_AddAttackObject(thing, side, 20);
}
else
{
K_AddDodgeObject(thing, side, 20);
}
break;
case MT_RANDOMITEM:
if (anglediff >= 45)
{

View file

@ -75,7 +75,7 @@ static brightmapStorage_t *K_GetBrightmapStorageByTextureName(const char *checkN
{
brightmapStorage_t *bms = &brightmapStorage[i];
if (checkHash == bms->textureHash)
if (checkHash == bms->textureHash && !strncmp(checkName, bms->textureName, 8))
{
// Name matches.
return bms;
@ -119,8 +119,8 @@ static boolean K_BRIGHTLumpParser(UINT8 *data, size_t size)
if (bms == NULL)
{
bms = K_NewBrightmap();
strncpy(bms->textureName, tkn, 9);
bms->textureHash = quickncasehash(bms->textureName, 8);
strncpy(bms->textureName, tkn, 8);
bms->textureHash = quickncasehash(tkn, 8);
}
Z_Free(tkn);
@ -129,8 +129,8 @@ static boolean K_BRIGHTLumpParser(UINT8 *data, size_t size)
if (tkn && pos < size)
{
strncpy(bms->brightmapName, tkn, 9);
bms->brightmapHash = quickncasehash(bms->brightmapName, 8);
strncpy(bms->brightmapName, tkn, 8);
bms->brightmapHash = quickncasehash(tkn, 8);
}
else
{

View file

@ -23,11 +23,11 @@ typedef struct brightmapStorage_s
// Stores data for brightmap definitions,
// before putting them into texturebrightmaps.
char textureName[9]; // The texture's name.
UINT32 textureHash; // The texture name's hash.
char textureName[8]; // The texture's name.
UINT32 textureHash; // The texture name's hash.
char brightmapName[9]; // The brightmap's name.
UINT32 brightmapHash; // The brightmap name's hash.
char brightmapName[8]; // The brightmap's name.
UINT32 brightmapHash; // The brightmap name's hash.
} brightmapStorage_t;
/*--------------------------------------------------

View file

@ -248,6 +248,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartcomeback);
CV_RegisterVar(&cv_kartencore);
CV_RegisterVar(&cv_kartvoterulechanges);
CV_RegisterVar(&cv_kartgametypepreference);
CV_RegisterVar(&cv_kartspeedometer);
CV_RegisterVar(&cv_kartvoices);
CV_RegisterVar(&cv_kartbot);
@ -354,7 +355,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
//P-Odds 0 1 2 3 4 5 6 7
/*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker
/*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker
/*Invincibility*/ { 0, 0, 0, 0, 3, 4, 6, 9 }, // Invincibility
/*Invincibility*/ { 0, 0, 0, 0, 3, 4, 5, 7 }, // Invincibility
/*Banana*/ { 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana
/*Eggman Monitor*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor
/*Orbinaut*/ { 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut
@ -364,7 +365,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
/*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog
/*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb
/*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow
/*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink
/*Shrink*/ { 0, 0, 0, 0, 0, 1, 3, 2 }, // Shrink
/*Lightning Shield*/ { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield
/*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield
/*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield
@ -374,7 +375,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
/*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
/*Drop Target*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target
/*Sneaker x2*/ { 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2
/*Sneaker x3*/ { 0, 0, 0, 1, 6,10, 5, 0 }, // Sneaker x3
/*Sneaker x3*/ { 0, 0, 0, 1, 6, 9, 5, 0 }, // Sneaker x3
/*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3
/*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10
/*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3
@ -2283,6 +2284,77 @@ void K_SpawnInvincibilitySpeedLines(mobj_t *mo)
fast->destscale = 6*((mo->player->invincibilitytimer/TICRATE)*FRACUNIT)/8;
}
static void K_SpawnGrowShrinkParticles(mobj_t *mo, INT32 timer)
{
const boolean shrink = (timer < 0);
const INT32 maxTime = (10*TICRATE);
const INT32 noTime = (2*TICRATE);
INT32 spawnFreq = 1;
mobj_t *particle = NULL;
fixed_t particleScale = FRACUNIT;
fixed_t particleSpeed = 0;
spawnFreq = abs(timer);
if (spawnFreq < noTime)
{
return;
}
spawnFreq -= noTime;
if (spawnFreq > maxTime)
{
spawnFreq = maxTime;
}
spawnFreq = (maxTime - spawnFreq) / TICRATE / 4;
if (spawnFreq == 0)
{
spawnFreq++;
}
if (leveltime % spawnFreq != 0)
{
return;
}
particle = P_SpawnMobjFromMobj(
mo,
P_RandomRange(-32, 32) * FRACUNIT,
P_RandomRange(-32, 32) * FRACUNIT,
(P_RandomRange(0, 24) + (shrink ? 48 : 0)) * FRACUNIT,
MT_GROW_PARTICLE
);
P_SetTarget(&particle->target, mo);
particle->momx = mo->momx;
particle->momy = mo->momy;
particle->momz = P_GetMobjZMovement(mo);
K_MatchGenericExtraFlags(particle, mo);
particleScale = FixedMul((shrink ? SHRINK_PHYSICS_SCALE : GROW_PHYSICS_SCALE), mapobjectscale);
particleSpeed = mo->scale * 4 * P_MobjFlip(mo); // NOT particleScale
particle->destscale = particleScale;
P_SetScale(particle, particle->destscale);
if (shrink == true)
{
particle->color = SKINCOLOR_KETCHUP;
particle->momz -= particleSpeed;
particle->renderflags |= RF_VERTICALFLIP;
}
else
{
particle->color = SKINCOLOR_SAPPHIRE;
particle->momz += particleSpeed;
}
}
void K_SpawnBumpEffect(mobj_t *mo)
{
mobj_t *fx = P_SpawnMobj(mo->x, mo->y, mo->z, MT_BUMP);
@ -3642,7 +3714,7 @@ void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 typ
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
}
static void K_RemoveGrowShrink(player_t *player)
void K_RemoveGrowShrink(player_t *player)
{
if (player->mo && !P_MobjWasRemoved(player->mo))
{
@ -5581,46 +5653,12 @@ void K_DoSneaker(player_t *player, INT32 type)
static void K_DoShrink(player_t *user)
{
INT32 i;
mobj_t *mobj, *next;
S_StartSound(user->mo, sfx_kc46); // Sound the BANG!
user->pflags |= PF_ATTACKDOWN;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || !players[i].mo)
continue;
if (&players[i] == user)
continue;
if (players[i].position < user->position)
{
//P_FlashPal(&players[i], PAL_NUKE, 10);
// Grow should get taken away.
if (players[i].growshrinktimer > 0)
K_RemoveGrowShrink(&players[i]);
else
{
// Start shrinking!
K_DropItems(&players[i]);
players[i].growshrinktimer = -(15*TICRATE);
if (players[i].mo && !P_MobjWasRemoved(players[i].mo))
{
players[i].mo->scalespeed = mapobjectscale/TICRATE;
players[i].mo->destscale = FixedMul(mapobjectscale, SHRINK_SCALE);
if (K_PlayerShrinkCheat(&players[i]) == true)
{
players[i].mo->destscale = FixedMul(players[i].mo->destscale, SHRINK_SCALE);
}
S_StartSound(players[i].mo, sfx_kc59);
}
}
}
}
Obj_CreateShrinkPohbees(user);
// kill everything in the kitem list while we're at it:
for (mobj = kitemcap; mobj; mobj = next)
@ -6700,7 +6738,7 @@ static void K_MoveHeldObjects(player_t *player)
targz += (player->mo->height/2 - 32*player->mo->scale)*6;
}
if (cur->tracer)
if (cur->tracer && !P_MobjWasRemoved(cur->tracer))
{
fixed_t diffx, diffy, diffz;
@ -7358,6 +7396,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
}
}
if (player->growshrinktimer != 0)
{
K_SpawnGrowShrinkParticles(player->mo, player->growshrinktimer);
}
if (gametype == GT_RACE && player->rings <= 0) // spawn ring debt indicator
{
mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy,
@ -7522,6 +7565,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
comebackshowninfo = true; // client has already seen the message
}
if (player->shrinkLaserDelay)
player->shrinkLaserDelay--;
if (player->ringdelay)
player->ringdelay--;
@ -9483,7 +9529,7 @@ void K_AdjustPlayerFriction(player_t *player)
}
// Wipeout slowdown
if (player->spinouttimer && player->wipeoutslow)
if (player->speed > 0 && player->spinouttimer && player->wipeoutslow)
{
if (player->offroad)
player->mo->friction -= 4912;
@ -10018,7 +10064,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
// TODO: gametyperules
player->growshrinktimer = (gametype == GT_BATTLE ? 8 : 12) * TICRATE;
player->growshrinktimer = max(player->growshrinktimer, (gametype == GT_BATTLE ? 8 : 12) * TICRATE);
if (player->invincibilitytimer > 0)
{

View file

@ -71,6 +71,7 @@ void K_AwardPlayerRings(player_t *player, INT32 rings, boolean overload);
void K_DoInstashield(player_t *player);
void K_DoPowerClash(player_t *t1, player_t *t2);
void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved);
void K_RemoveGrowShrink(player_t *player);
void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type);
void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
void K_TumbleInterrupt(player_t *player);

View file

@ -8,4 +8,11 @@ void Obj_HyudoroThink(mobj_t *actor);
void Obj_HyudoroCenterThink(mobj_t *actor);
void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher);
/* Shrink */
void Obj_PohbeeThinker(mobj_t *pohbee);
void Obj_PohbeeRemoved(mobj_t *pohbee);
void Obj_ShrinkGunRemoved(mobj_t *gun);
boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim);
void Obj_CreateShrinkPohbees(player_t *owner);
#endif/*k_objects_H*/

View file

@ -190,6 +190,10 @@ static boolean K_PathfindSetupValid(const pathfindsetup_t *const pathfindsetup)
{
CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL gettraversable function.\n");
}
else if (pathfindsetup->getfinished == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL getfinished function.\n");
}
else if (pathfindsetup->getconnectednodes(pathfindsetup->startnodedata, &sourcenodenumconnectednodes) == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_PathfindSetupValid: Source node returned NULL connecting nodes.\n");
@ -295,242 +299,244 @@ boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup
{
CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: Pathfinding setup is not valid.\n");
}
else if (pathfindsetup->startnodedata == pathfindsetup->endnodedata)
else
{
// At the destination, return a simple 1 node path
pathfindnode_t singlenode = {0};
singlenode.camefrom = NULL;
singlenode.nodedata = pathfindsetup->endnodedata;
singlenode.nodedata = pathfindsetup->startnodedata;
singlenode.heapindex = SIZE_MAX;
singlenode.hscore = 0U;
singlenode.gscore = 0U;
K_ReconstructPath(path, &singlenode);
pathfindsuccess = true;
}
else
{
bheap_t openset = {0};
bheapitem_t poppedbheapitem = {0};
pathfindnode_t *nodesarray = NULL;
pathfindnode_t **closedset = NULL;
pathfindnode_t *newnode = NULL;
pathfindnode_t *currentnode = NULL;
pathfindnode_t *connectingnode = NULL;
void **connectingnodesdata = NULL;
void *checknodedata = NULL;
UINT32 *connectingnodecosts = NULL;
size_t numconnectingnodes = 0U;
size_t connectingnodeheapindex = 0U;
size_t nodesarraycount = 0U;
size_t closedsetcount = 0U;
size_t i = 0U;
UINT32 tentativegscore = 0U;
// Set the dynamic structure capacites to defaults if they are 0
if (pathfindsetup->nodesarraycapacity == 0U)
if (pathfindsetup->getfinished(&singlenode, pathfindsetup) == true)
{
pathfindsetup->nodesarraycapacity = DEFAULT_NODEARRAY_CAPACITY;
// At the destination, return a simple 1 node path
K_ReconstructPath(path, &singlenode);
pathfindsuccess = true;
}
if (pathfindsetup->opensetcapacity == 0U)
else
{
pathfindsetup->opensetcapacity = DEFAULT_OPENSET_CAPACITY;
}
if (pathfindsetup->closedsetcapacity == 0U)
{
pathfindsetup->closedsetcapacity = DEFAULT_CLOSEDSET_CAPACITY;
}
bheap_t openset = {0};
bheapitem_t poppedbheapitem = {0};
pathfindnode_t *nodesarray = NULL;
pathfindnode_t **closedset = NULL;
pathfindnode_t *newnode = NULL;
pathfindnode_t *currentnode = NULL;
pathfindnode_t *connectingnode = NULL;
void **connectingnodesdata = NULL;
void *checknodedata = NULL;
UINT32 *connectingnodecosts = NULL;
size_t numconnectingnodes = 0U;
size_t connectingnodeheapindex = 0U;
size_t nodesarraycount = 0U;
size_t closedsetcount = 0U;
size_t i = 0U;
UINT32 tentativegscore = 0U;
// Allocate the necessary memory
nodesarray = Z_Calloc(pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL);
if (nodesarray == NULL)
{
I_Error("K_PathfindAStar: Out of memory allocating nodes array.");
}
closedset = Z_Calloc(pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL);
if (closedset == NULL)
{
I_Error("K_PathfindAStar: Out of memory allocating closed set.");
}
K_BHeapInit(&openset, pathfindsetup->opensetcapacity);
// Create the first node and add it to the open set
newnode = &nodesarray[nodesarraycount];
newnode->heapindex = SIZE_MAX;
newnode->nodedata = pathfindsetup->startnodedata;
newnode->camefrom = NULL;
newnode->gscore = 0U;
newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata);
nodesarraycount++;
K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex);
// update openset capacity if it changed
if (openset.capacity != pathfindsetup->opensetcapacity)
{
pathfindsetup->opensetcapacity = openset.capacity;
}
// Go through each node in the openset, adding new ones from each node to it
// this continues until a path is found or there are no more nodes to check
while (openset.count > 0U)
{
// pop the best node off of the openset
K_BHeapPop(&openset, &poppedbheapitem);
currentnode = (pathfindnode_t*)poppedbheapitem.data;
if (currentnode->nodedata == pathfindsetup->endnodedata)
// Set the dynamic structure capacites to defaults if they are 0
if (pathfindsetup->nodesarraycapacity == 0U)
{
pathfindsuccess = K_ReconstructPath(path, currentnode);
break;
pathfindsetup->nodesarraycapacity = DEFAULT_NODEARRAY_CAPACITY;
}
if (pathfindsetup->opensetcapacity == 0U)
{
pathfindsetup->opensetcapacity = DEFAULT_OPENSET_CAPACITY;
}
if (pathfindsetup->closedsetcapacity == 0U)
{
pathfindsetup->closedsetcapacity = DEFAULT_CLOSEDSET_CAPACITY;
}
// Place the node we just popped into the closed set, as we are now evaluating it
if (closedsetcount >= pathfindsetup->closedsetcapacity)
// Allocate the necessary memory
nodesarray = Z_Calloc(pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL);
if (nodesarray == NULL)
{
// Need to reallocate closedset to fit another node
pathfindsetup->closedsetcapacity = pathfindsetup->closedsetcapacity * 2;
closedset =
Z_Realloc(closedset, pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL);
if (closedset == NULL)
I_Error("K_PathfindAStar: Out of memory allocating nodes array.");
}
closedset = Z_Calloc(pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL);
if (closedset == NULL)
{
I_Error("K_PathfindAStar: Out of memory allocating closed set.");
}
K_BHeapInit(&openset, pathfindsetup->opensetcapacity);
// Create the first node and add it to the open set
newnode = &nodesarray[nodesarraycount];
newnode->heapindex = SIZE_MAX;
newnode->nodedata = pathfindsetup->startnodedata;
newnode->camefrom = NULL;
newnode->gscore = 0U;
newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata);
nodesarraycount++;
K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex);
// update openset capacity if it changed
if (openset.capacity != pathfindsetup->opensetcapacity)
{
pathfindsetup->opensetcapacity = openset.capacity;
}
// Go through each node in the openset, adding new ones from each node to it
// this continues until a path is found or there are no more nodes to check
while (openset.count > 0U)
{
// pop the best node off of the openset
K_BHeapPop(&openset, &poppedbheapitem);
currentnode = (pathfindnode_t*)poppedbheapitem.data;
if (pathfindsetup->getfinished(currentnode, pathfindsetup) == true)
{
I_Error("K_PathfindAStar: Out of memory reallocating closed set.");
pathfindsuccess = K_ReconstructPath(path, currentnode);
break;
}
}
closedset[closedsetcount] = currentnode;
closedsetcount++;
// Get the needed data for the next nodes from the current node
connectingnodesdata = pathfindsetup->getconnectednodes(currentnode->nodedata, &numconnectingnodes);
connectingnodecosts = pathfindsetup->getconnectioncosts(currentnode->nodedata);
if (connectingnodesdata == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node data.\n");
}
else if (connectingnodecosts == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node costs.\n");
}
else
{
// For each connecting node add it to the openset if it's unevaluated and not there,
// skip it if it's in the closedset or not traversable
for (i = 0; i < numconnectingnodes; i++)
// Place the node we just popped into the closed set, as we are now evaluating it
if (closedsetcount >= pathfindsetup->closedsetcapacity)
{
checknodedata = connectingnodesdata[i];
if (checknodedata == NULL)
// Need to reallocate closedset to fit another node
pathfindsetup->closedsetcapacity = pathfindsetup->closedsetcapacity * 2;
closedset =
Z_Realloc(closedset, pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL);
if (closedset == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node has a NULL connecting node.\n");
I_Error("K_PathfindAStar: Out of memory reallocating closed set.");
}
else
}
closedset[closedsetcount] = currentnode;
closedsetcount++;
// Get the needed data for the next nodes from the current node
connectingnodesdata = pathfindsetup->getconnectednodes(currentnode->nodedata, &numconnectingnodes);
connectingnodecosts = pathfindsetup->getconnectioncosts(currentnode->nodedata);
if (connectingnodesdata == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node data.\n");
}
else if (connectingnodecosts == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node costs.\n");
}
else
{
// For each connecting node add it to the openset if it's unevaluated and not there,
// skip it if it's in the closedset or not traversable
for (i = 0; i < numconnectingnodes; i++)
{
// skip this node if it isn't traversable
if (pathfindsetup->gettraversable(checknodedata, currentnode->nodedata) == false)
checknodedata = connectingnodesdata[i];
if (checknodedata == NULL)
{
continue;
}
// Figure out what the gscore of this route for the connecting node is
tentativegscore = currentnode->gscore + connectingnodecosts[i];
// find this data in the nodes array if it's been generated before
connectingnode = K_NodesArrayContainsNodeData(nodesarray, checknodedata, nodesarraycount);
if (connectingnode != NULL)
{
// The connecting node has been seen before, so it must be in either the closedset (skip it)
// or the openset (re-evaluate it's gscore)
if (K_ClosedsetContainsNode(closedset, connectingnode, closedsetcount) == true)
{
continue;
}
else if (tentativegscore < connectingnode->gscore)
{
// The node is not in the closedset, update it's gscore if this path to it is faster
connectingnode->gscore = tentativegscore;
connectingnode->camefrom = currentnode;
connectingnodeheapindex =
K_BHeapContains(&openset, connectingnode, connectingnode->heapindex);
if (connectingnodeheapindex != SIZE_MAX)
{
K_UpdateBHeapItemValue(
&openset.array[connectingnodeheapindex], K_NodeGetFScore(connectingnode));
}
else
{
// SOMEHOW the node is not in either the closed set OR the open set
CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node is not in either set.\n");
}
}
CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node has a NULL connecting node.\n");
}
else
{
// Node is not created yet, so it hasn't been seen so far
// Reallocate nodesarray if it's full
if (nodesarraycount >= pathfindsetup->nodesarraycapacity)
// skip this node if it isn't traversable
if (pathfindsetup->gettraversable(checknodedata, currentnode->nodedata) == false)
{
pathfindnode_t *nodesarrayrealloc = NULL;
pathfindsetup->nodesarraycapacity = pathfindsetup->nodesarraycapacity * 2;
nodesarrayrealloc = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL);
if (nodesarrayrealloc == NULL)
{
I_Error("K_PathfindAStar: Out of memory reallocating nodes array.");
}
// Need to update pointers in closedset, openset, and node "camefrom" if nodesarray moved.
if (nodesarray != nodesarrayrealloc)
{
size_t j = 0U;
size_t arrayindex = 0U;
for (j = 0U; j < closedsetcount; j++)
{
arrayindex = closedset[j] - nodesarray;
closedset[j] = &nodesarrayrealloc[arrayindex];
}
for (j = 0U; j < openset.count; j++)
{
arrayindex = ((pathfindnode_t *)(openset.array[j].data)) - nodesarray;
openset.array[j].data = &nodesarrayrealloc[arrayindex];
}
for (j = 0U; j < nodesarraycount; j++)
{
if (nodesarrayrealloc[j].camefrom != NULL)
{
arrayindex = nodesarrayrealloc[j].camefrom - nodesarray;
nodesarrayrealloc[j].camefrom = &nodesarrayrealloc[arrayindex];
}
}
arrayindex = currentnode - nodesarray;
currentnode = &nodesarrayrealloc[arrayindex];
}
nodesarray = nodesarrayrealloc;
continue;
}
// Create the new node and add it to the nodes array and open set
newnode = &nodesarray[nodesarraycount];
newnode->heapindex = SIZE_MAX;
newnode->nodedata = checknodedata;
newnode->camefrom = currentnode;
newnode->gscore = tentativegscore;
newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata);
nodesarraycount++;
K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex);
// Figure out what the gscore of this route for the connecting node is
tentativegscore = currentnode->gscore + connectingnodecosts[i];
// find this data in the nodes array if it's been generated before
connectingnode = K_NodesArrayContainsNodeData(nodesarray, checknodedata, nodesarraycount);
if (connectingnode != NULL)
{
// The connecting node has been seen before, so it must be in either the closedset (skip it)
// or the openset (re-evaluate it's gscore)
if (K_ClosedsetContainsNode(closedset, connectingnode, closedsetcount) == true)
{
continue;
}
else if (tentativegscore < connectingnode->gscore)
{
// The node is not in the closedset, update it's gscore if this path to it is faster
connectingnode->gscore = tentativegscore;
connectingnode->camefrom = currentnode;
connectingnodeheapindex =
K_BHeapContains(&openset, connectingnode, connectingnode->heapindex);
if (connectingnodeheapindex != SIZE_MAX)
{
K_UpdateBHeapItemValue(
&openset.array[connectingnodeheapindex], K_NodeGetFScore(connectingnode));
}
else
{
// SOMEHOW the node is not in either the closed set OR the open set
CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node is not in either set.\n");
}
}
}
else
{
// Node is not created yet, so it hasn't been seen so far
// Reallocate nodesarray if it's full
if (nodesarraycount >= pathfindsetup->nodesarraycapacity)
{
pathfindnode_t *nodesarrayrealloc = NULL;
pathfindsetup->nodesarraycapacity = pathfindsetup->nodesarraycapacity * 2;
nodesarrayrealloc = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL);
if (nodesarrayrealloc == NULL)
{
I_Error("K_PathfindAStar: Out of memory reallocating nodes array.");
}
// Need to update pointers in closedset, openset, and node "camefrom" if nodesarray moved.
if (nodesarray != nodesarrayrealloc)
{
size_t j = 0U;
size_t arrayindex = 0U;
for (j = 0U; j < closedsetcount; j++)
{
arrayindex = closedset[j] - nodesarray;
closedset[j] = &nodesarrayrealloc[arrayindex];
}
for (j = 0U; j < openset.count; j++)
{
arrayindex = ((pathfindnode_t *)(openset.array[j].data)) - nodesarray;
openset.array[j].data = &nodesarrayrealloc[arrayindex];
}
for (j = 0U; j < nodesarraycount; j++)
{
if (nodesarrayrealloc[j].camefrom != NULL)
{
arrayindex = nodesarrayrealloc[j].camefrom - nodesarray;
nodesarrayrealloc[j].camefrom = &nodesarrayrealloc[arrayindex];
}
}
arrayindex = currentnode - nodesarray;
currentnode = &nodesarrayrealloc[arrayindex];
}
nodesarray = nodesarrayrealloc;
}
// Create the new node and add it to the nodes array and open set
newnode = &nodesarray[nodesarraycount];
newnode->heapindex = SIZE_MAX;
newnode->nodedata = checknodedata;
newnode->camefrom = currentnode;
newnode->gscore = tentativegscore;
newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata);
nodesarraycount++;
K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex);
}
}
}
}
}
}
// Clean up the memory
K_BHeapFree(&openset);
Z_Free(closedset);
Z_Free(nodesarray);
// Clean up the memory
K_BHeapFree(&openset);
Z_Free(closedset);
Z_Free(nodesarray);
}
}
return pathfindsuccess;

View file

@ -29,6 +29,9 @@ typedef UINT32(*getnodeheuristicfunc)(void*, void*);
// function pointer for getting if a node is traversable from its base data
typedef boolean(*getnodetraversablefunc)(void*, void*);
// function pointer for getting if a node is our pathfinding end point
typedef boolean(*getpathfindfinishedfunc)(void*, void*);
// A pathfindnode contains information about a node from the pathfinding
// heapindex is only used within the pathfinding algorithm itself, and is always 0 after it is completed
@ -58,10 +61,12 @@ typedef struct pathfindsetup_s {
size_t nodesarraycapacity;
void *startnodedata;
void *endnodedata;
UINT32 endgscore;
getconnectednodesfunc getconnectednodes;
getnodeconnectioncostsfunc getconnectioncosts;
getnodeheuristicfunc getheuristic;
getnodetraversablefunc gettraversable;
getpathfindfinishedfunc getfinished;
} pathfindsetup_t;

View file

@ -57,16 +57,8 @@ SINT8 K_UsingPowerLevels(void)
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;
}
}
memset(clientpowerlevels, 0, sizeof clientpowerlevels);
memset(clientPowerAdd, 0, sizeof clientPowerAdd);
}
// Adapted from this: http://wiki.tockdom.com/wiki/Player_Rating

View file

@ -91,6 +91,7 @@ t_splash_t *K_GetSplashByIndex(size_t checkIndex)
--------------------------------------------------*/
t_splash_t *K_GetSplashByName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN);
size_t i;
if (numSplashDefs == 0)
@ -102,7 +103,7 @@ t_splash_t *K_GetSplashByName(const char *checkName)
{
t_splash_t *s = &splashDefs[i];
if (stricmp(checkName, s->name) == 0)
if (checkHash == s->hash && !strncmp(checkName, s->name, TERRAIN_NAME_LEN))
{
// Name matches.
return s;
@ -159,6 +160,7 @@ t_footstep_t *K_GetFootstepByIndex(size_t checkIndex)
--------------------------------------------------*/
t_footstep_t *K_GetFootstepByName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN);
size_t i;
if (numFootstepDefs == 0)
@ -170,7 +172,7 @@ t_footstep_t *K_GetFootstepByName(const char *checkName)
{
t_footstep_t *fs = &footstepDefs[i];
if (stricmp(checkName, fs->name) == 0)
if (checkHash == fs->hash && !strncmp(checkName, fs->name, TERRAIN_NAME_LEN))
{
// Name matches.
return fs;
@ -227,21 +229,20 @@ terrain_t *K_GetTerrainByIndex(size_t checkIndex)
--------------------------------------------------*/
terrain_t *K_GetTerrainByName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN);
size_t i;
if (numTerrainDefs == 0)
if (numTerrainDefs > 0)
{
return NULL;
}
for (i = 0; i < numTerrainDefs; i++)
{
terrain_t *t = &terrainDefs[i];
if (stricmp(checkName, t->name) == 0)
for (i = 0; i < numTerrainDefs; i++)
{
// Name matches.
return t;
terrain_t *t = &terrainDefs[i];
if (checkHash == t->hash && !strncmp(checkName, t->name, TERRAIN_NAME_LEN))
{
// Name matches.
return t;
}
}
}
@ -265,20 +266,19 @@ terrain_t *K_GetDefaultTerrain(void)
--------------------------------------------------*/
terrain_t *K_GetTerrainForTextureName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, 8);
size_t i;
if (numTerrainFloorDefs == 0)
if (numTerrainFloorDefs > 0)
{
return NULL;
}
for (i = 0; i < numTerrainFloorDefs; i++)
{
t_floor_t *f = &terrainFloorDefs[i];
if (strncasecmp(checkName, f->textureName, 8) == 0)
for (i = 0; i < numTerrainFloorDefs; i++)
{
return K_GetTerrainByIndex(f->terrainID);
t_floor_t *f = &terrainFloorDefs[i];
if (checkHash == f->textureHash && !strncmp(checkName, f->textureName, 8))
{
return K_GetTerrainByIndex(f->terrainID);
}
}
}
@ -294,15 +294,15 @@ terrain_t *K_GetTerrainForTextureName(const char *checkName)
--------------------------------------------------*/
terrain_t *K_GetTerrainForTextureNum(INT32 textureNum)
{
texture_t *tex = NULL;
if (textureNum < 0 || textureNum >= numtextures)
if (textureNum >= 0 && textureNum < numtextures)
{
return NULL;
texture_t *tex = textures[textureNum];
return K_GetTerrainForTextureName(tex->name);
}
tex = textures[textureNum];
return K_GetTerrainForTextureName(tex->name);
// This texture doesn't have a terrain directly applied to it,
// so we fallback to the default terrain.
return K_GetDefaultTerrain();
}
/*--------------------------------------------------
@ -1187,6 +1187,7 @@ static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(size_t, char *, c
static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
{
char *tkn = M_GetToken((char *)data);
UINT32 tknHash = 0;
size_t pos = 0;
size_t i;
@ -1211,11 +1212,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
{
t_splash_t *s = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numSplashDefs; i++)
{
s = &splashDefs[i];
if (stricmp(tkn, s->name) == 0)
if (tknHash == s->hash && !strncmp(tkn, s->name, TERRAIN_NAME_LEN))
{
break;
}
@ -1227,6 +1230,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
s = &splashDefs[i];
strncpy(s->name, tkn, TERRAIN_NAME_LEN);
s->hash = tknHash;
CONS_Printf("Created new Splash type '%s'\n", s->name);
}
@ -1248,11 +1253,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
{
t_footstep_t *fs = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numFootstepDefs; i++)
{
fs = &footstepDefs[i];
if (stricmp(tkn, fs->name) == 0)
if (tknHash == fs->hash && !strncmp(tkn, fs->name, TERRAIN_NAME_LEN))
{
break;
}
@ -1264,6 +1271,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
fs = &footstepDefs[i];
strncpy(fs->name, tkn, TERRAIN_NAME_LEN);
fs->hash = tknHash;
CONS_Printf("Created new Footstep type '%s'\n", fs->name);
}
@ -1285,11 +1294,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
{
terrain_t *t = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numTerrainDefs; i++)
{
t = &terrainDefs[i];
if (stricmp(tkn, t->name) == 0)
if (tknHash == t->hash && !strncmp(tkn, t->name, TERRAIN_NAME_LEN))
{
break;
}
@ -1301,6 +1312,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
t = &terrainDefs[i];
strncpy(t->name, tkn, TERRAIN_NAME_LEN);
t->hash = tknHash;
CONS_Printf("Created new Terrain type '%s'\n", t->name);
}
@ -1333,11 +1346,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
{
t_floor_t *f = NULL;
tknHash = quickncasehash(tkn, 8);
for (i = 0; i < numTerrainFloorDefs; i++)
{
f = &terrainFloorDefs[i];
if (stricmp(tkn, f->textureName) == 0)
if (f->textureHash == tknHash && !strncmp(tkn, f->textureName, 8))
{
break;
}
@ -1348,7 +1363,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
K_NewTerrainFloorDefs();
f = &terrainFloorDefs[i];
strncpy(f->textureName, tkn, 9);
strncpy(f->textureName, tkn, 8);
f->textureHash = tknHash;
}
Z_Free(tkn);
@ -1398,11 +1414,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
{
terrain_t *t = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numTerrainDefs; i++)
{
t = &terrainDefs[i];
if (stricmp(tkn, t->name) == 0)
if (tknHash == t->hash && !strncmp(tkn, t->name, TERRAIN_NAME_LEN))
{
break;
}
@ -1435,11 +1453,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size)
{
t_footstep_t *fs = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numFootstepDefs; i++)
{
fs = &footstepDefs[i];
if (stricmp(tkn, fs->name) == 0)
if (tknHash == fs->hash && !strncmp(tkn, fs->name, TERRAIN_NAME_LEN))
{
break;
}

View file

@ -28,6 +28,7 @@ typedef struct t_splash_s
// These are particles spawned when hitting the floor.
char name[TERRAIN_NAME_LEN]; // Lookup name.
UINT32 hash; // Lookup name's hash.
UINT16 mobjType; // Thing type. MT_NULL to not spawn anything.
UINT16 sfx; // Sound to play.
@ -48,6 +49,7 @@ typedef struct t_footstep_s
// These are particles spawned when moving fast enough on a floor.
char name[TERRAIN_NAME_LEN]; // Lookup name.
UINT32 hash; // Lookup name's hash.
UINT16 mobjType; // Thing type. MT_NULL to not spawn anything.
UINT16 sfx; // Sound to play.
@ -79,6 +81,7 @@ typedef struct terrain_s
// These are all of the properties that the floor gets.
char name[TERRAIN_NAME_LEN]; // Lookup name.
UINT32 hash; // Lookup name's hash.
size_t splashID; // Splash defintion ID.
size_t footstepID; // Footstep defintion ID.
@ -93,16 +96,14 @@ typedef struct terrain_s
typedef struct t_floor_s
{
// Terrain floor definition.
// Ties texture names to a .
// Ties a texture name to a terrain definition.
// (Could be optimized by using texture IDs instead of names,
// but was concerned because I recall sooomething about those not being netsafe?
// Someone confirm if I just hallucinated that. :V)
char textureName[9]; // Floor texture name.
char textureName[8]; // Floor texture name.
UINT32 textureHash; // Floor texture hash.
size_t terrainID; // Terrain definition ID.
} t_floor_t;
/*--------------------------------------------------
size_t K_GetSplashHeapIndex(t_splash_t *splash);
@ -285,6 +286,7 @@ terrain_t *K_GetTerrainByIndex(size_t checkIndex);
terrain_t *K_GetTerrainByName(const char *checkName);
/*--------------------------------------------------
terrain_t *K_GetDefaultTerrain(void);

View file

@ -1039,6 +1039,102 @@ static boolean K_WaypointPathfindTraversableNoShortcuts(void *data, void *prevda
return traversable;
}
/*--------------------------------------------------
static boolean K_WaypointPathfindReachedEnd(void *data, void *setupData)
Returns if the current waypoint data is our end point of our pathfinding.
Input Arguments:-
data - Should point to a pathfindnode_t to compare
setupData - Should point to the pathfindsetup_t to compare
Return:-
True if the waypoint is the pathfind end point, false otherwise.
--------------------------------------------------*/
static boolean K_WaypointPathfindReachedEnd(void *data, void *setupData)
{
boolean isEnd = false;
if (data == NULL || setupData == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindReachedEnd received NULL data.\n");
}
else
{
pathfindnode_t *node = (pathfindnode_t *)data;
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
isEnd = (node->nodedata == setup->endnodedata);
}
return isEnd;
}
/*--------------------------------------------------
static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData)
Returns if the current waypoint data reaches our end G score.
Input Arguments:-
data - Should point to a pathfindnode_t to compare
setupData - Should point to the pathfindsetup_t to compare
Return:-
True if the waypoint reached the G score, false otherwise.
--------------------------------------------------*/
static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData)
{
boolean scoreReached = false;
if (data == NULL || setupData == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindReachedGScore received NULL data.\n");
}
else
{
pathfindnode_t *node = (pathfindnode_t *)data;
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
scoreReached = (node->gscore >= setup->endgscore);
}
return scoreReached;
}
/*--------------------------------------------------
static boolean K_WaypointPathfindReachedGScoreSpawnable(void *data, void *setupData)
Returns if the current waypoint data reaches our end G score.
Input Arguments:-
data - Should point to a pathfindnode_t to compare
setupData - Should point to the pathfindsetup_t to compare
Return:-
True if the waypoint reached the G score, false otherwise.
--------------------------------------------------*/
static boolean K_WaypointPathfindReachedGScoreSpawnable(void *data, void *setupData)
{
boolean scoreReached = false;
boolean spawnable = false;
if (data == NULL || setupData == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindReachedGScoreSpawnable received NULL data.\n");
}
else
{
pathfindnode_t *node = (pathfindnode_t *)data;
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
waypoint_t *wp = (waypoint_t *)node->nodedata;
scoreReached = (node->gscore >= setup->endgscore);
spawnable = K_GetWaypointIsSpawnpoint(wp);
}
return (scoreReached && spawnable);
}
/*--------------------------------------------------
boolean K_PathfindToWaypoint(
waypoint_t *const sourcewaypoint,
@ -1087,18 +1183,19 @@ boolean K_PathfindToWaypoint(
getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts;
getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic;
getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts;
getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedEnd;
if (huntbackwards)
{
nextnodesfunc = K_WaypointPathfindGetPrev;
nodecostsfunc = K_WaypointPathfindGetPrevCosts;
}
if (useshortcuts)
{
traversablefunc = K_WaypointPathfindTraversableAllEnabled;
}
pathfindsetup.opensetcapacity = K_GetOpensetBaseSize();
pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize();
pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize();
@ -1108,6 +1205,173 @@ boolean K_PathfindToWaypoint(
pathfindsetup.getconnectioncosts = nodecostsfunc;
pathfindsetup.getheuristic = heuristicfunc;
pathfindsetup.gettraversable = traversablefunc;
pathfindsetup.getfinished = finishedfunc;
pathfound = K_PathfindAStar(returnpath, &pathfindsetup);
K_UpdateOpensetBaseSize(pathfindsetup.opensetcapacity);
K_UpdateClosedsetBaseSize(pathfindsetup.closedsetcapacity);
K_UpdateNodesArrayBaseSize(pathfindsetup.nodesarraycapacity);
}
return pathfound;
}
/*--------------------------------------------------
boolean K_PathfindThruCircuit(
waypoint_t *const sourcewaypoint,
const UINT32 traveldistance,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards)
See header file for description.
--------------------------------------------------*/
boolean K_PathfindThruCircuit(
waypoint_t *const sourcewaypoint,
const UINT32 traveldistance,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards)
{
boolean pathfound = false;
if (sourcewaypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_PathfindThruCircuit.\n");
}
else if (finishline == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL finishline in K_PathfindThruCircuit.\n");
}
else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0))
|| ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0)))
{
CONS_Debug(DBG_GAMELOGIC,
"K_PathfindThruCircuit: sourcewaypoint with ID %d has no next waypoint\n",
K_GetWaypointID(sourcewaypoint));
}
else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0))
|| ((huntbackwards == true) && (finishline->numnextwaypoints == 0)))
{
CONS_Debug(DBG_GAMELOGIC,
"K_PathfindThruCircuit: finishline with ID %d has no previous waypoint\n",
K_GetWaypointID(finishline));
}
else
{
pathfindsetup_t pathfindsetup = {0};
getconnectednodesfunc nextnodesfunc = K_WaypointPathfindGetNext;
getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts;
getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic;
getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts;
getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedGScore;
if (huntbackwards)
{
nextnodesfunc = K_WaypointPathfindGetPrev;
nodecostsfunc = K_WaypointPathfindGetPrevCosts;
}
if (useshortcuts)
{
traversablefunc = K_WaypointPathfindTraversableAllEnabled;
}
pathfindsetup.opensetcapacity = K_GetOpensetBaseSize();
pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize();
pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize();
pathfindsetup.startnodedata = sourcewaypoint;
pathfindsetup.endnodedata = finishline;
pathfindsetup.endgscore = traveldistance;
pathfindsetup.getconnectednodes = nextnodesfunc;
pathfindsetup.getconnectioncosts = nodecostsfunc;
pathfindsetup.getheuristic = heuristicfunc;
pathfindsetup.gettraversable = traversablefunc;
pathfindsetup.getfinished = finishedfunc;
pathfound = K_PathfindAStar(returnpath, &pathfindsetup);
K_UpdateOpensetBaseSize(pathfindsetup.opensetcapacity);
K_UpdateClosedsetBaseSize(pathfindsetup.closedsetcapacity);
K_UpdateNodesArrayBaseSize(pathfindsetup.nodesarraycapacity);
}
return pathfound;
}
/*--------------------------------------------------
boolean K_PathfindThruCircuitSpawnable(
waypoint_t *const sourcewaypoint,
const UINT32 traveldistance,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards)
See header file for description.
--------------------------------------------------*/
boolean K_PathfindThruCircuitSpawnable(
waypoint_t *const sourcewaypoint,
const UINT32 traveldistance,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards)
{
boolean pathfound = false;
if (sourcewaypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_PathfindThruCircuitSpawnable.\n");
}
else if (finishline == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL finishline in K_PathfindThruCircuitSpawnable.\n");
}
else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0))
|| ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0)))
{
CONS_Debug(DBG_GAMELOGIC,
"K_PathfindThruCircuitSpawnable: sourcewaypoint with ID %d has no next waypoint\n",
K_GetWaypointID(sourcewaypoint));
}
else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0))
|| ((huntbackwards == true) && (finishline->numnextwaypoints == 0)))
{
CONS_Debug(DBG_GAMELOGIC,
"K_PathfindThruCircuitSpawnable: finishline with ID %d has no previous waypoint\n",
K_GetWaypointID(finishline));
}
else
{
pathfindsetup_t pathfindsetup = {0};
getconnectednodesfunc nextnodesfunc = K_WaypointPathfindGetNext;
getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts;
getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic;
getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts;
getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedGScoreSpawnable;
if (huntbackwards)
{
nextnodesfunc = K_WaypointPathfindGetPrev;
nodecostsfunc = K_WaypointPathfindGetPrevCosts;
}
if (useshortcuts)
{
traversablefunc = K_WaypointPathfindTraversableAllEnabled;
}
pathfindsetup.opensetcapacity = K_GetOpensetBaseSize();
pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize();
pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize();
pathfindsetup.startnodedata = sourcewaypoint;
pathfindsetup.endnodedata = finishline;
pathfindsetup.endgscore = traveldistance;
pathfindsetup.getconnectednodes = nextnodesfunc;
pathfindsetup.getconnectioncosts = nodecostsfunc;
pathfindsetup.getheuristic = heuristicfunc;
pathfindsetup.gettraversable = traversablefunc;
pathfindsetup.getfinished = finishedfunc;
pathfound = K_PathfindAStar(returnpath, &pathfindsetup);
@ -1183,18 +1447,19 @@ waypoint_t *K_GetNextWaypointToDestination(
getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts;
getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic;
getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts;
getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedEnd;
if (huntbackwards)
{
nextnodesfunc = K_WaypointPathfindGetPrev;
nodecostsfunc = K_WaypointPathfindGetPrevCosts;
}
if (useshortcuts)
{
traversablefunc = K_WaypointPathfindTraversableAllEnabled;
}
pathfindsetup.opensetcapacity = K_GetOpensetBaseSize();
pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize();
pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize();
@ -1204,6 +1469,7 @@ waypoint_t *K_GetNextWaypointToDestination(
pathfindsetup.getconnectioncosts = nodecostsfunc;
pathfindsetup.getheuristic = heuristicfunc;
pathfindsetup.gettraversable = traversablefunc;
pathfindsetup.getfinished = finishedfunc;
pathfindsuccess = K_PathfindAStar(&pathtowaypoint, &pathfindsetup);
@ -1895,6 +2161,7 @@ boolean K_SetupWaypointList(void)
// Loop through the waypointcap here so that all waypoints are added to the heap, and allow easier debugging
for (waypointmobj = waypointcap; waypointmobj; waypointmobj = waypointmobj->tracer)
{
waypointmobj->cusval = (INT32)numwaypoints;
K_SetupWaypoint(waypointmobj);
}

View file

@ -214,6 +214,67 @@ boolean K_PathfindToWaypoint(
const boolean huntbackwards);
/*--------------------------------------------------
boolean K_PathfindThruCircuit(
waypoint_t *const sourcewaypoint,
const UINT32 traveldistance,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards)
Tries a pathfind to the finish line waypoint, similar to K_PathfindToWaypoint, but it will continue
until it reaches the specified distance. The final path returned will only have the waypoints up to the
specified distance.
Input Arguments:-
sourcewaypoint - The waypoint to start searching from
traveldistance - How far along the circuit it will try to pathfind.
returnpath - The path_t that will contain the final found path
useshortcuts - Whether to use waypoints that are marked as being shortcuts in the search
huntbackwards - Goes through the waypoints backwards if true
Return:-
True if a circuit path could be constructed, false if it couldn't.
--------------------------------------------------*/
boolean K_PathfindThruCircuit(
waypoint_t *const sourcewaypoint,
const UINT32 traveldistance,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards);
/*--------------------------------------------------
boolean K_PathfindThruCircuitSpawnable(
waypoint_t *const sourcewaypoint,
const UINT32 traveldistance,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards)
The same as K_PathfindThruCircuit, but continues until hitting a waypoint that
can be respawned at.
Input Arguments:-
sourcewaypoint - The waypoint to start searching from
traveldistance - How far along the circuit it will try to pathfind.
returnpath - The path_t that will contain the final found path
useshortcuts - Whether to use waypoints that are marked as being shortcuts in the search
huntbackwards - Goes through the waypoints backwards if true
Return:-
True if a circuit path could be constructed, false if it couldn't.
--------------------------------------------------*/
boolean K_PathfindThruCircuitSpawnable(
waypoint_t *const sourcewaypoint,
const UINT32 traveldistance,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards);
/*--------------------------------------------------
waypoint_t *K_GetNextWaypointToDestination(
waypoint_t *const sourcewaypoint,

View file

@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2020 by Sonic Team Junior.
// Copyright (C) 2012-2022 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@ -12,115 +12,134 @@
#include "r_defs.h"
#include "d_player.h"
#include "s_sound.h"
#include "d_event.h"
#include "lua_hudlib_drawlist.h"
enum hook {
hook_NetVars=0,
hook_MapChange,
hook_MapLoad,
hook_PlayerJoin,
hook_PreThinkFrame,
hook_ThinkFrame,
hook_PostThinkFrame,
hook_MobjSpawn,
hook_MobjCollide,
hook_MobjLineCollide,
hook_MobjMoveCollide,
hook_TouchSpecial,
hook_MobjFuse,
hook_MobjThinker,
hook_BossThinker,
hook_ShouldDamage,
hook_MobjDamage,
hook_MobjDeath,
hook_BossDeath,
hook_MobjRemoved,
hook_JumpSpecial,
hook_AbilitySpecial,
hook_SpinSpecial,
hook_JumpSpinSpecial,
hook_BotTiccmd,
hook_BotAI,
hook_BotRespawn,
hook_LinedefExecute,
hook_PlayerMsg,
hook_HurtMsg,
hook_PlayerSpawn,
hook_ShieldSpawn,
hook_ShieldSpecial,
hook_MobjMoveBlocked,
hook_MapThingSpawn,
hook_FollowMobj,
hook_PlayerCanDamage,
hook_PlayerQuit,
hook_MusicChange,
hook_TeamSwitch,
hook_ViewpointSwitch,
hook_PlayerThink,
hook_ShouldJingleContinue,
hook_GameQuit,
hook_PlayerCmd,
/*
Do you know what an 'X Macro' is? Such a macro is called over each element of
a list and expands the input. I use it for the hook lists because both an enum
and array of hook names need to be kept in order. The X Macro handles this
automatically.
*/
// SRB2Kart
hook_IntermissionThinker,
hook_VoteThinker,
#define MOBJ_HOOK_LIST(X) \
X (MobjSpawn),/* P_SpawnMobj */\
X (MobjCollide),/* PIT_CheckThing */\
X (MobjLineCollide),/* ditto */\
X (MobjMoveCollide),/* tritto */\
X (TouchSpecial),/* P_TouchSpecialThing */\
X (MobjFuse),/* when mobj->fuse runs out */\
X (MobjThinker),/* P_MobjThinker, P_SceneryThinker */\
X (BossThinker),/* P_GenericBossThinker */\
X (ShouldDamage),/* P_DamageMobj (Should mobj take damage?) */\
X (MobjDamage),/* P_DamageMobj (Mobj actually takes damage!) */\
X (MobjDeath),/* P_KillMobj */\
X (BossDeath),/* A_BossDeath */\
X (MobjRemoved),/* P_RemoveMobj */\
X (BotRespawn),/* B_CheckRespawn */\
X (MobjMoveBlocked),/* P_XYMovement (when movement is blocked) */\
X (MapThingSpawn),/* P_SpawnMapThing */\
X (FollowMobj),/* P_PlayerAfterThink Smiles mobj-following */\
hook_MAX // last hook
};
extern const char *const hookNames[];
#define HOOK_LIST(X) \
X (NetVars),/* add to archive table (netsave) */\
X (MapChange),/* (before map load) */\
X (MapLoad),\
X (PlayerJoin),/* Got_AddPlayer */\
X (PreThinkFrame)/* frame (before mobj and player thinkers) */,\
X (ThinkFrame),/* frame (after mobj and player thinkers) */\
X (PostThinkFrame),/* frame (at end of tick, ie after overlays, precipitation, specials) */\
X (JumpSpecial),/* P_DoJumpStuff (Any-jumping) */\
X (AbilitySpecial),/* P_DoJumpStuff (Double-jumping) */\
X (SpinSpecial),/* P_DoSpinAbility (Spin button effect) */\
X (JumpSpinSpecial),/* P_DoJumpStuff (Spin button effect (mid-air)) */\
X (BotTiccmd),/* B_BuildTiccmd */\
X (PlayerMsg),/* chat messages */\
X (HurtMsg),/* imhurttin */\
X (PlayerSpawn),/* G_SpawnPlayer */\
X (ShieldSpawn),/* P_SpawnShieldOrb */\
X (ShieldSpecial),/* shield abilities */\
X (PlayerCanDamage),/* P_PlayerCanDamage */\
X (PlayerQuit),\
X (IntermissionThinker),/* Y_Ticker */\
X (TeamSwitch),/* team switching in... uh... *what* speak, spit it the fuck out */\
X (ViewpointSwitch),/* spy mode (no trickstabs) */\
X (SeenPlayer),/* MT_NAMECHECK */\
X (PlayerThink),/* P_PlayerThink */\
X (GameQuit),\
X (PlayerCmd),/* building the player's ticcmd struct */\
X (MusicChange),\
X (VoteThinker),/* Y_VoteTicker */\
#define STRING_HOOK_LIST(X) \
X (LinedefExecute),\
X (ShouldJingleContinue),/* should jingle of the given music continue playing */\
#define HUD_HOOK_LIST(X) \
X (game),\
X (scores),/* emblems/multiplayer list */\
X (title),/* titlescreen */\
X (titlecard),\
X (intermission),\
/*
I chose to access the hook enums through a macro as well. This could provide
a hint to lookup the macro's definition instead of the enum's definition.
(Since each enumeration is not defined in the source code, but by the list
macros above, it is not greppable.) The name passed to the macro can also be
grepped and found in the lists above.
*/
#define MOBJ_HOOK(name) mobjhook_ ## name
#define HOOK(name) hook_ ## name
#define HUD_HOOK(name) hudhook_ ## name
#define STRING_HOOK(name) stringhook_ ## name
#define ENUM(X) enum { X ## _LIST (X) X(MAX) }
ENUM (MOBJ_HOOK);
ENUM (HOOK);
ENUM (HUD_HOOK);
ENUM (STRING_HOOK);
#undef ENUM
/* dead simple, LUA_HOOK(GameQuit) */
#define LUA_HOOK(type) LUA_HookVoid(HOOK(type))
//#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type))
extern boolean hook_cmd_running;
extern int hook_defrosting;
void LUAh_MapChange(INT16 mapnumber); // Hook for map change (before load)
void LUAh_MapLoad(void); // Hook for map load
void LUAh_PlayerJoin(int playernum); // Hook for Got_AddPlayer
void LUAh_PreThinkFrame(void); // Hook for frame (before mobj and player thinkers)
void LUAh_ThinkFrame(void); // Hook for frame (after mobj and player thinkers)
void LUAh_PostThinkFrame(void); // Hook for frame (at end of tick, ie after overlays, precipitation, specials)
boolean LUAh_MobjHook(mobj_t *mo, enum hook which);
boolean LUAh_PlayerHook(player_t *plr, enum hook which);
#define LUAh_MobjSpawn(mo) LUAh_MobjHook(mo, hook_MobjSpawn) // Hook for P_SpawnMobj by mobj type
UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which);
UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which);
#define LUAh_MobjCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjCollide) // Hook for PIT_CheckThing by (thing) mobj type
#define LUAh_MobjLineCollide(thing, line) LUAh_MobjLineCollideHook(thing, line, hook_MobjLineCollide) // Hook for PIT_CheckThing by (thing) mobj type
#define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type
boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type
#define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type
boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type
#define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type
UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for P_KillMobj by mobj type
#define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type
#define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type
#define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping)
#define LUAh_AbilitySpecial(player) LUAh_PlayerHook(player, hook_AbilitySpecial) // Hook for P_DoJumpStuff (Double-jumping)
#define LUAh_SpinSpecial(player) LUAh_PlayerHook(player, hook_SpinSpecial) // Hook for P_DoSpinAbility (Spin button effect)
#define LUAh_JumpSpinSpecial(player) LUAh_PlayerHook(player, hook_JumpSpinSpecial) // Hook for P_DoJumpStuff (Spin button effect (mid-air))
boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd); // Hook for B_BuildTiccmd
boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name
boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails); // Hook for B_CheckRespawn
boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors
boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg, int mute); // Hook for chat messages
boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for hurt messages
#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer
void LUAh_PlayerQuit(player_t *plr, kickreason_t reason); // Hook for player quitting
boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping,
UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms); // Hook for music changes
void LUA_HookVoid(int hook);
void LUA_HookHUD(huddrawlist_h, int hook);
boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Allows to write to player cmd before the game does anything with them.
int LUA_HookMobj(mobj_t *, int hook);
int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook);
void LUA_HookInt(INT32 integer, int hook);
void LUA_HookBool(boolean value, int hook);
int LUA_HookPlayer(player_t *, int hook);
int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook);
int LUA_HookKey(event_t *event, int hook); // Hooks for key events
void LUAh_IntermissionThinker(void); // Hook for Y_Ticker
void LUAh_VoteThinker(void); // Hook for Y_VoteTicker
#define LUAh_MobjMoveBlocked(mo) LUAh_MobjHook(mo, hook_MobjMoveBlocked) // Hook for P_XYMovement (when movement is blocked)
boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type
boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj); // Hook for P_PlayerAfterThink Smiles mobj-following
UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj); // Hook for P_PlayerCanDamage
boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh....
UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode
#define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink
boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname); // Hook for whether a jingle of the given music should continue playing
void LUAh_GameQuit(boolean quitting); // Hook for game quitting
void LUA_HookThinkFrame(void);
int LUA_HookMobjLineCollide(mobj_t *, line_t *);
int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher);
int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype);
int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype);
int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype);
int LUA_HookMobjMoveBlocked(mobj_t *, mobj_t *, line_t *);
void LUA_HookLinedefExecute(line_t *, mobj_t *, sector_t *);
int LUA_HookPlayerMsg(int source, int target, int flags, char *msg, int mute);
int LUA_HookHurtMsg(player_t *, mobj_t *inflictor, mobj_t *source, UINT8 damagetype);
int LUA_HookMapThingSpawn(mobj_t *, mapthing_t *);
int LUA_HookFollowMobj(player_t *, mobj_t *);
int LUA_HookPlayerCanDamage(player_t *, mobj_t *);
void LUA_HookPlayerQuit(player_t *, kickreason_t);
int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble);
int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced);
int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend);
int LUA_HookShouldJingleContinue(player_t *, const char *musname);
int LUA_HookMusicChange(const char *oldname, struct MusicChange *);

File diff suppressed because it is too large Load diff

View file

@ -47,10 +47,6 @@ extern boolean hud_running;
boolean LUA_HudEnabled(enum hud option);
void LUAh_GameHUD(player_t *stplyr, huddrawlist_h list);
void LUAh_ScoresHUD(huddrawlist_h list);
void LUAh_TitleHUD(huddrawlist_h list);
void LUAh_TitleCardHUD(player_t *stplayr, huddrawlist_h list);
void LUAh_IntermissionHUD(huddrawlist_h list);
void LUA_SetHudHook(int hook, huddrawlist_h list);
#endif // __LUA_HUD_H__

View file

@ -29,14 +29,13 @@
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h"
#include "lua_hook.h"
#define HUDONLY if (!hud_running) return luaL_error(L, "HUD rendering code should not be called outside of rendering hooks!");
boolean hud_running = false;
static UINT8 hud_enabled[(hud_MAX/8)+1];
static UINT8 hudAvailable; // hud hooks field
static UINT8 camnum = 1;
// must match enum hud in lua_hud.h
@ -79,21 +78,6 @@ static const char *const patch_opt[] = {
"topoffset",
NULL};
enum hudhook {
hudhook_game = 0,
hudhook_scores,
hudhook_intermission,
hudhook_title,
hudhook_titlecard
};
static const char *const hudhook_opt[] = {
"game",
"scores",
"intermission",
"title",
"titlecard",
NULL};
// alignment types for v.drawString
enum align {
align_left = 0,
@ -1249,6 +1233,8 @@ static luaL_Reg lib_draw[] = {
{NULL, NULL}
};
static int lib_draw_ref;
//
// lib_hud
//
@ -1282,28 +1268,7 @@ static int lib_hudenabled(lua_State *L)
}
// add a HUD element for rendering
static int lib_hudadd(lua_State *L)
{
enum hudhook field;
luaL_checktype(L, 1, LUA_TFUNCTION);
field = luaL_checkoption(L, 2, "game", hudhook_opt);
if (!lua_lumploading)
return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
lua_getfield(L, LUA_REGISTRYINDEX, "HUD");
I_Assert(lua_istable(L, -1));
lua_rawgeti(L, -1, field+2); // HUD[2+]
I_Assert(lua_istable(L, -1));
lua_remove(L, -2);
lua_pushvalue(L, 1);
lua_rawseti(L, -2, (int)(lua_objlen(L, -2) + 1));
hudAvailable |= 1<<field;
return 0;
}
extern int lib_hudadd(lua_State *L);
static luaL_Reg lib_hud[] = {
{"enable", lib_hudenable},
@ -1321,26 +1286,9 @@ int LUA_HudLib(lua_State *L)
{
memset(hud_enabled, 0xff, (hud_MAX/8)+1);
lua_newtable(L); // HUD registry table
lua_newtable(L);
luaL_register(L, NULL, lib_draw);
lua_rawseti(L, -2, 1); // HUD[1] = lib_draw
lua_newtable(L);
lua_rawseti(L, -2, 2); // HUD[2] = game rendering functions array
lua_newtable(L);
lua_rawseti(L, -2, 3); // HUD[3] = scores rendering functions array
lua_newtable(L);
lua_rawseti(L, -2, 4); // HUD[4] = intermission rendering functions array
lua_newtable(L);
lua_rawseti(L, -2, 5); // HUD[5] = title rendering functions array
lua_newtable(L);
lua_rawseti(L, -2, 6); // HUD[6] = title card rendering functions array
lua_setfield(L, LUA_REGISTRYINDEX, "HUD");
lua_newtable(L);
luaL_register(L, NULL, lib_draw);
lib_draw_ref = luaL_ref(L, LUA_REGISTRYINDEX);
luaL_newmetatable(L, META_COLORMAP);
lua_pushcfunction(L, colormap_get);
@ -1371,170 +1319,28 @@ boolean LUA_HudEnabled(enum hud option)
return false;
}
// Hook for HUD rendering
void LUAh_GameHUD(player_t *stplayr, huddrawlist_h list)
void LUA_SetHudHook(int hook, huddrawlist_h list)
{
if (!gL || !(hudAvailable & (1<<hudhook_game)))
return;
lua_getref(gL, lib_draw_ref);
lua_pushlightuserdata(gL, list);
lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST");
hud_running = true;
lua_settop(gL, 0);
switch (hook)
{
case HUD_HOOK(game):
camnum = R_GetViewNumber();
lua_pushcfunction(gL, LUA_GetErrorMessage);
LUA_PushUserdata(gL, stplyr, META_PLAYER);
LUA_PushUserdata(gL, &camera[camnum], META_CAMERA);
lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -1, 2+hudhook_game); // HUD[2] = rendering funcs
I_Assert(lua_istable(gL, -1));
camnum++; // for compatibility
break;
lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
I_Assert(lua_istable(gL, -1));
lua_remove(gL, -3); // pop HUD
LUA_PushUserdata(gL, stplayr, META_PLAYER);
camnum = R_GetViewNumber();
LUA_PushUserdata(gL, &camera[camnum], META_CAMERA);
camnum++; // for compatibility
lua_pushnil(gL);
while (lua_next(gL, -5) != 0) {
lua_pushvalue(gL, -5); // graphics library (HUD[1])
lua_pushvalue(gL, -5); // stplayr
lua_pushvalue(gL, -5); // camera
LUA_Call(gL, 3, 0, 1);
case HUD_HOOK(titlecard):
LUA_PushUserdata(gL, stplyr, META_PLAYER);
lua_pushinteger(gL, lt_ticker);
lua_pushinteger(gL, (lt_endtime + TICRATE));
break;
}
lua_settop(gL, 0);
hud_running = false;
}
void LUAh_ScoresHUD(huddrawlist_h list)
{
if (!gL || !(hudAvailable & (1<<hudhook_scores)))
return;
lua_pushlightuserdata(gL, list);
lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST");
hud_running = true;
lua_settop(gL, 0);
lua_pushcfunction(gL, LUA_GetErrorMessage);
lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -1, 2+hudhook_scores); // HUD[3] = rendering funcs
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
I_Assert(lua_istable(gL, -1));
lua_remove(gL, -3); // pop HUD
lua_pushnil(gL);
while (lua_next(gL, -3) != 0) {
lua_pushvalue(gL, -3); // graphics library (HUD[1])
LUA_Call(gL, 1, 0, 1);
}
lua_settop(gL, 0);
hud_running = false;
}
void LUAh_TitleHUD(huddrawlist_h list)
{
if (!gL || !(hudAvailable & (1<<hudhook_title)))
return;
lua_pushlightuserdata(gL, list);
lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST");
hud_running = true;
lua_settop(gL, 0);
lua_pushcfunction(gL, LUA_GetErrorMessage);
lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -1, 2+hudhook_title); // HUD[5] = rendering funcs
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
I_Assert(lua_istable(gL, -1));
lua_remove(gL, -3); // pop HUD
lua_pushnil(gL);
while (lua_next(gL, -3) != 0) {
lua_pushvalue(gL, -3); // graphics library (HUD[1])
LUA_Call(gL, 1, 0, 1);
}
lua_settop(gL, 0);
hud_running = false;
}
void LUAh_TitleCardHUD(player_t *stplayr, huddrawlist_h list)
{
if (!gL || !(hudAvailable & (1<<hudhook_titlecard)))
return;
lua_pushlightuserdata(gL, list);
lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST");
hud_running = true;
lua_settop(gL, 0);
lua_pushcfunction(gL, LUA_GetErrorMessage);
lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -1, 2+hudhook_titlecard); // HUD[6] = rendering funcs
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
I_Assert(lua_istable(gL, -1));
lua_remove(gL, -3); // pop HUD
LUA_PushUserdata(gL, stplayr, META_PLAYER);
lua_pushinteger(gL, lt_ticker);
lua_pushinteger(gL, (lt_endtime + TICRATE));
lua_pushnil(gL);
while (lua_next(gL, -6) != 0) {
lua_pushvalue(gL, -6); // graphics library (HUD[1])
lua_pushvalue(gL, -6); // stplayr
lua_pushvalue(gL, -6); // lt_ticker
lua_pushvalue(gL, -6); // lt_endtime
LUA_Call(gL, 4, 0, 1);
}
lua_settop(gL, 0);
hud_running = false;
}
void LUAh_IntermissionHUD(huddrawlist_h list)
{
if (!gL || !(hudAvailable & (1<<hudhook_intermission)))
return;
lua_pushlightuserdata(gL, list);
lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST");
hud_running = true;
lua_settop(gL, 0);
lua_pushcfunction(gL, LUA_GetErrorMessage);
lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -1, 2+hudhook_intermission); // HUD[4] = rendering funcs
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
I_Assert(lua_istable(gL, -1));
lua_remove(gL, -3); // pop HUD
lua_pushnil(gL);
while (lua_next(gL, -3) != 0) {
lua_pushvalue(gL, -3); // graphics library (HUD[1])
LUA_Call(gL, 1, 0, 1);
}
lua_settop(gL, 0);
hud_running = false;
}

View file

@ -54,7 +54,7 @@ typedef struct drawitem_s {
fixed_t sy;
INT32 num;
INT32 digits;
const char *str;
size_t stroffset; // offset into strbuf to get str
UINT16 color;
UINT8 strength;
INT32 align;
@ -129,6 +129,10 @@ void LUA_HUD_DestroyDrawList(huddrawlist_h list)
{
Z_Free(list->items);
}
if (list->strbuf)
{
Z_Free(list->strbuf);
}
Z_Free(list);
}
@ -156,7 +160,7 @@ static size_t AllocateDrawItem(huddrawlist_h list)
// copy string to list's internal string buffer
// lua can deallocate the string before we get to use it, so it's important to
// keep our own copy
static const char *CopyString(huddrawlist_h list, const char* str)
static size_t CopyString(huddrawlist_h list, const char* str)
{
size_t lenstr;
@ -168,10 +172,13 @@ static const char *CopyString(huddrawlist_h list, const char* str)
else list->strbuf_capacity *= 2;
list->strbuf = (char*) Z_ReallocAlign(list->strbuf, sizeof(char) * list->strbuf_capacity, PU_STATIC, NULL, 8);
}
const char *result = (const char *) &list->strbuf[list->strbuf_len];
strncpy(&list->strbuf[list->strbuf_len], str, lenstr + 1);
list->strbuf_len += lenstr + 1;
return result;
{
size_t old_len = list->strbuf_len;
strncpy(&list->strbuf[old_len], str, lenstr + 1);
list->strbuf_len += lenstr + 1;
return old_len;
}
}
void LUA_HUD_AddDraw(
@ -325,7 +332,7 @@ void LUA_HUD_AddDrawString(
item->type = DI_DrawString;
item->x = x;
item->y = y;
item->str = CopyString(list, str);
item->stroffset = CopyString(list, str);
item->flags = flags;
item->align = align;
}
@ -360,7 +367,7 @@ void LUA_HUD_AddDrawTitleCardString(
item->x = x;
item->y = y;
item->flags = flags;
item->str = CopyString(list, str);
item->stroffset = CopyString(list, str);
item->bossmode = bossmode;
item->timer = timer;
item->threshold = threshold;
@ -379,8 +386,8 @@ void LUA_HUD_AddDrawKartString(
item->type = DI_DrawKartString;
item->x = x;
item->y = y;
item->stroffset = CopyString(list, str);
item->flags = flags;
item->str = CopyString(list, str);
}
void LUA_HUD_DrawList(huddrawlist_h list)
@ -394,6 +401,7 @@ void LUA_HUD_DrawList(huddrawlist_h list)
for (i = 0; i < list->items_len; i++)
{
drawitem_t *item = &list->items[i];
const char *itemstr = &list->strbuf[item->stroffset];
switch (item->type)
{
@ -423,33 +431,33 @@ void LUA_HUD_DrawList(huddrawlist_h list)
{
// hu_font
case align_left:
V_DrawString(item->x, item->y, item->flags, item->str);
V_DrawString(item->x, item->y, item->flags, itemstr);
break;
case align_center:
V_DrawCenteredString(item->x, item->y, item->flags, item->str);
V_DrawCenteredString(item->x, item->y, item->flags, itemstr);
break;
case align_right:
V_DrawRightAlignedString(item->x, item->y, item->flags, item->str);
V_DrawRightAlignedString(item->x, item->y, item->flags, itemstr);
break;
// hu_font, 0.5x scale
case align_small:
V_DrawSmallString(item->x, item->y, item->flags, item->str);
V_DrawSmallString(item->x, item->y, item->flags, itemstr);
break;
case align_smallcenter:
V_DrawCenteredSmallString(item->x, item->y, item->flags, item->str);
V_DrawCenteredSmallString(item->x, item->y, item->flags, itemstr);
break;
case align_smallright:
V_DrawRightAlignedSmallString(item->x, item->y, item->flags, item->str);
V_DrawRightAlignedSmallString(item->x, item->y, item->flags, itemstr);
break;
// tny_font
case align_thin:
V_DrawThinString(item->x, item->y, item->flags, item->str);
V_DrawThinString(item->x, item->y, item->flags, itemstr);
break;
case align_thincenter:
V_DrawCenteredThinString(item->x, item->y, item->flags, item->str);
V_DrawCenteredThinString(item->x, item->y, item->flags, itemstr);
break;
case align_thinright:
V_DrawRightAlignedThinString(item->x, item->y, item->flags, item->str);
V_DrawRightAlignedThinString(item->x, item->y, item->flags, itemstr);
break;
}
break;
@ -457,10 +465,10 @@ void LUA_HUD_DrawList(huddrawlist_h list)
V_DrawFadeScreen(item->color, item->strength);
break;
case DI_DrawTitleCardString:
V_DrawTitleCardString(item->x, item->y, item->str, item->flags, item->bossmode, item->timer, item->threshold);
V_DrawTitleCardString(item->x, item->y, itemstr, item->flags, item->bossmode, item->timer, item->threshold);
break;
case DI_DrawKartString:
V_DrawKartString(item->x, item->y, item->flags, item->str);
V_DrawKartString(item->x, item->y, item->flags, itemstr);
break;
default:
I_Error("can't draw draw list item: invalid draw list item type");

View file

@ -1687,7 +1687,7 @@ void LUA_Archive(UINT8 **p)
WRITEUINT32(*p, UINT32_MAX); // end of mobjs marker, replaces mobjnum.
LUAh_NetArchiveHook(NetArchive); // call the NetArchive hook in archive mode
LUA_HookNetArchive(NetArchive); // call the NetArchive hook in archive mode
}
ArchiveTables(p);
@ -1726,7 +1726,7 @@ void LUA_UnArchive(UINT8 **p)
}
} while(mobjnum != UINT32_MAX); // repeat until end of mobjs marker.
LUAh_NetArchiveHook(NetUnArchive); // call the NetArchive hook in unarchive mode
LUA_HookNetArchive(NetUnArchive); // call the NetArchive hook in unarchive mode
}
UnArchiveTables(p);

View file

@ -63,7 +63,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c
void LUA_CVarChanged(void *cvar); // lua_consolelib.c
int Lua_optoption(lua_State *L, int narg,
const char *def, const char *const lst[]);
void LUAh_NetArchiveHook(lua_CFunction archFunc);
void LUA_HookNetArchive(lua_CFunction archFunc);
void LUA_PushTaggableObjectArray
( lua_State *L,

View file

@ -204,7 +204,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedInt(fixed_t a)
*/
FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedDiv(fixed_t a, fixed_t b)
{
if ((abs(a) >> (FRACBITS-2)) >= abs(b))
if ((abs(a / (FRACUNIT/4))) >= abs(b))
return (a^b) < 0 ? INT32_MIN : INT32_MAX;
return FixedDiv2(a, b);

View file

@ -1 +1,2 @@
hyudoro.c
shrink.c

782
src/objects/shrink.c Normal file
View file

@ -0,0 +1,782 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2022 by Kart Krew
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file shrink.c
/// \brief Shrink laser item code.
#include "../doomdef.h"
#include "../doomstat.h"
#include "../info.h"
#include "../k_kart.h"
#include "../k_objects.h"
#include "../m_random.h"
#include "../p_local.h"
#include "../r_main.h"
#include "../s_sound.h"
#include "../g_game.h"
#include "../z_zone.h"
#include "../k_waypoint.h"
//
// ███████╗██╗██╗░░██╗███╗░░░███╗███████╗
// ██╔════╝██║╚██╗██╔╝████╗░████║██╔════╝
// █████╗░░██║░╚███╔╝░██╔████╔██║█████╗░░
// ██╔══╝░░██║░██╔██╗░██║╚██╔╝██║██╔══╝░░
// ██║░░░░░██║██╔╝╚██╗██║░╚═╝░██║███████╗
// ╚═╝░░░░░╚═╝╚═╝░░╚═╝╚═╝░░░░░╚═╝╚══════╝
//
// vertical flip
//
#define POHBEE_HOVER (256 << FRACBITS)
#define POHBEE_SPEED (128 << FRACBITS)
#define POHBEE_TIME (30 * TICRATE)
#define POHBEE_DIST (4096 << FRACBITS)
#define GUN_SWING (ANGLE_90 - ANG10)
#define GUN_SWINGTIME (4 * TICRATE)
#define CHAIN_SIZE (52)
#define EXTRA_FOR_FIRST (7)
enum
{
POHBEE_MODE_SPAWN,
POHBEE_MODE_ACT,
POHBEE_MODE_DESPAWN,
};
#define pohbee_mode(o) ((o)->cusval)
#define pohbee_timer(o) ((o)->reactiontime)
#define pohbee_waypoint_cur(o) ((o)->extravalue1)
#define pohbee_waypoint_dest(o) ((o)->extravalue2)
#define pohbee_height(o) ((o)->movefactor)
#define pohbee_owner(o) ((o)->target)
#define pohbee_guns(o) ((o)->hnext)
#define gun_offset(o) ((o)->movecount)
#define gun_numsegs(o) ((o)->extravalue1)
#define gun_pohbee(o) ((o)->target)
#define gun_laser(o) ((o)->tracer)
#define gun_chains(o) ((o)->hprev)
#define chain_index(o) ((o)->extravalue1)
enum
{
LASER_SHRINK,
LASER_GROW,
};
static skincolornum_t ShrinkLaserColor(mobj_t *pohbee)
{
UINT8 laserState = LASER_SHRINK;
player_t *owner = NULL;
if (pohbee_owner(pohbee) != NULL && P_MobjWasRemoved(pohbee_owner(pohbee)) == false)
{
owner = pohbee_owner(pohbee)->player;
}
if (owner != NULL && P_IsDisplayPlayer(owner) == true)
{
laserState = LASER_GROW;
if (r_splitscreen > 0 && (leveltime & 1))
{
// TODO: make this properly screen dependent,
// instead of flashing.
laserState = LASER_SHRINK;
}
}
switch (laserState)
{
default:
case LASER_SHRINK:
return SKINCOLOR_KETCHUP;
case LASER_GROW:
return SKINCOLOR_SAPPHIRE;
}
}
static boolean ShrinkLaserActive(mobj_t *pohbee)
{
return (pohbee_mode(pohbee) == POHBEE_MODE_ACT);
}
static void PohbeeMoveTo(mobj_t *pohbee, fixed_t destx, fixed_t desty, fixed_t destz)
{
pohbee->momx = destx - pohbee->x;
pohbee->momy = desty - pohbee->y;
pohbee->momz = destz - pohbee->z;
}
static fixed_t GenericDistance(
fixed_t curx, fixed_t cury, fixed_t curz,
fixed_t destx, fixed_t desty, fixed_t destz)
{
return P_AproxDistance(P_AproxDistance(destx - curx, desty - cury), destz - curz);
}
static fixed_t PohbeeWaypointZ(mobj_t *pohbee, mobj_t *dest)
{
return dest->z + (pohbee_height(pohbee) + FixedMul(POHBEE_HOVER, mapobjectscale) * P_MobjFlip(dest));
}
static void PohbeeSpawn(mobj_t *pohbee)
{
waypoint_t *curWaypoint = NULL;
waypoint_t *destWaypoint = NULL;
fixed_t distLeft = INT32_MAX;
fixed_t newX = pohbee->x;
fixed_t newY = pohbee->y;
fixed_t newZ = pohbee->z;
boolean finalize = false;
const boolean useshortcuts = false;
const boolean huntbackwards = false;
boolean pathfindsuccess = false;
path_t pathtofinish = {0};
size_t pathIndex = 0;
curWaypoint = K_GetWaypointFromIndex((size_t)pohbee_waypoint_cur(pohbee));
destWaypoint = K_GetWaypointFromIndex((size_t)pohbee_waypoint_dest(pohbee));
if (curWaypoint == NULL || destWaypoint == NULL)
{
// Waypoints aren't valid.
// Just transition into the next state.
pohbee_mode(pohbee) = POHBEE_MODE_ACT;
return;
}
distLeft = FixedMul(POHBEE_SPEED, mapobjectscale);
while (distLeft > 0)
{
fixed_t wpX = curWaypoint->mobj->x;
fixed_t wpY = curWaypoint->mobj->y;
fixed_t wpZ = PohbeeWaypointZ(pohbee, curWaypoint->mobj);
fixed_t distToNext = GenericDistance(
newX, newY, newZ,
wpX, wpY, wpZ
);
if (distToNext > distLeft)
{
// Only made it partially there.
newX += FixedMul(FixedDiv(wpX - newX, distToNext), distLeft);
newY += FixedMul(FixedDiv(wpY - newY, distToNext), distLeft);
newZ += FixedMul(FixedDiv(wpZ - newZ, distToNext), distLeft);
distLeft = 0;
}
else
{
// Close enough to the next waypoint,
// move there and remove the distance.
newX = wpX;
newY = wpY;
newZ = wpZ;
distLeft -= distToNext;
if (curWaypoint == destWaypoint)
{
// Reached the end.
finalize = true;
break;
}
// Create waypoint path to our destination.
// Crazy over-engineered, just to catch when
// waypoints are insanely close to each other :P
if (pathfindsuccess == false)
{
pathfindsuccess = K_PathfindToWaypoint(
curWaypoint, destWaypoint,
&pathtofinish,
useshortcuts, huntbackwards
);
if (pathfindsuccess == false)
{
// Path isn't valid.
// Just transition into the next state.
finalize = true;
break;
}
}
pathIndex++;
if (pathIndex >= pathtofinish.numnodes)
{
// Successfully reached the end of the path.
finalize = true;
break;
}
// Now moving to the next waypoint.
curWaypoint = (waypoint_t *)pathtofinish.array[pathIndex].nodedata;
pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(curWaypoint);
}
}
PohbeeMoveTo(pohbee, newX, newY, newZ);
pohbee->angle = K_MomentumAngle(pohbee);
if (finalize == true)
{
// Move to next state
pohbee_mode(pohbee) = POHBEE_MODE_ACT;
}
if (pathfindsuccess == true)
{
Z_Free(pathtofinish.array);
}
}
static void PohbeeAct(mobj_t *pohbee)
{
pohbee_timer(pohbee)--;
if (pohbee_timer(pohbee) <= 0)
{
// Move to next state
pohbee_mode(pohbee) = POHBEE_MODE_DESPAWN;
pohbee->fuse = 5*TICRATE;
}
}
static void PohbeeDespawn(mobj_t *pohbee)
{
pohbee->momz = 16 * pohbee->scale * P_MobjFlip(pohbee);
}
static void DoGunSwing(mobj_t *gun, mobj_t *pohbee)
{
const angle_t angle = gun->angle + ANGLE_90;
const tic_t swingTimer = leveltime + gun_offset(gun);
const angle_t swingAmt = swingTimer * (ANGLE_MAX / GUN_SWINGTIME);
const fixed_t swingCos = FINECOSINE(swingAmt >> ANGLETOFINESHIFT);
const angle_t pitch = -ANGLE_90 + FixedMul(swingCos, GUN_SWING);
const fixed_t dist = gun_numsegs(gun) * CHAIN_SIZE * gun->scale;
fixed_t offsetX = FixedMul(
dist, FixedMul(
FINECOSINE(angle >> ANGLETOFINESHIFT),
FINECOSINE(pitch >> ANGLETOFINESHIFT)
)
);
fixed_t offsetY = FixedMul(
dist, FixedMul(
FINESINE(angle >> ANGLETOFINESHIFT),
FINECOSINE(pitch >> ANGLETOFINESHIFT)
)
);
fixed_t offsetZ = FixedMul(
dist, FINESINE(pitch >> ANGLETOFINESHIFT)
);
PohbeeMoveTo(gun, pohbee->x + offsetX, pohbee->y + offsetY, pohbee->z + offsetZ);
}
static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser)
{
const fixed_t gunX = gun->x + gun->momx;
const fixed_t gunY = gun->y + gun->momy;
const fixed_t gunZ = P_GetMobjFeet(gun) + gun->momz;
PohbeeMoveTo(laser, gunX, gunY, gun->floorz);
if (ShrinkLaserActive(pohbee) == true)
{
mobj_t *particle = NULL;
laser->renderflags &= ~RF_DONTDRAW;
laser->color = gun->color;
if (leveltime & 1)
{
laser->spritexscale = 5*FRACUNIT/2;
}
else
{
laser->spritexscale = FRACUNIT;
}
laser->spriteyscale = FixedDiv(FixedDiv(gunZ - gun->floorz, mapobjectscale), laser->info->height);
particle = P_SpawnMobjFromMobj(
laser,
P_RandomRange(-16, 16) * FRACUNIT,
P_RandomRange(-16, 16) * FRACUNIT,
0,
MT_SHRINK_PARTICLE
);
P_SetTarget(&gun_pohbee(particle), pohbee);
particle->color = laser->color;
P_SetScale(particle, particle->scale * 2);
particle->destscale = 0;
//particle->momz = 2 * particle->scale * P_MobjFlip(particle);
}
else
{
laser->renderflags |= RF_DONTDRAW;
}
}
static void DoGunChains(mobj_t *gun, mobj_t *pohbee)
{
const fixed_t gunX = gun->x + gun->momx;
const fixed_t gunY = gun->y + gun->momy;
const fixed_t gunZ = P_GetMobjHead(gun) + gun->momz;
const fixed_t beeX = pohbee->x + pohbee->momx;
const fixed_t beeY = pohbee->y + pohbee->momy;
const fixed_t beeZ = P_GetMobjFeet(pohbee) + pohbee->momz;
const fixed_t offsetX = (beeX - gunX) / gun_numsegs(gun);
const fixed_t offsetY = (beeY - gunY) / gun_numsegs(gun);
const fixed_t offsetZ = (beeZ - gunZ) / gun_numsegs(gun);
mobj_t *chain = NULL;
fixed_t curX = gunX + (offsetX / 2);
fixed_t curY = gunY + (offsetY / 2);
fixed_t curZ = gunZ + (offsetZ / 2);
chain = gun_chains(gun);
while (chain != NULL && P_MobjWasRemoved(chain) == false)
{
PohbeeMoveTo(chain, curX, curY, curZ);
curX += offsetX;
curY += offsetY;
curZ += offsetZ;
chain = gun_chains(chain);
}
}
static void ShrinkGunThinker(mobj_t *gun)
{
mobj_t *pohbee = gun_pohbee(gun);
if (pohbee == NULL || P_MobjWasRemoved(pohbee) == true)
{
P_RemoveMobj(gun);
return;
}
gun->angle = pohbee->angle;
gun->color = ShrinkLaserColor(pohbee);
DoGunSwing(gun, pohbee);
if (gun_laser(gun) != NULL && P_MobjWasRemoved(gun_laser(gun)) == false)
{
ShrinkLaserThinker(pohbee, gun, gun_laser(gun));
}
DoGunChains(gun, pohbee);
}
void Obj_PohbeeThinker(mobj_t *pohbee)
{
mobj_t *gun = NULL;
pohbee->momx = pohbee->momy = pohbee->momz = 0;
switch (pohbee_mode(pohbee))
{
case POHBEE_MODE_SPAWN:
PohbeeSpawn(pohbee);
break;
case POHBEE_MODE_ACT:
PohbeeAct(pohbee);
break;
case POHBEE_MODE_DESPAWN:
PohbeeDespawn(pohbee);
break;
default:
// failsafe
pohbee_mode(pohbee) = POHBEE_MODE_SPAWN;
break;
}
gun = pohbee_guns(pohbee);
while (gun != NULL && P_MobjWasRemoved(gun) == false)
{
ShrinkGunThinker(gun);
gun = pohbee_guns(gun);
}
}
void Obj_PohbeeRemoved(mobj_t *pohbee)
{
mobj_t *gun = pohbee_guns(pohbee);
while (gun != NULL && P_MobjWasRemoved(gun) == false)
{
mobj_t *nextGun = pohbee_guns(gun);
P_RemoveMobj(gun);
gun = nextGun;
}
}
void Obj_ShrinkGunRemoved(mobj_t *gun)
{
mobj_t *chain = NULL;
if (gun_laser(gun) != NULL && P_MobjWasRemoved(gun_laser(gun)) == false)
{
P_RemoveMobj(gun_laser(gun));
}
chain = gun_chains(gun);
while (chain != NULL && P_MobjWasRemoved(chain) == false)
{
mobj_t *nextChain = gun_chains(chain);
P_RemoveMobj(chain);
chain = nextChain;
}
}
boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim)
{
mobj_t *pohbee = gun_pohbee(gun);
mobj_t *owner = NULL;
INT32 prevTimer = 0;
if (pohbee == NULL || P_MobjWasRemoved(pohbee) == true)
{
return true;
}
if (ShrinkLaserActive(pohbee) == false)
{
return true;
}
if (victim->player->shrinkLaserDelay > 0)
{
victim->player->shrinkLaserDelay = TICRATE;
return true;
}
victim->player->shrinkLaserDelay = TICRATE;
owner = pohbee_owner(pohbee);
prevTimer = victim->player->growshrinktimer;
if (owner != NULL && victim == owner)
{
// Belongs to us. Give us Grow!
if (prevTimer < 0)
{
// Take away Shrink.
K_RemoveGrowShrink(victim->player);
}
else
{
victim->player->growshrinktimer += 3*TICRATE;
S_StartSound(victim, sfx_kc5a);
if (prevTimer <= 0)
{
victim->scalespeed = mapobjectscale/TICRATE;
victim->destscale = FixedMul(mapobjectscale, GROW_SCALE);
if (K_PlayerShrinkCheat(victim->player) == true)
{
victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE);
}
if (victim->player->invincibilitytimer > 0)
{
; // invincibility has priority in P_RestoreMusic, no point in starting here
}
else if (P_IsLocalPlayer(victim->player) == true)
{
S_ChangeMusicSpecial("kgrow");
}
else //used to be "if (P_IsDisplayPlayer(victim->player) == false)"
{
S_StartSound(victim, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow));
}
P_RestoreMusic(victim->player);
}
}
}
else
{
if (prevTimer > 0)
{
// Take away Grow.
K_RemoveGrowShrink(victim->player);
}
else
{
// Start shrinking!
victim->player->growshrinktimer -= 5*TICRATE;
S_StartSound(victim, sfx_kc59);
if (prevTimer >= 0)
{
//K_DropItems(victim->player);
victim->scalespeed = mapobjectscale/TICRATE;
victim->destscale = FixedMul(mapobjectscale, SHRINK_SCALE);
if (K_PlayerShrinkCheat(victim->player) == true)
{
victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE);
}
}
}
}
return true;
}
static waypoint_t *GetPohbeeWaypoint(waypoint_t *anchor, const UINT32 traveldist, const boolean huntbackwards)
{
const boolean useshortcuts = false;
boolean pathfindsuccess = false;
path_t pathtofinish = {0};
waypoint_t *ret = NULL;
pathfindsuccess = K_PathfindThruCircuitSpawnable(
anchor, traveldist,
&pathtofinish,
useshortcuts, huntbackwards
);
if (pathfindsuccess == true)
{
ret = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata;
Z_Free(pathtofinish.array);
}
else
{
ret = anchor;
}
return ret;
}
static waypoint_t *GetPohbeeStart(waypoint_t *anchor)
{
const UINT32 traveldist = FixedMul(POHBEE_DIST >> 1, mapobjectscale) / FRACUNIT;
const boolean huntbackwards = true;
return GetPohbeeWaypoint(anchor, traveldist, huntbackwards);
}
static waypoint_t *GetPohbeeEnd(waypoint_t *anchor)
{
const UINT32 traveldist = FixedMul(POHBEE_DIST, mapobjectscale) / FRACUNIT;
const boolean huntbackwards = false;
return GetPohbeeWaypoint(anchor, traveldist, huntbackwards);
}
static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UINT8 numLasers)
{
mobj_t *pohbee = NULL;
fixed_t size = 0;
INT32 baseSegs = INT32_MAX;
INT32 segVal = INT32_MAX;
mobj_t *prevGun = NULL;
size_t i, j;
if (owner == NULL || owner->mo == NULL || P_MobjWasRemoved(owner->mo) == true
|| start == NULL || end == NULL
|| numLasers == 0)
{
// Invalid inputs
return;
}
// Calculate number of chain segments added per laser.
size = FixedMul(end->mobj->radius, 3*FRACUNIT/2);
segVal = max(1, 1 + ((size / start->mobj->scale) / CHAIN_SIZE) / numLasers);
baseSegs = segVal * numLasers;
// Valid spawning conditions,
// we can start creating each individual part.
pohbee = P_SpawnMobjFromMobj(start->mobj, 0, 0, (baseSegs * CHAIN_SIZE * FRACUNIT) + POHBEE_HOVER * 3, MT_SHRINK_POHBEE);
P_SetTarget(&pohbee_owner(pohbee), owner->mo);
pohbee_mode(pohbee) = POHBEE_MODE_SPAWN;
pohbee_timer(pohbee) = POHBEE_TIME;
pohbee_height(pohbee) = size;
pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(start);
pohbee_waypoint_dest(pohbee) = (INT32)K_GetWaypointHeapIndex(end);
prevGun = pohbee;
for (i = 0; i < numLasers; i++)
{
const UINT8 numSegs = segVal * (i + 1);
mobj_t *gun = P_SpawnMobjFromMobj(pohbee, 0, 0, 0, MT_SHRINK_GUN);
mobj_t *laser = NULL;
mobj_t *prevChain = NULL;
P_SetTarget(&gun_pohbee(gun), pohbee);
P_SetTarget(&pohbee_guns(prevGun), gun);
gun_numsegs(gun) = numSegs;
gun_offset(gun) = P_RandomKey(GUN_SWINGTIME);
laser = P_SpawnMobjFromMobj(gun, 0, 0, 0, MT_SHRINK_LASER);
P_SetTarget(&gun_laser(gun), laser);
prevChain = gun;
for (j = 0; j < numSegs; j++)
{
mobj_t *chain = P_SpawnMobjFromMobj(gun, 0, 0, 0, MT_SHRINK_CHAIN);
P_SetTarget(&gun_chains(prevChain), chain);
chain_index(chain) = j;
prevChain = chain;
}
prevGun = gun;
}
}
void Obj_CreateShrinkPohbees(player_t *owner)
{
UINT8 ownerPos = 1;
struct {
waypoint_t *start;
waypoint_t *end;
UINT8 lasers;
boolean first;
} pohbees[MAXPLAYERS];
size_t numPohbees = 0;
size_t i, j;
if (owner == NULL || owner->mo == NULL || P_MobjWasRemoved(owner->mo) == true)
{
return;
}
ownerPos = owner->position;
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
waypoint_t *endWaypoint = NULL;
if (playeringame[i] == false)
{
// Not valid.
continue;
}
player = &players[i];
if (player->spectator == true)
{
// Not playing.
continue;
}
if (player->position > ownerPos)
{
// Too far behind.
continue;
}
if (player->nextwaypoint == NULL)
{
// No waypoint?
continue;
}
endWaypoint = GetPohbeeEnd(player->nextwaypoint);
for (j = 0; j < numPohbees; j++)
{
if (pohbees[j].end == endWaypoint)
{
// Increment laser count for the already existing poh-bee,
// if another one would occupy the same space.
pohbees[j].lasers++;
break;
}
}
if (j == numPohbees)
{
// Push a new poh-bee
pohbees[j].start = GetPohbeeStart(player->nextwaypoint);
pohbees[j].end = endWaypoint;
pohbees[j].lasers = 1;
if (player->position == 1)
{
pohbees[j].first = true;
}
numPohbees++;
}
}
for (i = 0; i < numPohbees; i++)
{
CreatePohbee(owner, pohbees[i].start, pohbees[i].end, pohbees[i].lasers);
if (pohbees[i].first == true)
{
// Add a chain of extra ones for 1st place.
waypoint_t *prev = pohbees[i].end;
for (j = 0; j < EXTRA_FOR_FIRST; j++)
{
waypoint_t *new = GetPohbeeEnd(prev);
CreatePohbee(owner, prev, new, 1);
prev = new;
}
}
}
}

View file

@ -2474,7 +2474,7 @@ void A_LobShot(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2 >> 16;
mobj_t *shot, *hitspot;
mobj_t *shot;
angle_t an;
fixed_t z;
fixed_t dist;
@ -2502,11 +2502,6 @@ void A_LobShot(mobj_t *actor)
shot->destscale = actor->scale;
P_SetScale(shot, actor->scale);
// Keep track of where it's going to land
hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL);
hitspot->tics = airtime;
P_SetTarget(&shot->tracer, hitspot);
P_SetTarget(&shot->target, actor); // where it came from
P_InitAngle(shot, actor->angle);
@ -3142,20 +3137,18 @@ void A_SkullAttack(mobj_t *actor)
actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90;
else if (locvar1 == 3)
{
statenum_t oldspawnstate = mobjinfo[MT_NULL].spawnstate;
UINT32 oldflags = mobjinfo[MT_NULL].flags;
fixed_t oldradius = mobjinfo[MT_NULL].radius;
fixed_t oldheight = mobjinfo[MT_NULL].height;
mobj_t *check;
statenum_t oldspawnstate = mobjinfo[MT_RAY].spawnstate;
UINT32 oldflags = mobjinfo[MT_RAY].flags;
fixed_t oldradius = mobjinfo[MT_RAY].radius;
fixed_t oldheight = mobjinfo[MT_RAY].height;
INT32 i, j;
static INT32 k;/* static for (at least) GCC 9.1 weirdness */
boolean allow;
angle_t testang = 0;
mobjinfo[MT_NULL].spawnstate = S_INVISIBLE;
mobjinfo[MT_NULL].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP;
mobjinfo[MT_NULL].radius = mobjinfo[actor->type].radius;
mobjinfo[MT_NULL].height = mobjinfo[actor->type].height;
mobjinfo[MT_RAY].spawnstate = S_INVISIBLE;
mobjinfo[MT_RAY].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP;
mobjinfo[MT_RAY].radius = mobjinfo[actor->type].radius;
mobjinfo[MT_RAY].height = mobjinfo[actor->type].height;
if (P_RandomChance(FRACUNIT/2)) // port priority 1?
{
@ -3168,15 +3161,12 @@ void A_SkullAttack(mobj_t *actor)
j = 9;
}
#define dostuff(q) check = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_NULL);\
#define dostuff(q) \
testang = actor->angle + ((i+(q))*ANG10);\
allow = (P_TryMove(check,\
P_ReturnThrustX(check, testang, dist + 2*actor->radius),\
P_ReturnThrustY(check, testang, dist + 2*actor->radius),\
true));\
P_RemoveMobj(check);\
if (allow)\
break;
if (P_CheckMove(actor,\
P_ReturnThrustX(actor, testang, dist + 2*actor->radius),\
P_ReturnThrustY(actor, testang, dist + 2*actor->radius),\
true)) break;
if (P_RandomChance(FRACUNIT/2)) // port priority 2?
{
@ -3202,10 +3192,10 @@ void A_SkullAttack(mobj_t *actor)
#undef dostuff
mobjinfo[MT_NULL].spawnstate = oldspawnstate;
mobjinfo[MT_NULL].flags = oldflags;
mobjinfo[MT_NULL].radius = oldradius;
mobjinfo[MT_NULL].height = oldheight;
mobjinfo[MT_RAY].spawnstate = oldspawnstate;
mobjinfo[MT_RAY].flags = oldflags;
mobjinfo[MT_RAY].radius = oldradius;
mobjinfo[MT_RAY].height = oldheight;
}
an = actor->angle >> ANGLETOFINESHIFT;
@ -3495,7 +3485,7 @@ void A_BossDeath(mobj_t *mo)
}
bossjustdie:
if (LUAh_BossDeath(mo))
if (LUA_HookMobj(mo, MOBJ_HOOK(BossDeath)))
return;
else if (P_MobjWasRemoved(mo))
return;

View file

@ -219,7 +219,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (special->flags & (MF_ENEMY|MF_BOSS) && special->flags2 & MF2_FRET)
return;
if (LUAh_TouchSpecial(special, toucher) || P_MobjWasRemoved(special))
if (LUA_HookTouchSpecial(special, toucher) || P_MobjWasRemoved(special))
return;
if ((special->flags & (MF_ENEMY|MF_BOSS)) && !(special->flags & MF_MISSILE))
@ -973,7 +973,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
target->shadowscale = 0;
}
if (LUAh_MobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target))
if (LUA_HookMobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target))
return;
//K_SetHitLagForObjects(target, inflictor, MAXHITLAGTICS, true);
@ -1861,7 +1861,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// Everything above here can't be forced.
if (!metalrecording)
{
UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage, damagetype);
UINT8 shouldForce = LUA_HookShouldDamage(target, inflictor, source, damage, damagetype);
if (P_MobjWasRemoved(target))
return (shouldForce == 1); // mobj was removed
if (shouldForce == 1)
@ -1887,7 +1887,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (!force && target->flags2 & MF2_FRET) // Currently flashing from being hit
return false;
if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
return true;
if (target->health > 1)
@ -1917,7 +1917,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (!P_KillPlayer(player, inflictor, source, damagetype))
return false;
}
else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
else if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype))
{
return true;
}

View file

@ -410,6 +410,7 @@ boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing);
boolean P_IsLineTripWire(const line_t *ld);
boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y);
boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam);
boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff);
fixed_t P_BaseStepUp(void);
fixed_t P_GetThingStepUp(mobj_t *thing);
boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff);

View file

@ -32,6 +32,7 @@
#include "hu_stuff.h" // SRB2kart
#include "i_system.h" // SRB2kart
#include "k_terrain.h"
#include "k_objects.h"
#include "r_splats.h"
@ -656,7 +657,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
}
{
UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type
UINT8 shouldCollide = LUA_Hook2Mobj(thing, tmthing, MOBJ_HOOK(MobjCollide)); // checks hook for thing's type
if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
return BMIT_CONTINUE; // one of them was removed???
if (shouldCollide == 1)
@ -664,7 +665,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
else if (shouldCollide == 2)
return BMIT_CONTINUE; // force no collide
shouldCollide = LUAh_MobjMoveCollide(tmthing, thing); // checks hook for tmthing's type
shouldCollide = LUA_Hook2Mobj(tmthing, thing, MOBJ_HOOK(MobjMoveCollide)); // checks hook for tmthing's type
if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
return BMIT_CONTINUE; // one of them was removed???
if (shouldCollide == 1)
@ -739,6 +740,81 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
// SRB2kart 011617 - Colission[sic] code for kart items //{
if (thing->type == MT_SHRINK_GUN || thing->type == MT_SHRINK_PARTICLE)
{
if (tmthing->type != MT_PLAYER)
{
return BMIT_CONTINUE;
}
if (thing->type == MT_SHRINK_GUN)
{
// Use special collision for the laser gun.
// The laser sprite itself is just a visual,
// the gun itself does the colliding for us.
if (tmthing->z > thing->z)
{
return BMIT_CONTINUE; // overhead
}
if (tmthing->z + tmthing->height < thing->floorz)
{
return BMIT_CONTINUE; // underneath
}
}
else
{
if (tmthing->z > thing->z + thing->height)
{
return BMIT_CONTINUE; // overhead
}
if (tmthing->z + tmthing->height < thing->z)
{
return BMIT_CONTINUE; // underneath
}
}
return Obj_ShrinkLaserCollide(thing, tmthing) ? BMIT_CONTINUE : BMIT_ABORT;
}
else if (tmthing->type == MT_SHRINK_GUN || tmthing->type == MT_SHRINK_PARTICLE)
{
if (thing->type != MT_PLAYER)
{
return BMIT_CONTINUE;
}
if (tmthing->type == MT_SHRINK_GUN)
{
// Use special collision for the laser gun.
// The laser sprite itself is just a visual,
// the gun itself does the colliding for us.
if (thing->z > tmthing->z)
{
return BMIT_CONTINUE; // overhead
}
if (thing->z + thing->height < tmthing->floorz)
{
return BMIT_CONTINUE; // underneath
}
}
else
{
if (tmthing->z > thing->z + thing->height)
{
return BMIT_CONTINUE; // overhead
}
if (tmthing->z + tmthing->height < thing->z)
{
return BMIT_CONTINUE; // underneath
}
}
return Obj_ShrinkLaserCollide(tmthing, thing) ? BMIT_CONTINUE : BMIT_ABORT;
}
if (tmthing->type == MT_SMK_ICEBLOCK)
{
// see if it went over / under
@ -1640,7 +1716,7 @@ static BlockItReturn_t PIT_CheckLine(line_t *ld)
blockingline = ld;
{
UINT8 shouldCollide = LUAh_MobjLineCollide(tmthing, blockingline); // checks hook for thing's type
UINT8 shouldCollide = LUA_HookMobjLineCollide(tmthing, blockingline); // checks hook for thing's type
if (P_MobjWasRemoved(tmthing))
return BMIT_CONTINUE; // one of them was removed???
if (shouldCollide == 1)
@ -2483,21 +2559,19 @@ fixed_t P_GetThingStepUp(mobj_t *thing)
return maxstep;
}
//
// P_TryMove
// Attempt to move to a new position.
//
boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
static boolean
increment_move
( mobj_t * thing,
fixed_t x,
fixed_t y,
boolean allowdropoff,
fixed_t * return_stairjank)
{
fixed_t tryx = thing->x;
fixed_t tryy = thing->y;
fixed_t oldx = tryx;
fixed_t oldy = tryy;
fixed_t radius = thing->radius;
fixed_t thingtop;
fixed_t startingonground = P_IsObjectOnGround(thing);
fixed_t stairjank = 0;
pslope_t *oldslope = thing->standingslope;
floatok = false;
// reset this to 0 at the start of each trymove call as it's only used here
@ -2643,7 +2717,45 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
}
} while (tryx != x || tryy != y);
if (return_stairjank)
*return_stairjank = stairjank;
return true;
}
//
// P_CheckMove
// Check if a P_TryMove would be successful.
//
boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
{
boolean moveok;
mobj_t *hack = P_SpawnMobjFromMobj(thing, 0, 0, 0, MT_RAY);
hack->radius = thing->radius;
hack->height = thing->height;
moveok = increment_move(hack, x, y, allowdropoff, NULL);
P_RemoveMobj(hack);
return moveok;
}
//
// P_TryMove
// Attempt to move to a new position.
//
boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
{
fixed_t oldx = thing->x;
fixed_t oldy = thing->y;
fixed_t startingonground = P_IsObjectOnGround(thing);
fixed_t stairjank = 0;
pslope_t *oldslope = thing->standingslope;
// The move is ok!
if (!increment_move(thing, x, y, allowdropoff, &stairjank))
return false;
// If it's a pushable object, check if anything is
// standing on top and move it, too.

View file

@ -1574,7 +1574,7 @@ void P_XYMovement(mobj_t *mo)
// blocked move
moved = false;
if (LUAh_MobjMoveBlocked(mo))
if (LUA_HookMobjMoveBlocked(mo, tmhitthing, blockingline))
{
if (P_MobjWasRemoved(mo))
return;
@ -3976,7 +3976,7 @@ static void P_RingThinker(mobj_t *mobj)
if (!mobj->fuse)
{
if (!LUAh_MobjFuse(mobj))
if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)))
{
mobj->renderflags &= ~RF_DONTDRAW;
spark = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_SIGNSPARKLE); // Spawn a fancy sparkle
@ -5356,7 +5356,7 @@ static boolean P_ParticleGenSceneryThink(mobj_t *mobj)
static void P_MobjSceneryThink(mobj_t *mobj)
{
if (LUAh_MobjThinker(mobj))
if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)))
return;
if (P_MobjWasRemoved(mobj))
return;
@ -6218,7 +6218,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
mobj->fuse--;
if (!mobj->fuse)
{
if (!LUAh_MobjFuse(mobj))
if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)))
P_RemoveMobj(mobj);
return;
}
@ -6238,7 +6238,7 @@ static boolean P_MobjPushableThink(mobj_t *mobj)
static boolean P_MobjBossThink(mobj_t *mobj)
{
if (LUAh_BossThinker(mobj))
if (LUA_HookMobj(mobj, MOBJ_HOOK(BossThinker)))
{
if (P_MobjWasRemoved(mobj))
return false;
@ -7840,6 +7840,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
Obj_HyudoroCenterThink(mobj);
break;
}
case MT_SHRINK_POHBEE:
{
Obj_PohbeeThinker(mobj);
break;
}
case MT_ROCKETSNEAKER:
if (!mobj->target || !mobj->target->health)
{
@ -9159,7 +9164,7 @@ static boolean P_FuseThink(mobj_t *mobj)
if (mobj->fuse)
return true;
if (LUAh_MobjFuse(mobj) || P_MobjWasRemoved(mobj))
if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)) || P_MobjWasRemoved(mobj))
;
else if (mobj->info->flags & MF_MONITOR)
{
@ -9391,13 +9396,13 @@ void P_MobjThinker(mobj_t *mobj)
// Check for a Lua thinker first
if (!mobj->player)
{
if (LUAh_MobjThinker(mobj) || P_MobjWasRemoved(mobj))
if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)) || P_MobjWasRemoved(mobj))
return;
}
else if (!mobj->player->spectator)
{
// You cannot short-circuit the player thinker like you can other thinkers.
LUAh_MobjThinker(mobj);
LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker));
if (P_MobjWasRemoved(mobj))
return;
}
@ -9888,7 +9893,24 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
const mobjinfo_t *info = &mobjinfo[type];
SINT8 sc = -1;
state_t *st;
mobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
mobj_t *mobj;
if (type == MT_NULL)
{
#if 0
#ifdef PARANOIA
I_Error("Tried to spawn MT_NULL\n");
#endif
return NULL;
#endif
// Hack: Some code assumes that P_SpawnMobj can never return NULL
// So replace MT_NULL with MT_RAY in the meantime
// Remove when dealt properly
CONS_Debug(DBG_GAMELOGIC, "Tried to spawn MT_NULL, using MT_RAY\n");
type = MT_RAY;
}
mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
// this is officially a mobj, declared as soon as possible.
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
@ -9989,7 +10011,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
// DANGER! This can cause P_SpawnMobj to return NULL!
// Avoid using P_RemoveMobj on the newly created mobj in "MobjSpawn" Lua hooks!
if (LUAh_MobjSpawn(mobj))
if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjSpawn)))
{
if (P_MobjWasRemoved(mobj))
return NULL;
@ -10328,7 +10350,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
mobj_t *side = P_SpawnMobj(mobj->x + FINECOSINE((ang>>ANGLETOFINESHIFT) & FINEMASK),
mobj->y + FINESINE((ang>>ANGLETOFINESHIFT) & FINEMASK), mobj->z, MT_DAYTONAPINETREE_SIDE);
P_InitAngle(side, ang);
side->target = mobj;
P_SetTarget(&side->target, mobj);
side->threshold = i;
}
break;
@ -10589,7 +10611,7 @@ void P_RemoveMobj(mobj_t *mobj)
return; // something already removing this mobj.
mobj->thinker.function.acp1 = (actionf_p1)P_RemoveThinkerDelayed; // shh. no recursing.
LUAh_MobjRemoved(mobj);
LUA_HookMobj(mobj, MOBJ_HOOK(MobjRemoved));
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; // needed for P_UnsetThingPosition, etc. to work.
// Rings only, please!
@ -10634,6 +10656,16 @@ void P_RemoveMobj(mobj_t *mobj)
P_SetTarget(&mobj->player->followmobj, NULL);
}
if (mobj->type == MT_SHRINK_POHBEE)
{
Obj_PohbeeRemoved(mobj);
}
if (mobj->type == MT_SHRINK_GUN)
{
Obj_ShrinkGunRemoved(mobj);
}
mobj->health = 0; // Just because
// unlink from sector and block lists
@ -10745,6 +10777,7 @@ void P_RemoveSavegameMobj(mobj_t *mobj)
// free block
P_RemoveThinker((thinker_t *)mobj);
R_RemoveMobjInterpolator(mobj);
}
static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}};
@ -12186,7 +12219,7 @@ static void P_SnapToFinishLine(mobj_t *mobj)
static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean *doangle)
{
boolean override = LUAh_MapThingSpawn(mobj, mthing);
boolean override = LUA_HookMapThingSpawn(mobj, mthing);
if (P_MobjWasRemoved(mobj))
return false;

View file

@ -122,7 +122,7 @@ typedef enum
MF_AMBIENT = 1<<10,
// Slide this object when it hits a wall.
MF_SLIDEME = 1<<11,
// Player cheat.
// Don't collide with walls or solid objects. Two MF_NOCLIP objects can't touch each other at all!
MF_NOCLIP = 1<<12,
// Allow moves to any height, no gravity. For active floaters.
MF_FLOAT = 1<<13,

View file

@ -367,6 +367,8 @@ static void P_NetArchivePlayers(void)
WRITEUINT8(save_p, players[i].stairjank);
WRITEUINT8(save_p, players[i].shrinkLaserDelay);
// respawnvars_t
WRITEUINT8(save_p, players[i].respawn.state);
WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].respawn.wp));
@ -654,6 +656,8 @@ static void P_NetUnArchivePlayers(void)
players[i].stairjank = READUINT8(save_p);
players[i].shrinkLaserDelay = READUINT8(save_p);
// respawnvars_t
players[i].respawn.state = READUINT8(save_p);
players[i].respawn.wp = (waypoint_t *)(size_t)READUINT32(save_p);
@ -4193,21 +4197,21 @@ static void P_RelinkPointers(void)
{
temp = (UINT32)(size_t)mobj->hnext;
mobj->hnext = NULL;
if (!(mobj->hnext = P_FindNewPosition(temp)))
if (!P_SetTarget(&mobj->hnext, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "hnext not found on %d\n", mobj->type);
}
if (mobj->hprev)
{
temp = (UINT32)(size_t)mobj->hprev;
mobj->hprev = NULL;
if (!(mobj->hprev = P_FindNewPosition(temp)))
if (!P_SetTarget(&mobj->hprev, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "hprev not found on %d\n", mobj->type);
}
if (mobj->itnext)
{
temp = (UINT32)(size_t)mobj->itnext;
mobj->itnext = NULL;
if (!(mobj->itnext = P_FindNewPosition(temp)))
if (!P_SetTarget(&mobj->itnext, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "itnext not found on %d\n", mobj->type);
}
if (mobj->terrain)
@ -4423,7 +4427,7 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
static void P_NetArchiveMisc(boolean resending)
{
INT32 i;
size_t i;
WRITEUINT32(save_p, ARCHIVEBLOCK_MISC);
@ -4552,11 +4556,25 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT8(save_p, 0x2f);
else
WRITEUINT8(save_p, 0x2e);
WRITEUINT32(save_p, livestudioaudience_timer);
// Only the server uses this, but it
// needs synched for remote admins anyway.
WRITEUINT32(save_p, schedule_len);
for (i = 0; i < schedule_len; i++)
{
scheduleTask_t *task = schedule[i];
WRITEINT16(save_p, task->basetime);
WRITEINT16(save_p, task->timer);
WRITESTRING(save_p, task->command);
}
}
static inline boolean P_NetUnArchiveMisc(boolean reloading)
{
INT32 i;
size_t i;
size_t numTasks;
if (READUINT32(save_p) != ARCHIVEBLOCK_MISC)
I_Error("Bad $$$.sav at archive block Misc");
@ -4700,6 +4718,26 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
if (READUINT8(save_p) == 0x2f)
paused = true;
livestudioaudience_timer = READUINT32(save_p);
// Only the server uses this, but it
// needs synched for remote admins anyway.
Schedule_Clear();
numTasks = READUINT32(save_p);
for (i = 0; i < numTasks; i++)
{
INT16 basetime;
INT16 timer;
char command[MAXTEXTCMD];
basetime = READINT16(save_p);
timer = READINT16(save_p);
READSTRING(save_p, command);
Schedule_Add(basetime, timer, command);
}
return true;
}

View file

@ -66,7 +66,7 @@
#include "md5.h" // map MD5
// for LUAh_MapLoad
// for MapLoad hook
#include "lua_script.h"
#include "lua_hook.h"
@ -4455,7 +4455,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
}
P_PreTicker(2);
P_MapStart(); // just in case MapLoad modifies tmthing
LUAh_MapLoad();
LUA_HookInt(gamemap, HOOK(MapLoad));
P_MapEnd(); // just in case MapLoad modifies tmthing
}

View file

@ -36,7 +36,7 @@
#include "v_video.h" // V_ALLOWLOWERCASE
#include "m_misc.h"
#include "m_cond.h" //unlock triggers
#include "lua_hook.h" // LUAh_LinedefExecute
#include "lua_hook.h" // LUA_HookLinedefExecute
#include "f_finale.h" // control text prompt
#include "r_skins.h" // skins
@ -3032,7 +3032,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
case 443: // Calls a named Lua function
if (line->stringargs[0])
LUAh_LinedefExecute(line, mo, callsec);
LUA_HookLinedefExecute(line, mo, callsec);
else
CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in arg0str)\n", sizeu1(line-lines));
break;

View file

@ -581,7 +581,7 @@ void P_Ticker(boolean run)
ps_lua_mobjhooks = 0;
ps_checkposition_calls = 0;
LUAh_PreThinkFrame();
LUA_HOOK(PreThinkFrame);
ps_playerthink_time = I_GetPreciseTime();
@ -654,7 +654,7 @@ void P_Ticker(boolean run)
}
ps_lua_thinkframe_time = I_GetPreciseTime();
LUAh_ThinkFrame();
LUA_HOOK(ThinkFrame);
ps_lua_thinkframe_time = I_GetPreciseTime() - ps_lua_thinkframe_time;
}
@ -721,7 +721,7 @@ void P_Ticker(boolean run)
G_WriteAllGhostTics();
if (cv_recordmultiplayerdemos.value && (demo.savemode == DSM_NOTSAVING || demo.savemode == DSM_WILLAUTOSAVE))
if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime && G_PlayerInputDown(0, gc_y, 0))
if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime && !menuactive && (G_PlayerInputDown(0, gc_b, 0) || G_PlayerInputDown(0, gc_x, 0)))
demo.savemode = DSM_TITLEENTRY;
}
else if (demo.playback) // Use Ghost data for consistency checks.
@ -748,7 +748,7 @@ void P_Ticker(boolean run)
// Always move the camera.
P_RunChaseCameras();
LUAh_PostThinkFrame();
LUA_HOOK(PostThinkFrame);
if (run)
{
@ -802,7 +802,7 @@ void P_PreTicker(INT32 frames)
K_KartUpdatePosition(&players[i]);
// OK! Now that we got all of that sorted, players can think!
LUAh_PreThinkFrame();
LUA_HOOK(PreThinkFrame);
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
@ -825,7 +825,7 @@ void P_PreTicker(INT32 frames)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
P_PlayerAfterThink(&players[i]);
LUAh_ThinkFrame();
LUA_HOOK(ThinkFrame);
// Run shield positioning
P_RunOverlays();
@ -833,7 +833,7 @@ void P_PreTicker(INT32 frames)
P_UpdateSpecials();
P_RespawnSpecials();
LUAh_PostThinkFrame();
LUA_HOOK(PostThinkFrame);
R_UpdateLevelInterpolators();
R_UpdateViewInterpolation();

View file

@ -665,7 +665,7 @@ boolean P_EvaluateMusicStatus(UINT16 status, const char *musname)
break;
case JT_OTHER: // Other state
result = LUAh_ShouldJingleContinue(&players[i], musname);
result = LUA_HookShouldJingleContinue(&players[i], musname);
break;
case JT_NONE: // Null state
@ -1197,7 +1197,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
ghost->sprite2 = mobj->sprite2;
ghost->frame = mobj->frame;
ghost->tics = -1;
ghost->renderflags |= tr_trans50 << RF_TRANSSHIFT;
ghost->renderflags = (mobj->renderflags & ~RF_TRANSMASK)|RF_TRANS50;
ghost->fuse = ghost->info->damage;
ghost->skin = mobj->skin;
ghost->standingslope = mobj->standingslope;
@ -1207,6 +1207,11 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
ghost->sprzoff = mobj->sprzoff;
ghost->rollangle = mobj->rollangle;
ghost->spritexscale = mobj->spritexscale;
ghost->spriteyscale = mobj->spriteyscale;
ghost->spritexoffset = mobj->spritexoffset;
ghost->spriteyoffset = mobj->spriteyoffset;
if (mobj->flags2 & MF2_OBJECTFLIP)
ghost->flags |= MF2_OBJECTFLIP;
@ -3044,6 +3049,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
subsector_t *newsubsec;
#endif
fixed_t playerScale = FixedDiv(player->mo->scale, mapobjectscale);
fixed_t scaleDiff = playerScale - FRACUNIT;
fixed_t cameraScale = mapobjectscale;
thiscam->old_x = thiscam->x;
thiscam->old_y = thiscam->y;
thiscam->old_z = thiscam->z;
@ -3132,8 +3141,11 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
return true;
}
thiscam->radius = 20*mapobjectscale;
thiscam->height = 16*mapobjectscale;
// Adjust camera to match Grow/Shrink
cameraScale = FixedMul(cameraScale, FRACUNIT + (scaleDiff / 3));
thiscam->radius = 20*cameraScale;
thiscam->height = 16*cameraScale;
// Don't run while respawning from a starpost
// Inu 4/8/13 Why not?!
@ -3159,8 +3171,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
camspeed = cv_cam_speed[num].value;
camstill = cv_cam_still[num].value;
camrotate = cv_cam_rotate[num].value;
camdist = FixedMul(cv_cam_dist[num].value, mapobjectscale);
camheight = FixedMul(cv_cam_height[num].value, mapobjectscale);
camdist = FixedMul(cv_cam_dist[num].value, cameraScale);
camheight = FixedMul(cv_cam_height[num].value, cameraScale);
if (timeover)
{
@ -3171,8 +3183,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
{
const INT32 introcam = (introtime - leveltime);
camrotate += introcam*5;
camdist += (introcam * mapobjectscale)*3;
camheight += (introcam * mapobjectscale)*2;
camdist += (introcam * cameraScale)*3;
camheight += (introcam * cameraScale)*2;
}
else if (player->exiting) // SRB2Kart: Leave the camera behind while exiting, for dramatic effect!
camstill = true;
@ -3236,7 +3248,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
// sets ideal cam pos
{
const fixed_t speedthreshold = 48*mapobjectscale;
const fixed_t speedthreshold = 48*cameraScale;
const fixed_t olddist = P_AproxDistance(mo->x - thiscam->x, mo->y - thiscam->y);
fixed_t lag, distoffset;
@ -3541,7 +3553,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
// point viewed by the camera
// this point is just 64 unit forward the player
dist = 64*mapobjectscale;
dist = 64*cameraScale;
viewpointx = mo->x + FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist) + xpan;
viewpointy = mo->y + FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist) + ypan;
@ -3684,7 +3696,7 @@ boolean P_SpectatorJoinGame(player_t *player)
else
changeto = (P_RandomFixed() & 1) + 1;
if (!LUAh_TeamSwitch(player, changeto, true, false, false))
if (!LUA_HookTeamSwitch(player, changeto, true, false, false))
return false;
}
@ -3710,7 +3722,7 @@ boolean P_SpectatorJoinGame(player_t *player)
{
if (localplayertable[i] == (player-players))
{
LUAh_ViewpointSwitch(player, player, true);
LUA_HookViewpointSwitch(player, player, true);
displayplayers[i] = (player-players);
break;
}
@ -4220,7 +4232,7 @@ void P_PlayerThink(player_t *player)
if (player->playerstate == PST_DEAD)
{
LUAh_PlayerThink(player);
LUA_HookPlayer(player, HOOK(PlayerThink));
return;
}
}
@ -4269,7 +4281,7 @@ void P_PlayerThink(player_t *player)
else
player->mo->renderflags &= ~RF_GHOSTLYMASK;
P_DeathThink(player);
LUAh_PlayerThink(player);
LUA_HookPlayer(player, HOOK(PlayerThink));
return;
}
@ -4474,7 +4486,7 @@ void P_PlayerThink(player_t *player)
if (player->carry == CR_SLIDING)
player->carry = CR_NONE;
LUAh_PlayerThink(player);
LUA_HookPlayer(player, HOOK(PlayerThink));
}
//
@ -4581,7 +4593,7 @@ void P_PlayerAfterThink(player_t *player)
if (player->followmobj)
{
if (LUAh_FollowMobj(player, player->followmobj) || P_MobjWasRemoved(player->followmobj))
if (LUA_HookFollowMobj(player, player->followmobj) || P_MobjWasRemoved(player->followmobj))
{;}
else
{

297
src/r_bbox.c Normal file
View file

@ -0,0 +1,297 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2021 by Sonic Team Junior.
// Copyright (C) 2022 by Kart Krew.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file r_bbox.c
/// \brief Boundary box (cube) renderer
#include "doomdef.h"
#include "command.h"
#include "r_local.h"
#include "screen.h" // cv_renderhitbox
#include "v_video.h" // V_DrawFill
enum {
RENDERHITBOX_OFF,
RENDERHITBOX_TANGIBLE,
RENDERHITBOX_ALL,
RENDERHITBOX_INTANGIBLE,
RENDERHITBOX_RINGS,
};
static CV_PossibleValue_t renderhitbox_cons_t[] = {
{RENDERHITBOX_OFF, "Off"},
{RENDERHITBOX_TANGIBLE, "Tangible"},
{RENDERHITBOX_ALL, "All"},
{RENDERHITBOX_INTANGIBLE, "Intangible"},
{RENDERHITBOX_RINGS, "Rings"},
{0}};
consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", 0, renderhitbox_cons_t, NULL);
struct bbox_col {
INT32 x;
INT32 y;
INT32 h;
};
struct bbox_config {
fixed_t height;
fixed_t tz;
struct bbox_col col[4];
UINT8 color;
};
static inline void
raster_bbox_seg
( INT32 x,
fixed_t y,
fixed_t h,
UINT8 pixel)
{
y /= FRACUNIT;
if (y < 0)
y = 0;
h = y + (FixedCeil(abs(h)) / FRACUNIT);
if (h >= viewheight)
h = viewheight;
while (y < h)
{
topleft[x + y * vid.width] = pixel;
y++;
}
}
static void
draw_bbox_col
( struct bbox_config * bb,
int p,
fixed_t tx,
fixed_t ty)
{
struct bbox_col *col = &bb->col[p];
fixed_t xscale, yscale;
if (ty < FRACUNIT) // projection breaks down here
ty = FRACUNIT;
xscale = FixedDiv(projection[viewssnum], ty);
yscale = FixedDiv(projectiony[viewssnum], ty);
col->x = (centerxfrac + FixedMul(tx, xscale)) / FRACUNIT;
col->y = (centeryfrac - FixedMul(bb->tz, yscale));
col->h = FixedMul(bb->height, yscale);
// Using this function is TOO EASY!
V_DrawFill(
viewwindowx + col->x,
viewwindowy + col->y / FRACUNIT, 1,
col->h / FRACUNIT, V_NOSCALESTART | bb->color);
}
static void
draw_bbox_row
( struct bbox_config * bb,
int p1,
int p2)
{
struct bbox_col
*a = &bb->col[p1],
*b = &bb->col[p2];
INT32 x1, x2; // left, right
INT32 dx; // width
fixed_t y1, y2; // top, bottom
fixed_t s1, s2; // top and bottom increment
if (a->x > b->x)
{
struct bbox_col *c = a;
a = b;
b = c;
}
x1 = a->x;
x2 = b->x;
if (x2 >= viewwidth)
x2 = viewwidth - 1;
if (x1 == x2 || x1 >= viewwidth || x2 < 0)
return;
dx = x2 - x1;
y1 = a->y;
y2 = b->y;
s1 = (y2 - y1) / dx;
y2 = y1 + a->h;
s2 = ((b->y + b->h) - y2) / dx;
// FixedCeil needs a minimum!!! :D :D
if (s1 == 0)
s1 = 1;
if (s2 == 0)
s2 = 1;
if (x1 < 0)
{
y1 -= x1 * s1;
y2 -= x1 * s2;
x1 = 0;
}
while (x1 < x2)
{
raster_bbox_seg(x1, y1, s1, bb->color);
raster_bbox_seg(x1, y2, s2, bb->color);
y1 += s1;
y2 += s2;
x1++;
}
}
UINT8 R_GetBoundingBoxColor(mobj_t *thing)
{
UINT32 flags = thing->flags;
if (thing->player)
return 255; // 0FF
if (flags & (MF_NOCLIPTHING))
return 7; // BFBFBF
if (flags & (MF_SPECIAL))
return 73; // FF0
if (flags & (MF_BOSS|MF_MISSILE|MF_ENEMY|MF_PAIN))
return 35; // F00
if (flags & (MF_NOCLIP))
return 152; // 00F
return 0; // FFF
}
void R_DrawThingBoundingBox(vissprite_t *vis)
{
// radius offsets
fixed_t rs = vis->scale;
fixed_t rc = vis->xscale;
// translated coordinates
fixed_t tx = vis->gx;
fixed_t ty = vis->gy;
struct bbox_config bb = {
.height = vis->thingheight,
.tz = vis->texturemid,
.color = R_GetBoundingBoxColor(vis->mobj),
};
// 1--3
// | |
// 0--2
// left
draw_bbox_col(&bb, 0, tx, ty); // bottom
draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top
// right
tx += rs;
ty += rc;
draw_bbox_col(&bb, 2, tx, ty); // bottom
draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top
// connect all four columns
draw_bbox_row(&bb, 0, 1);
draw_bbox_row(&bb, 1, 3);
draw_bbox_row(&bb, 3, 2);
draw_bbox_row(&bb, 2, 0);
}
static boolean is_tangible (mobj_t *thing)
{
// These objects can never touch another
if (thing->flags & (MF_NOCLIPTHING))
{
return false;
}
// These objects probably do nothing! :D
if ((thing->flags & (MF_SPECIAL|MF_SOLID|MF_SHOOTABLE
|MF_PUSHABLE|MF_BOSS|MF_MISSILE|MF_SPRING
|MF_MONITOR|MF_ENEMY|MF_PAIN|MF_STICKY
|MF_PICKUPFROMBELOW)) == 0U)
{
return false;
}
return true;
}
boolean R_ThingBoundingBoxVisible(mobj_t *thing)
{
INT32 cvmode = cv_renderhitbox.value;
if (thing->type == MT_WAYPOINT)
{
// Waypoints debugger serves this purpose
return false;
}
if (thing == r_viewmobj)
{
// Rendering bbox right on top causes anomalies
return false;
}
switch (cvmode)
{
case RENDERHITBOX_OFF:
return false;
case RENDERHITBOX_ALL:
return true;
case RENDERHITBOX_INTANGIBLE:
return !is_tangible(thing);
case RENDERHITBOX_TANGIBLE:
// Exclude rings from here, lots of them!
if (thing->type == MT_RING)
{
return false;
}
return is_tangible(thing);
case RENDERHITBOX_RINGS:
return (thing->type == MT_RING);
default:
return false;
}
}

View file

@ -1449,6 +1449,104 @@ static void R_ProjectDropShadow(
objectsdrawn++;
}
static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis)
{
fixed_t gx, gy;
fixed_t tx, tz;
vissprite_t *box;
// uncapped/interpolation
interpmobjstate_t interp = {0};
if (!R_ThingBoundingBoxVisible(thing))
{
return;
}
// do interpolation
if (R_UsingFrameInterpolation() && !paused)
{
R_InterpolateMobjState(thing, rendertimefrac, &interp);
}
else
{
R_InterpolateMobjState(thing, FRACUNIT, &interp);
}
// 1--3
// | |
// 0--2
// start in the (0) corner
gx = interp.x - thing->radius - viewx;
gy = interp.y - thing->radius - viewy;
tz = FixedMul(gx, viewcos) + FixedMul(gy, viewsin);
// thing is behind view plane?
// if parent vis is visible, ignore this
if (!vis && (tz < FixedMul(MINZ, interp.scale)))
{
return;
}
tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos);
// too far off the side?
if (!vis && abs(tx) > FixedMul(tz, fovtan[viewssnum])<<2)
{
return;
}
box = R_NewVisSprite();
box->mobj = thing;
box->mobjflags = thing->flags;
box->thingheight = thing->height;
box->cut = SC_BBOX;
box->gx = tx;
box->gy = tz;
box->scale = 2 * FixedMul(thing->radius, viewsin);
box->xscale = 2 * FixedMul(thing->radius, viewcos);
box->pz = interp.z;
box->pzt = box->pz + box->thingheight;
box->gzt = box->pzt;
box->gz = box->pz;
box->texturemid = box->gzt - viewz;
if (vis)
{
box->x1 = vis->x1;
box->x2 = vis->x2;
box->szt = vis->szt;
box->sz = vis->sz;
box->sortscale = vis->sortscale; // link sorting to sprite
box->dispoffset = vis->dispoffset + 5;
box->cut |= SC_LINKDRAW;
}
else
{
fixed_t xscale = FixedDiv(projection[viewssnum], tz);
fixed_t yscale = FixedDiv(projectiony[viewssnum], tz);
fixed_t top = (centeryfrac - FixedMul(box->texturemid, yscale));
box->x1 = (centerxfrac + FixedMul(box->gx, xscale)) / FRACUNIT;
box->x2 = box->x1;
box->szt = top / FRACUNIT;
box->sz = (top + FixedMul(box->thingheight, yscale)) / FRACUNIT;
box->sortscale = yscale;
box->dispoffset = 0;
}
}
//
// R_ProjectSprite
// Generates a vissprite for a thing
@ -1690,8 +1788,14 @@ static void R_ProjectSprite(mobj_t *thing)
if (spriterotangle
&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
{
rollangle = R_GetRollAngle(vflip
? InvAngle(spriterotangle) : spriterotangle);
if ((papersprite && ang >= ANGLE_180) != vflip)
{
rollangle = R_GetRollAngle(InvAngle(spriterotangle));
}
else
{
rollangle = R_GetRollAngle(spriterotangle);
}
rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
if (rotsprite != NULL)
@ -2195,6 +2299,8 @@ static void R_ProjectSprite(mobj_t *thing)
R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz);
}
R_ProjectBoundingBox(oldthing, vis);
// Debug
++objectsdrawn;
}
@ -2429,8 +2535,26 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel)
limit_dist = (fixed_t)(cv_drawdist.value) * mapobjectscale;
for (thing = sec->thinglist; thing; thing = thing->snext)
{
if (R_ThingVisibleWithinDist(thing, limit_dist))
R_ProjectSprite(thing);
if (R_ThingWithinDist(thing, limit_dist))
{
const INT32 oldobjectsdrawn = objectsdrawn;
if (R_ThingVisible(thing))
{
R_ProjectSprite(thing);
}
// I'm so smart :^)
if (objectsdrawn == oldobjectsdrawn)
{
/*
Object is invisible OR is off screen but
render its bbox even if the latter because
radius could be bigger than sprite.
*/
R_ProjectBoundingBox(thing, NULL);
}
}
}
// no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off
@ -2503,6 +2627,10 @@ static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 e
if (dsfirst->cut & SC_SHADOW)
continue;
// don't connect to your bounding box!
if (dsfirst->cut & SC_BBOX)
continue;
// don't connect if it's not the tracer
if (dsfirst->mobj != ds->mobj)
continue;
@ -2946,7 +3074,9 @@ static void R_DrawSprite(vissprite_t *spr)
mfloorclip = spr->clipbot;
mceilingclip = spr->cliptop;
if (spr->cut & SC_SPLAT)
if (spr->cut & SC_BBOX)
R_DrawThingBoundingBox(spr);
else if (spr->cut & SC_SPLAT)
R_DrawFloorSplat(spr);
else
R_DrawVisSprite(spr);
@ -3245,6 +3375,12 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
INT32 x1 = (spr->cut & SC_SPLAT) ? 0 : spr->x1;
INT32 x2 = (spr->cut & SC_SPLAT) ? viewwidth : spr->x2;
if (spr->cut & SC_BBOX)
{
// Do not clip bounding boxes
continue;
}
if (x2 < cx)
{
drawsegs_xrange = drawsegs_xranges[1].items;
@ -3283,18 +3419,14 @@ boolean R_ThingVisible (mobj_t *thing)
return true;
}
boolean R_ThingVisibleWithinDist (mobj_t *thing,
fixed_t limit_dist)
boolean R_ThingWithinDist (mobj_t *thing, fixed_t limit_dist)
{
fixed_t approx_dist;
const fixed_t dist = R_PointToDist(thing->x, thing->y);
if (! R_ThingVisible(thing))
return false;
approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y);
if (limit_dist && approx_dist > limit_dist)
if (limit_dist && dist > limit_dist)
{
return false;
}
return true;
}

View file

@ -66,9 +66,12 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel);
void R_InitSprites(void);
void R_ClearSprites(void);
UINT8 R_GetBoundingBoxColor(mobj_t *thing);
boolean R_ThingBoundingBoxVisible(mobj_t *thing);
boolean R_ThingVisible (mobj_t *thing);
boolean R_ThingVisibleWithinDist (mobj_t *thing,
boolean R_ThingWithinDist (mobj_t *thing,
fixed_t draw_dist);
boolean R_PrecipThingVisible (precipmobj_t *precipthing,
@ -134,6 +137,7 @@ typedef enum
SC_SPLAT = 1<<11,
// srb2kart
SC_SEMIBRIGHT = 1<<12,
SC_BBOX = 1<<13,
// masks
SC_CUTMASK = SC_TOP|SC_BOTTOM,
SC_FLAGMASK = ~SC_CUTMASK
@ -220,6 +224,8 @@ extern UINT32 visspritecount;
void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* portal);
void R_DrawThingBoundingBox(vissprite_t *spr);
UINT8 *R_GetSpriteTranslation(vissprite_t *vis);
// ----------

View file

@ -31,6 +31,7 @@
#include "m_cond.h" // for conditionsets
#include "lua_hook.h" // MusicChange hook
#include "k_boss.h" // bossinfo
#include "byteptr.h"
#ifdef HW3SOUND
// 3D Sound Interface
@ -43,6 +44,8 @@ CV_PossibleValue_t soundvolume_cons_t[] = {{0, "MIN"}, {MAX_VOLUME, "MAX"}, {0,
static void SetChannelsNum(void);
static void Command_Tunes_f(void);
static void Command_RestartAudio_f(void);
static void Command_PlaySound(void);
static void Got_PlaySound(UINT8 **p, INT32 playernum);
// Sound system toggles
static void GameSounds_OnChange(void);
@ -268,6 +271,8 @@ void S_RegisterSoundStuff(void)
COM_AddCommand("tunes", Command_Tunes_f);
COM_AddCommand("restartaudio", Command_RestartAudio_f);
COM_AddCommand("playsound", Command_PlaySound);
RegisterNetXCmd(XD_PLAYSOUND, Got_PlaySound);
}
static void SetChannelsNum(void)
@ -2121,6 +2126,15 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32
{
char newmusic[7];
struct MusicChange hook_param = {
newmusic,
&mflags,
&looping,
&position,
&prefadems,
&fadeinms
};
if (S_MusicDisabled()
|| demo.rewinding // Don't mess with music while rewinding!
|| demo.title) // SRB2Kart: Demos don't interrupt title screen music
@ -2128,7 +2142,7 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32
strncpy(newmusic, mmusic, 7);
if (LUAh_MusicChange(music_name, newmusic, &mflags, &looping, &position, &prefadems, &fadeinms))
if (LUA_HookMusicChange(music_name, &hook_param))
return;
newmusic[6] = 0;
@ -2470,6 +2484,71 @@ static void Command_RestartAudio_f(void)
S_ChangeMusicInternal("titles", looptitle);
}
static void Command_PlaySound(void)
{
const char *sound;
const size_t argc = COM_Argc();
sfxenum_t sfx = NUMSFX;
UINT8 buf[4];
UINT8 *buf_p = buf;
if (argc < 2)
{
CONS_Printf("playsound <name/num>: Plays a sound effect for the entire server.\n");
return;
}
if (client && !IsPlayerAdmin(consoleplayer))
{
CONS_Printf("This can only be used by the server host.\n");
return;
}
sound = COM_Argv(1);
if (*sound >= '0' && *sound <= '9')
{
sfx = atoi(sound);
}
else
{
for (sfx = 0; sfx < sfxfree; sfx++)
{
if (S_sfx[sfx].name && fasticmp(sound, S_sfx[sfx].name))
break;
}
}
if (sfx < 0 || sfx >= NUMSFX)
{
CONS_Printf("Could not find sound effect named \"sfx_%s\".\n", sound);
return;
}
WRITEINT32(buf_p, sfx);
SendNetXCmd(XD_PLAYSOUND, buf, buf_p - buf);
}
static void Got_PlaySound(UINT8 **cp, INT32 playernum)
{
INT32 sound_id = READINT32(*cp);
if (playernum != serverplayer && !IsPlayerAdmin(playernum)) // hacked client, or disasterous bug
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal playsound received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]);
if (server)
SendKick(playernum, KICK_MSG_CON_FAIL);
return;
}
if (sound_id < 0 || sound_id >= NUMSFX)
{
// bad sound effect, ignore
return;
}
S_StartSound(NULL, sound_id);
}
void GameSounds_OnChange(void)
{
if (M_CheckParm("-nosound") || M_CheckParm("-noaudio"))

View file

@ -243,6 +243,16 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst);
// Music Playback
//
/* this is for the sake of the hook */
struct MusicChange {
char * newname;
UINT16 * mflags;
boolean * looping;
UINT32 * position;
UINT32 * prefadems;
UINT32 * fadeinms;
};
enum
{
MUS_SPECIAL = 1,/* powerups--invincibility, grow */

View file

@ -201,7 +201,7 @@ extern CV_PossibleValue_t cv_renderer_t[];
extern INT32 scr_bpp;
extern UINT8 *scr_borderpatch; // patch used to fill the view borders
extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen;
extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_renderhitbox, cv_fullscreen;
extern consvar_t cv_vhseffect, cv_shittyscreen;
// wait for page flipping to end or not

View file

@ -106,8 +106,10 @@ rendermode_t chosenrendermode = render_none; // set by command line arguments
boolean highcolor = false;
static void Impl_SetVsync(void);
// synchronize page flipping with screen refresh
consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE, CV_OnOff, NULL);
consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, Impl_SetVsync);
static consvar_t cv_stretch = CVAR_INIT ("stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL);
static consvar_t cv_alwaysgrabmouse = CVAR_INIT ("alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL);
@ -1041,7 +1043,7 @@ void I_GetEvent(void)
#endif
break;
case SDL_QUIT:
LUAh_GameQuit(true);
LUA_HookBool(true, HOOK(GameQuit));
I_Quit();
break;
}
@ -2002,3 +2004,11 @@ UINT32 I_GetRefreshRate(void)
// trouble querying mode over and over again.
return refresh_rate;
}
static void Impl_SetVsync(void)
{
#if SDL_VERSION_ATLEAST(2,0,18)
if (renderer)
SDL_RenderSetVSync(renderer, cv_vidwait.value);
#endif
}

View file

@ -1114,6 +1114,9 @@ sfxinfo_t S_sfx[NUMSFX] =
{"ffbonc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
// Shout message sound effect
{"sysmsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Server notification"},
// SRB2Kart - Engine sounds
// Engine class A
{"krta00", false, 48, 65, -1, NULL, 0, -1, -1, LUMPERROR, ""},

View file

@ -1179,6 +1179,9 @@ typedef enum
// Fast fall bounce
sfx_ffbonc,
// Shout message sound effect
sfx_sysmsg,
// Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy...
// Engine class A - Low Speed, Low Weight
sfx_krta00,

View file

@ -47,6 +47,7 @@
#include "lua_hudlib_drawlist.h"
#include "lua_hud.h"
#include "lua_hook.h"
// SRB2Kart
#include "k_hud.h" // SRB2kart
@ -965,7 +966,7 @@ luahook:
if (renderisnewtic)
{
LUA_HUD_ClearDrawList(luahuddrawlist_titlecard);
LUAh_TitleCardHUD(stplyr, luahuddrawlist_titlecard);
LUA_HookHUD(luahuddrawlist_titlecard, HUD_HOOK(titlecard));
}
LUA_HUD_DrawList(luahuddrawlist_titlecard);
}
@ -1012,7 +1013,7 @@ static void ST_overlayDrawer(void)
if (renderisnewtic)
{
LUA_HUD_ClearDrawList(luahuddrawlist_game);
LUAh_GameHUD(stplyr, luahuddrawlist_game);
LUA_HookHUD(luahuddrawlist_game, HUD_HOOK(game));
}
LUA_HUD_DrawList(luahuddrawlist_game);
}
@ -1217,7 +1218,7 @@ void ST_Drawer(void)
switch (demo.savemode)
{
case DSM_NOTSAVING:
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Look Backward: Save replay");
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "(B) or (X): Save replay");
break;
case DSM_WILLAUTOSAVE:

View file

@ -2560,6 +2560,30 @@ void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, con
V_DrawThinStringAtFixed(x, y, option, string);
}
// Draws a number using the PING font thingy.
// TODO: Merge number drawing functions into one with "font name" selection.
INT32 V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap)
{
INT32 w = SHORT(fontv[PINGNUM_FONT].font[0]->width); // this SHOULD always be 5 but I guess custom graphics exist.
if (flags & V_NOSCALESTART)
w *= vid.dupx;
if (num < 0)
num = -num;
// draw the number
do
{
x -= (w-1); // Oni wanted their outline to intersect.
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT, flags, fontv[PINGNUM_FONT].font[num%10], colormap);
num /= 10;
} while (num);
return x;
}
void V_DrawCenteredKartString(INT32 x, INT32 y, INT32 option, const char *string)
{
x -= V_KartStringWidth(string, option)/2;
@ -2683,28 +2707,6 @@ void V_DrawProfileNum(INT32 x, INT32 y, INT32 flags, UINT8 num)
} while (--digits);
}
// Draws a number using the PING font thingy.
// TODO: Merge number drawing functions into one with "font name" selection.
void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap)
{
INT32 w = SHORT(fontv[PINGNUM_FONT].font[0]->width); // this SHOULD always be 5 but I guess custom graphics exist.
if (flags & V_NOSCALESTART)
w *= vid.dupx;
if (num < 0)
num = -num;
// draw the number
do
{
x -= (w-1); // Oni wanted their outline to intersect.
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT, flags, fontv[PINGNUM_FONT].font[num%10], colormap);
num /= 10;
} while (num);
}
// Find max height of the string
//
INT32 V_LevelNameHeight(const char *string)

View file

@ -314,7 +314,7 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits)
// Draw ping numbers. Used by the scoreboard and that one ping option. :P
// This is a separate function because IMO lua should have access to it as well.
void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap);
INT32 V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap);
void V_DrawProfileNum(INT32 x, INT32 y, INT32 flags, UINT8 num);

View file

@ -347,7 +347,7 @@ void Y_IntermissionDrawer(void)
if (renderisnewtic)
{
LUA_HUD_ClearDrawList(luahuddrawlist_intermission);
LUAh_IntermissionHUD(luahuddrawlist_intermission);
LUA_HookHUD(luahuddrawlist_intermission, HUD_HOOK(intermission));
}
LUA_HUD_DrawList(luahuddrawlist_intermission);
@ -591,7 +591,7 @@ skiptallydrawer:
switch (demo.savemode)
{
case DSM_NOTSAVING:
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "(B): Save replay");
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "(B) or (X): Save replay");
break;
case DSM_SAVED:
@ -632,7 +632,7 @@ void Y_Ticker(void)
if (demo.recording)
{
if (demo.savemode == DSM_NOTSAVING && G_PlayerInputDown(0, gc_y, 0))
if (demo.savemode == DSM_NOTSAVING && !menuactive && (G_PlayerInputDown(0, gc_b, 0) || G_PlayerInputDown(0, gc_x, 0)))
demo.savemode = DSM_TITLEENTRY;
if (demo.savemode == DSM_WILLSAVE || demo.savemode == DSM_WILLAUTOSAVE)
@ -643,7 +643,7 @@ void Y_Ticker(void)
if (paused || P_AutoPause())
return;
LUAh_IntermissionThinker();
LUA_HOOK(IntermissionThinker);
intertic++;
@ -883,6 +883,7 @@ void Y_StartIntermission(void)
K_CashInPowerLevels();
}
Automate_Run(AEV_INTERMISSIONSTART);
bgpatch = W_CachePatchName("MENUBG", PU_STATIC);
widebgpatch = W_CachePatchName("WEIRDRES", PU_STATIC);
@ -1258,7 +1259,7 @@ void Y_VoteTicker(void)
if (paused || P_AutoPause() || !voteclient.loaded)
return;
LUAh_VoteThinker();
LUA_HOOK(VoteThinker);
votetic++;
@ -1474,6 +1475,7 @@ void Y_VoteTicker(void)
void Y_StartVote(void)
{
INT32 i = 0;
boolean battlemode = ((votelevels[0][1] & ~VOTEMODIFIER_ENCORE) == GT_BATTLE); // todo gametyperules
votetic = -1;
@ -1482,8 +1484,8 @@ void Y_StartVote(void)
I_Error("voteendtic is dirty");
#endif
widebgpatch = W_CachePatchName(((gametype == GT_BATTLE) ? "BATTLSCW" : "INTERSCW"), PU_STATIC);
bgpatch = W_CachePatchName(((gametype == GT_BATTLE) ? "BATTLSCR" : "INTERSCR"), PU_STATIC);
widebgpatch = W_CachePatchName((battlemode ? "BATTLSCW" : "INTERSCW"), PU_STATIC);
bgpatch = W_CachePatchName((battlemode ? "BATTLSCR" : "INTERSCR"), PU_STATIC);
cursor = W_CachePatchName("M_CURSOR", PU_STATIC);
cursor1 = W_CachePatchName("P1CURSOR", PU_STATIC);
cursor2 = W_CachePatchName("P2CURSOR", PU_STATIC);
@ -1515,8 +1517,8 @@ void Y_StartVote(void)
for (i = 0; i < 4; i++)
{
// set up the encore
levelinfo[i].encore = (votelevels[i][1] & 0x80);
votelevels[i][1] &= ~0x80;
levelinfo[i].encore = (votelevels[i][1] & VOTEMODIFIER_ENCORE);
votelevels[i][1] &= ~VOTEMODIFIER_ENCORE;
// set up the levelstring
if (mapheaderinfo[votelevels[i][0]]->levelflags & LF_NOZONE || !mapheaderinfo[votelevels[i][0]]->zonttl[0])
@ -1557,6 +1559,7 @@ void Y_StartVote(void)
}
voteclient.loaded = true;
Automate_Run(AEV_VOTESTART);
}
//