Merge remote-tracking branch 'upstream/next' into lua-music

This commit is contained in:
fickleheart 2020-03-18 22:43:07 -05:00
commit 05b2570ae7
53 changed files with 2339 additions and 1151 deletions

View File

@ -120,6 +120,7 @@ set(SRB2_CORE_RENDER_SOURCES
r_main.c
r_plane.c
r_segs.c
r_skins.c
r_sky.c
r_splats.c
r_things.c
@ -134,6 +135,7 @@ set(SRB2_CORE_RENDER_SOURCES
r_main.h
r_plane.h
r_segs.h
r_skins.h
r_sky.h
r_splats.h
r_state.h
@ -279,6 +281,7 @@ if(${SRB2_CONFIG_HAVE_BLUA})
blua/lfunc.c
blua/lgc.c
blua/linit.c
blua/liolib.c
blua/llex.c
blua/lmem.c
blua/lobject.c

View File

@ -468,6 +468,7 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/r_main.o \
$(OBJDIR)/r_plane.o \
$(OBJDIR)/r_segs.o \
$(OBJDIR)/r_skins.o \
$(OBJDIR)/r_sky.o \
$(OBJDIR)/r_splats.o \
$(OBJDIR)/r_things.o \

View File

@ -18,6 +18,7 @@ OBJS:=$(OBJS) \
$(OBJDIR)/ldo.o \
$(OBJDIR)/lfunc.o \
$(OBJDIR)/linit.o \
$(OBJDIR)/liolib.o \
$(OBJDIR)/llex.o \
$(OBJDIR)/lmem.o \
$(OBJDIR)/lobject.o \

View File

@ -17,6 +17,7 @@
static const luaL_Reg lualibs[] = {
{"", luaopen_base},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_STRLIBNAME, luaopen_string},
{NULL, NULL}
};

635
src/blua/liolib.c Normal file
View File

@ -0,0 +1,635 @@
/*
** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define liolib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "../i_system.h"
#include "../g_game.h"
#include "../d_netfil.h"
#include "../lua_libs.h"
#include "../byteptr.h"
#include "../lua_script.h"
#include "../m_misc.h"
#define IO_INPUT 1
#define IO_OUTPUT 2
#define FILELIMIT (1024 * 1024) // Size limit for reading/writing files
#define FMT_FILECALLBACKID "file_callback_%d"
// Allow scripters to write files of these types to SRB2's folder
static const char *whitelist[] = {
".bmp",
".cfg",
".csv",
".dat",
".png",
".sav2",
".txt",
};
static int pushresult (lua_State *L, int i, const char *filename) {
int en = errno; /* calls to Lua API may change this value */
if (i) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushnil(L);
if (filename)
lua_pushfstring(L, "%s: %s", filename, strerror(en));
else
lua_pushfstring(L, "%s", strerror(en));
lua_pushinteger(L, en);
return 3;
}
}
#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
static int io_type (lua_State *L) {
void *ud;
luaL_checkany(L, 1);
ud = lua_touserdata(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
lua_pushnil(L); /* not a file */
else if (*((FILE **)ud) == NULL)
lua_pushliteral(L, "closed file");
else
lua_pushliteral(L, "file");
return 1;
}
static FILE *tofile (lua_State *L) {
FILE **f = tofilep(L);
if (*f == NULL)
luaL_error(L, "attempt to use a closed file");
return *f;
}
/*
** When creating file handles, always creates a `closed' file handle
** before opening the actual file; so, if there is a memory error, the
** file is not left opened.
*/
static FILE **newfile (lua_State *L) {
FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
*pf = NULL; /* file handle is currently `closed' */
luaL_getmetatable(L, LUA_FILEHANDLE);
lua_setmetatable(L, -2);
return pf;
}
/*
** function to (not) close the standard files stdin, stdout, and stderr
*/
static int io_noclose (lua_State *L) {
lua_pushnil(L);
lua_pushliteral(L, "cannot close standard file");
return 2;
}
/*
** function to close regular files
*/
static int io_fclose (lua_State *L) {
FILE **p = tofilep(L);
int ok = (fclose(*p) == 0);
*p = NULL;
return pushresult(L, ok, NULL);
}
static int aux_close (lua_State *L) {
lua_getfenv(L, 1);
lua_getfield(L, -1, "__close");
return (lua_tocfunction(L, -1))(L);
}
static int io_close (lua_State *L) {
if (lua_isnone(L, 1))
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
tofile(L); /* make sure argument is a file */
return aux_close(L);
}
static int io_gc (lua_State *L) {
FILE *f = *tofilep(L);
/* ignore closed files */
if (f != NULL)
aux_close(L);
return 0;
}
static int io_tostring (lua_State *L) {
FILE *f = *tofilep(L);
if (f == NULL)
lua_pushliteral(L, "file (closed)");
else
lua_pushfstring(L, "file (%p)", f);
return 1;
}
// Create directories in the path
void MakePathDirs(char *path)
{
char *c;
for (c = path; *c; c++)
if (*c == '/' || *c == '\\')
{
char sep = *c;
*c = '\0';
I_mkdir(path, 0755);
*c = sep;
}
}
static int CheckFileName(lua_State *L, const char *filename)
{
int length = strlen(filename);
boolean pass = false;
size_t i;
if (strchr(filename, '\\'))
{
luaL_error(L, "access denied to %s: \\ is not allowed, use / instead", filename);
return pushresult(L,0,filename);
}
for (i = 0; i < (sizeof (whitelist) / sizeof(const char *)); i++)
if (!stricmp(&filename[length - strlen(whitelist[i])], whitelist[i]))
{
pass = true;
break;
}
if (strstr(filename, "./")
|| strstr(filename, "..") || strchr(filename, ':')
|| filename[0] == '/'
|| !pass)
{
luaL_error(L, "access denied to %s", filename);
return pushresult(L,0,filename);
}
return 0;
}
static int io_open (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
int checkresult;
checkresult = CheckFileName(L, filename);
if (checkresult)
return checkresult;
luaL_checktype(L, 3, LUA_TFUNCTION);
if (!(strchr(mode, 'r') || strchr(mode, '+')))
luaL_error(L, "open() is only for reading, use openlocal() for writing");
AddLuaFileTransfer(filename, mode);
return 0;
}
static int io_openlocal (lua_State *L) {
FILE **pf;
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
char *realfilename;
luafiletransfer_t *filetransfer;
int checkresult;
checkresult = CheckFileName(L, filename);
if (checkresult)
return checkresult;
realfilename = va("%s" PATHSEP "%s", luafiledir, filename);
if (client && strnicmp(filename, "client/", strlen("client/")))
I_Error("Access denied to %s\n"
"Clients can only access files stored in luafiles/client/\n",
filename);
// Prevent access if the file is being downloaded
for (filetransfer = luafiletransfers; filetransfer; filetransfer = filetransfer->next)
if (!stricmp(filetransfer->filename, filename))
I_Error("Access denied to %s\n"
"Files can't be opened while being downloaded\n",
filename);
MakePathDirs(realfilename);
// Open and return the file
pf = newfile(L);
*pf = fopen(realfilename, mode);
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}
void Got_LuaFile(UINT8 **cp, INT32 playernum)
{
FILE **pf = NULL;
UINT8 success = READUINT8(*cp); // The first (and only) byte indicates whether the file could be opened
if (playernum != serverplayer)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal luafile command received from %s\n"), player_names[playernum]);
if (server)
SendKick(playernum, KICK_MSG_CON_FAIL);
return;
}
if (!luafiletransfers)
I_Error("No Lua file transfer\n");
// Retrieve the callback and push it on the stack
lua_pushfstring(gL, FMT_FILECALLBACKID, luafiletransfers->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
// Push the first argument (file handle or nil) on the stack
if (success)
{
pf = newfile(gL); // Create and push the file handle
*pf = fopen(luafiletransfers->realfilename, luafiletransfers->mode); // Open the file
if (!*pf)
I_Error("Can't open file \"%s\"\n", luafiletransfers->realfilename); // The file SHOULD exist
}
else
lua_pushnil(gL);
// Push the second argument (file name) on the stack
lua_pushstring(gL, luafiletransfers->filename);
// Call the callback
LUA_Call(gL, 2);
if (success)
{
// Close the file
if (*pf)
{
fclose(*pf);
*pf = NULL;
}
if (client)
remove(luafiletransfers->realfilename);
}
RemoveLuaFileTransfer();
if (server && luafiletransfers)
{
if (FIL_FileOK(luafiletransfers->realfilename))
SV_PrepareSendLuaFileToNextNode();
else
{
// Send a net command with 0 as its first byte to indicate the file couldn't be opened
success = 0;
SendNetXCmd(XD_LUAFILE, &success, 1);
}
}
}
void StoreLuaFileCallback(INT32 id)
{
lua_pushfstring(gL, FMT_FILECALLBACKID, id);
lua_pushvalue(gL, 3); // Parameter 3 is the callback
lua_settable(gL, LUA_REGISTRYINDEX); // registry[callbackid] = callback
}
void RemoveLuaFileCallback(INT32 id)
{
lua_pushfstring(gL, FMT_FILECALLBACKID, id);
lua_pushnil(gL);
lua_settable(gL, LUA_REGISTRYINDEX); // registry[callbackid] = nil
}
static int io_tmpfile (lua_State *L) {
FILE **pf = newfile(L);
*pf = tmpfile();
return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
}
static int io_readline (lua_State *L);
static void aux_lines (lua_State *L, int idx, int toclose) {
lua_pushvalue(L, idx);
lua_pushboolean(L, toclose); /* close/not close file when finished */
lua_pushcclosure(L, io_readline, 2);
}
static int f_lines (lua_State *L) {
tofile(L); /* check that it's a valid file handle */
aux_lines(L, 1, 0);
return 1;
}
/*
** {======================================================
** READ
** =======================================================
*/
static int read_number (lua_State *L, FILE *f) {
lua_Number d;
if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
lua_pushnumber(L, d);
return 1;
}
else return 0; /* read fails */
}
static int test_eof (lua_State *L, FILE *f) {
int c = getc(f);
ungetc(c, f);
lua_pushlstring(L, NULL, 0);
return (c != EOF);
}
static int read_line (lua_State *L, FILE *f) {
luaL_Buffer b;
luaL_buffinit(L, &b);
for (;;) {
size_t l;
char *p = luaL_prepbuffer(&b);
if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */
luaL_pushresult(&b); /* close buffer */
return (lua_objlen(L, -1) > 0); /* check whether read something */
}
l = strlen(p);
if (l == 0 || p[l-1] != '\n')
luaL_addsize(&b, l);
else {
luaL_addsize(&b, l - 1); /* do not include `eol' */
luaL_pushresult(&b); /* close buffer */
return 1; /* read at least an `eol' */
}
}
}
static int read_chars (lua_State *L, FILE *f, size_t n) {
size_t rlen; /* how much to read */
size_t nr; /* number of chars actually read */
luaL_Buffer b;
luaL_buffinit(L, &b);
rlen = LUAL_BUFFERSIZE; /* try to read that much each time */
do {
char *p = luaL_prepbuffer(&b);
if (rlen > n) rlen = n; /* cannot read more than asked */
nr = fread(p, sizeof(char), rlen, f);
luaL_addsize(&b, nr);
n -= nr; /* still have to read `n' chars */
} while (n > 0 && nr == rlen); /* until end of count or eof */
luaL_pushresult(&b); /* close buffer */
return (n == 0 || lua_objlen(L, -1) > 0);
}
static int g_read (lua_State *L, FILE *f, int first) {
int nargs = lua_gettop(L) - 1;
int success;
int n;
clearerr(f);
if (nargs == 0) { /* no arguments? */
success = read_line(L, f);
n = first+1; /* to return 1 result */
}
else { /* ensure stack space for all results and for auxlib's buffer */
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
success = 1;
for (n = first; nargs-- && success; n++) {
if (lua_type(L, n) == LUA_TNUMBER) {
size_t l = (size_t)lua_tointeger(L, n);
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
}
else {
const char *p = lua_tostring(L, n);
luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
switch (p[1]) {
case 'n': /* number */
success = read_number(L, f);
break;
case 'l': /* line */
success = read_line(L, f);
break;
case 'a': /* file */
read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */
success = 1; /* always success */
break;
default:
return luaL_argerror(L, n, "invalid format");
}
}
}
}
if (ferror(f))
return pushresult(L, 0, NULL);
if (!success) {
lua_pop(L, 1); /* remove last result */
lua_pushnil(L); /* push nil instead */
}
return n - first;
}
static int f_read (lua_State *L) {
return g_read(L, tofile(L), 2);
}
static int io_readline (lua_State *L) {
FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
int sucess;
if (f == NULL) /* file is already closed? */
luaL_error(L, "file is already closed");
sucess = read_line(L, f);
if (ferror(f))
return luaL_error(L, "%s", strerror(errno));
if (sucess) return 1;
else { /* EOF */
if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */
lua_settop(L, 0);
lua_pushvalue(L, lua_upvalueindex(1));
aux_close(L); /* close it */
}
return 0;
}
}
/* }====================================================== */
static int g_write (lua_State *L, FILE *f, int arg) {
int nargs = lua_gettop(L) - 1;
int status = 1;
for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */
status = status &&
fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
}
else {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
if (ftell(f) + l > FILELIMIT) {
luaL_error(L,"write limit bypassed in file. Changes have been discarded.");
break;
}
status = status && (fwrite(s, sizeof(char), l, f) == l);
}
}
return pushresult(L, status, NULL);
}
static int f_write (lua_State *L) {
return g_write(L, tofile(L), 2);
}
static int f_seek (lua_State *L) {
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, "cur", modenames);
long offset = luaL_optlong(L, 3, 0);
op = fseek(f, offset, mode[op]);
if (op)
return pushresult(L, 0, NULL); /* error */
else {
lua_pushinteger(L, ftell(f));
return 1;
}
}
static int f_setvbuf (lua_State *L) {
static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
static const char *const modenames[] = {"no", "full", "line", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, NULL, modenames);
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
int res = setvbuf(f, NULL, mode[op], sz);
return pushresult(L, res == 0, NULL);
}
static int f_flush (lua_State *L) {
return pushresult(L, fflush(tofile(L)) == 0, NULL);
}
static const luaL_Reg iolib[] = {
{"close", io_close},
{"open", io_open},
{"openlocal", io_openlocal},
{"tmpfile", io_tmpfile},
{"type", io_type},
{NULL, NULL}
};
static const luaL_Reg flib[] = {
{"close", io_close},
{"flush", f_flush},
{"lines", f_lines},
{"read", f_read},
{"seek", f_seek},
{"setvbuf", f_setvbuf},
{"write", f_write},
{"__gc", io_gc},
{"__tostring", io_tostring},
{NULL, NULL}
};
static void createmeta (lua_State *L) {
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_register(L, NULL, flib); /* file methods */
}
static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
*newfile(L) = f;
if (k > 0) {
lua_pushvalue(L, -1);
lua_rawseti(L, LUA_ENVIRONINDEX, k);
}
lua_pushvalue(L, -2); /* copy environment */
lua_setfenv(L, -2); /* set it */
lua_setfield(L, -3, fname);
}
static void newfenv (lua_State *L, lua_CFunction cls) {
lua_createtable(L, 0, 1);
lua_pushcfunction(L, cls);
lua_setfield(L, -2, "__close");
}
LUALIB_API int luaopen_io (lua_State *L) {
createmeta(L);
/* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
newfenv(L, io_fclose);
lua_replace(L, LUA_ENVIRONINDEX);
/* open library */
luaL_register(L, LUA_IOLIBNAME, iolib);
/* create (and set) default files */
newfenv(L, io_noclose); /* close function for default files */
createstdfile(L, stdin, IO_INPUT, "stdin");
createstdfile(L, stdout, IO_OUTPUT, "stdout");
createstdfile(L, stderr, 0, "stderr");
lua_pop(L, 1); /* pop environment for default files */
return 1;
}

View File

@ -21,6 +21,9 @@ LUALIB_API int (luaopen_base) (lua_State *L);
#define LUA_TABLIBNAME "table"
LUALIB_API int (luaopen_table) (lua_State *L);
#define LUA_IOLIBNAME "io"
LUALIB_API int (luaopen_io) (lua_State *L);
#define LUA_STRLIBNAME "string"
LUALIB_API int (luaopen_string) (lua_State *L);

View File

@ -613,15 +613,6 @@ void CON_Ticker(void)
con_tick++;
con_tick &= 7;
// if the menu is open then close the console.
if (menuactive && con_destlines)
{
consoletoggle = false;
con_destlines = 0;
CON_ClearHUD();
I_UpdateMouseGrab();
}
// console key was pushed
if (consoletoggle)
{
@ -793,7 +784,7 @@ boolean CON_Responder(event_t *ev)
// check other keys only if console prompt is active
if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!!
{
if (bindtable[key])
if (! menuactive && bindtable[key])
{
COM_BufAddText(bindtable[key]);
COM_BufAddText("\n");

View File

@ -1289,6 +1289,37 @@ static boolean CL_SendJoin(void)
return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
}
static INT32 FindRejoinerNum(SINT8 node)
{
char strippednodeaddress[64];
const char *nodeaddress;
char *port;
INT32 i;
// Make sure there is no dead dress before proceeding to the stripping
if (!I_GetNodeAddress)
return -1;
nodeaddress = I_GetNodeAddress(node);
if (!nodeaddress)
return -1;
// Strip the address of its port
strcpy(strippednodeaddress, nodeaddress);
port = strchr(strippednodeaddress, ':');
if (port)
*port = '\0';
// Check if any player matches the stripped address
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX
&& !strcmp(playeraddress[i], strippednodeaddress))
return i;
}
return -1;
}
static void SV_SendServerInfo(INT32 node, tic_t servertime)
{
UINT8 *p;
@ -1306,6 +1337,16 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
if (FindRejoinerNum(node) != -1)
netbuffer->u.serverinfo.refusereason = 0;
else if (!cv_allownewplayer.value)
netbuffer->u.serverinfo.refusereason = 1;
else if (D_NumPlayers() >= cv_maxplayers.value)
netbuffer->u.serverinfo.refusereason = 2;
else
netbuffer->u.serverinfo.refusereason = 0;
strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
sizeof netbuffer->u.serverinfo.gametypename);
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
@ -1863,12 +1904,17 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent)
}
// Quit here rather than downloading files and being refused later.
if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer)
if (serverlist[i].info.refusereason)
{
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING);
if (serverlist[i].info.refusereason == 1)
M_StartMessage(M_GetText("The server is not accepting\njoins for the moment.\n\nPress ESC\n"), NULL, MM_NOTHING);
else if (serverlist[i].info.refusereason == 2)
M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING);
else
M_StartMessage(M_GetText("You can't join.\nI don't know why,\nbut you can't join.\n\nPress ESC\n"), NULL, MM_NOTHING);
return false;
}
@ -2439,14 +2485,14 @@ static void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
if (!playeringame[playernum])
return;
if (server && !demoplayback)
if (server && !demoplayback && playernode[playernum] != UINT8_MAX)
{
INT32 node = playernode[playernum];
playerpernode[node]--;
if (playerpernode[node] <= 0)
{
nodeingame[playernode[playernum]] = false;
Net_CloseConnection(playernode[playernum]);
nodeingame[node] = false;
Net_CloseConnection(node);
ResetNode(node);
}
}
@ -2513,7 +2559,7 @@ static void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
#ifdef HAVE_BLUA
// Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], true);
LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true);
#endif
displayplayer = consoleplayer;
}
@ -2792,16 +2838,13 @@ static void Command_Kick(void)
if (pn == -1 || pn == 0)
return;
if (server)
// Special case if we are trying to kick a player who is downloading the game state:
// trigger a timeout instead of kicking them, because a kick would only
// take effect after they have finished downloading
if (server && playernode[pn] != UINT8_MAX && sendingsavegame[playernode[pn]])
{
// Special case if we are trying to kick a player who is downloading the game state:
// trigger a timeout instead of kicking them, because a kick would only
// take effect after they have finished downloading
if (sendingsavegame[playernode[pn]])
{
Net_ConnectionTimeout(playernode[pn]);
return;
}
Net_ConnectionTimeout(playernode[pn]);
return;
}
WRITESINT8(p, pn);
@ -2859,7 +2902,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
// Is playernum authorized to make this kick?
if (playernum != serverplayer && !IsPlayerAdmin(playernum)
&& !(playerpernode[playernode[playernum]] == 2
&& !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2
&& nodetoplayer2[playernode[playernum]] == pnum))
{
// We received a kick command from someone who isn't the
@ -3018,7 +3061,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
}
else if (keepbody)
{
if (server && !demoplayback)
if (server && !demoplayback && playernode[pnum] != UINT8_MAX)
{
INT32 node = playernode[pnum];
playerpernode[node]--;
@ -3197,6 +3240,10 @@ void D_QuitNetGame(void)
// abort send/receive of files
CloseNetFile();
#ifdef HAVE_BLUA
RemoveAllLuaFileTransfers();
waitingforluafiletransfer = false;
#endif
if (server)
{
@ -3230,37 +3277,6 @@ void D_QuitNetGame(void)
#endif
}
static INT32 FindRejoinerNum(SINT8 node)
{
char strippednodeaddress[64];
const char *nodeaddress;
char *port;
INT32 i;
// Make sure there is no dead dress before proceeding to the stripping
if (!I_GetNodeAddress)
return -1;
nodeaddress = I_GetNodeAddress(node);
if (!nodeaddress)
return -1;
// Strip the address of its port
strcpy(strippednodeaddress, nodeaddress);
port = strchr(strippednodeaddress, ':');
if (port)
*port = '\0';
// Check if any player matches the stripped address
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX
&& !strcmp(playeraddress[i], strippednodeaddress))
return i;
}
return -1;
}
// Adds a node to the game (player will follow at map change or at savegame....)
static inline void SV_AddNode(INT32 node)
{
@ -3591,7 +3607,7 @@ static void HandleConnect(SINT8 node)
rejoinernum = FindRejoinerNum(node);
if (bannednode && bannednode[node])
SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server"));
SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server."));
else if (netbuffer->u.clientcfg._255 != 255 ||
netbuffer->u.clientcfg.packetversion != PACKETVERSION)
SV_SendRefuse(node, "Incompatible packet formats.");
@ -3602,13 +3618,17 @@ static void HandleConnect(SINT8 node)
|| netbuffer->u.clientcfg.subversion != SUBVERSION)
SV_SendRefuse(node, va(M_GetText("Different SRB2 versions cannot\nplay a netgame!\n(server version %d.%d.%d)"), VERSION/100, VERSION%100, SUBVERSION));
else if (!cv_allownewplayer.value && node && rejoinernum == -1)
SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment"));
SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment."));
else if (D_NumPlayers() >= cv_maxplayers.value && rejoinernum == -1)
SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value));
else if (netgame && netbuffer->u.clientcfg.localplayers > 1) // Hacked client?
SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join?
SV_SendRefuse(node, M_GetText("No players from\nthis node."));
#ifdef HAVE_BLUA
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."));
#endif
else
{
#ifndef NONET
@ -3966,7 +3986,7 @@ static void HandlePacketFromPlayer(SINT8 node)
case PT_RESYNCHGET:
if (client)
break;
SV_AcknowledgeResynchAck(netconsole, netbuffer->u.resynchgot);
SV_AcknowledgeResynchAck(node, netbuffer->u.resynchgot);
break;
case PT_CLIENTCMD:
case PT_CLIENT2CMD:
@ -4195,6 +4215,20 @@ static void HandlePacketFromPlayer(SINT8 node)
Net_CloseConnection(node);
nodeingame[node] = false;
break;
#ifdef HAVE_BLUA
case PT_ASKLUAFILE:
if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
{
char *name = va("%s" PATHSEP "%s", luafiledir, luafiletransfers->filename);
boolean textmode = !strchr(luafiletransfers->mode, 'b');
SV_SendLuaFile(node, name, textmode);
}
break;
case PT_HASLUAFILE:
if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING)
SV_HandleLuaFileSent(node);
break;
#endif
// -------------------------------------------- CLIENT RECEIVE ----------
case PT_RESYNCHEND:
// Only accept PT_RESYNCHEND from the server.
@ -4322,6 +4356,12 @@ static void HandlePacketFromPlayer(SINT8 node)
if (client)
Got_Filetxpak();
break;
#ifdef HAVE_BLUA
case PT_SENDINGLUAFILE:
if (client)
CL_PrepareDownloadLuaFile();
break;
#endif
default:
DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
netbuffer->packettype, node));
@ -4943,7 +4983,7 @@ void NetUpdate(void)
PingUpdate();
// update node latency values so we can take an average later.
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
if (playeringame[i] && playernode[i] != UINT8_MAX)
realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
pingmeasurecount++;
}

View File

@ -27,7 +27,7 @@ This version is independent of the mod name, and standard
version and subversion. It should only account for the
basic fields of the packet, and change infrequently.
*/
#define PACKETVERSION 2
#define PACKETVERSION 3
// Network play related stuff.
// There is a data struct that stores network
@ -36,7 +36,7 @@ basic fields of the packet, and change infrequently.
// be transmitted.
// Networking and tick handling related.
#define BACKUPTICS 32
#define BACKUPTICS 96
#define MAXTEXTCMD 256
//
// Packet structure
@ -67,6 +67,12 @@ typedef enum
PT_RESYNCHEND, // Player is now resynched and is being requested to remake the gametic
PT_RESYNCHGET, // Player got resynch packet
#ifdef HAVE_BLUA
PT_SENDINGLUAFILE, // Server telling a client Lua needs to open a file
PT_ASKLUAFILE, // Client telling the server they don't have the file
PT_HASLUAFILE, // Client telling the server they have the file
#endif
// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
@ -361,6 +367,7 @@ typedef struct
UINT8 subversion;
UINT8 numberofplayer;
UINT8 maxplayer;
UINT8 refusereason; // 0: joinable, 1: joins disabled, 2: full
char gametypename[24];
UINT8 modifiedgame;
UINT8 cheatsenabled;

View File

@ -508,13 +508,11 @@ static void D_Display(void)
// vid size change is now finished if it was on...
vid.recalc = 0;
// FIXME: draw either console or menu, not the two
if (gamestate != GS_TIMEATTACK)
CON_Drawer();
M_Drawer(); // menu is drawn even on top of everything
// focus lost moved to M_Drawer
CON_Drawer();
//
// wipe update
//
@ -1126,7 +1124,10 @@ void D_SRB2Main(void)
// can't use sprintf since there is %u in savegamename
strcatbf(savegamename, srb2home, PATHSEP);
#else
#ifdef HAVE_BLUA
snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", srb2home);
#endif
#else // DEFAULTDIR
snprintf(srb2home, sizeof srb2home, "%s", userhome);
snprintf(downloaddir, sizeof downloaddir, "%s", userhome);
if (dedicated)
@ -1136,7 +1137,11 @@ void D_SRB2Main(void)
// can't use sprintf since there is %u in savegamename
strcatbf(savegamename, userhome, PATHSEP);
#ifdef HAVE_BLUA
snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", userhome);
#endif
#endif // DEFAULTDIR
}
configfile[sizeof configfile - 1] = '\0';
@ -1278,11 +1283,18 @@ void D_SRB2Main(void)
// Lactozilla: Does the render mode need to change?
if ((setrenderneeded != 0) && (setrenderneeded != rendermode))
{
CONS_Printf("Switching the renderer...\n");
CONS_Printf(M_GetText("Switching the renderer...\n"));
Z_PreparePatchFlush();
// set needpatchflush / needpatchrecache true for D_CheckRendererState
needpatchflush = true;
needpatchrecache = true;
// Set cv_renderer to the new render mode
VID_CheckRenderer();
SCR_ChangeRendererCVars(setrenderneeded);
// check the renderer's state, and then clear setrenderneeded
D_CheckRendererState();
setrenderneeded = 0;
}

View File

@ -715,6 +715,10 @@ void Net_CloseConnection(INT32 node)
InitNode(&nodes[node]);
SV_AbortSendFiles(node);
#ifdef HAVE_BLUA
if (server)
SV_AbortLuaFileTransfer(node);
#endif
I_NetFreeNodenum(node);
#endif
}
@ -799,6 +803,12 @@ static const char *packettypename[NUMPACKETTYPE] =
"RESYNCHEND",
"RESYNCHGET",
#ifdef HAVE_BLUA
"SENDINGLUAFILE",
"ASKLUAFILE",
"HASLUAFILE",
#endif
"FILEFRAGMENT",
"TEXTCMD",
"TEXTCMD2",

View File

@ -22,7 +22,7 @@
#include "g_input.h"
#include "m_menu.h"
#include "r_local.h"
#include "r_things.h"
#include "r_skins.h"
#include "p_local.h"
#include "p_setup.h"
#include "s_sound.h"
@ -417,7 +417,8 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
"SUICIDE",
#ifdef HAVE_BLUA
"LUACMD",
"LUAVAR"
"LUAVAR",
"LUAFILE"
#endif
};
@ -453,6 +454,7 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_RUNSOC, Got_RunSOCcmd);
#ifdef HAVE_BLUA
RegisterNetXCmd(XD_LUACMD, Got_Luacmd);
RegisterNetXCmd(XD_LUAFILE, Got_LuaFile);
#endif
// Remote Administration
@ -2843,7 +2845,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
// Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed.
if (displayplayer != consoleplayer) // You're already viewing yourself. No big deal.
LUAh_ViewpointSwitch(&players[playernum], &players[displayplayer], true);
LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true);
#endif
displayplayer = consoleplayer;
}
@ -3342,10 +3344,6 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
boolean kick = false;
boolean toomany = false;
INT32 i,j;
serverinfo_pak *dummycheck = NULL;
// Shut the compiler up.
(void)dummycheck;
READSTRINGN(*cp, filename, 240);
READMEM(*cp, md5sum, 16);
@ -3753,11 +3751,11 @@ static void ExitMove_OnChange(void)
{
if (players[i].mo->target && players[i].mo->target->type == MT_SIGN)
P_SetTarget(&players[i].mo->target, NULL);
if (players[i].pflags & PF_FINISHED)
P_GiveFinishFlags(&players[i]);
}
CONS_Printf(M_GetText("Players can now move after completing the level.\n"));
}
else

View File

@ -145,6 +145,7 @@ typedef enum
#ifdef HAVE_BLUA
XD_LUACMD, // 22
XD_LUAVAR, // 23
XD_LUAFILE, // 24
#endif
MAXNETXCMD
} netxcmd_t;

View File

@ -69,6 +69,7 @@ typedef struct filetx_s
UINT32 size; // Size of the file
UINT8 fileid;
INT32 node; // Destination
boolean textmode; // For files requested by Lua without the "b" option
struct filetx_s *next; // Next file in the list
} filetx_t;
@ -94,6 +95,13 @@ char downloaddir[512] = "DOWNLOAD";
INT32 lastfilenum = -1;
#endif
#ifdef HAVE_BLUA
luafiletransfer_t *luafiletransfers = NULL;
boolean waitingforluafiletransfer = false;
char luafiledir[256 + 16] = "luafiles";
#endif
/** Fills a serverinfo packet with information about wad files loaded.
*
* \todo Give this function a better name since it is in global scope.
@ -159,6 +167,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
fileneeded[i].file = NULL; // The file isn't open yet
READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name
READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum
fileneeded[i].textmode = false;
}
}
@ -170,6 +179,7 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave)
fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, tmpsave);
fileneeded[0].textmode = false;
}
/** Checks the server to see if we CAN download all the files,
@ -448,6 +458,164 @@ void CL_LoadServerFiles(void)
}
}
#ifdef HAVE_BLUA
void AddLuaFileTransfer(const char *filename, const char *mode)
{
luafiletransfer_t **prevnext; // A pointer to the "next" field of the last transfer in the list
luafiletransfer_t *filetransfer;
static INT32 id;
//CONS_Printf("AddLuaFileTransfer \"%s\"\n", filename);
// Find the last transfer in the list and set a pointer to its "next" field
prevnext = &luafiletransfers;
while (*prevnext)
prevnext = &((*prevnext)->next);
// Allocate file transfer information and append it to the transfer list
filetransfer = malloc(sizeof(luafiletransfer_t));
if (!filetransfer)
I_Error("AddLuaFileTransfer: Out of memory\n");
*prevnext = filetransfer;
filetransfer->next = NULL;
// Allocate the file name
filetransfer->filename = strdup(filename);
if (!filetransfer->filename)
I_Error("AddLuaFileTransfer: Out of memory\n");
// Create and allocate the real file name
if (server)
filetransfer->realfilename = strdup(va("%s" PATHSEP "%s",
luafiledir, filename));
else
filetransfer->realfilename = strdup(va("%s" PATHSEP "client" PATHSEP "$$$%d%d.tmp",
luafiledir, rand(), rand()));
if (!filetransfer->realfilename)
I_Error("AddLuaFileTransfer: Out of memory\n");
strlcpy(filetransfer->mode, mode, sizeof(filetransfer->mode));
if (server)
{
INT32 i;
// Set status to "waiting" for everyone
for (i = 0; i < MAXNETNODES; i++)
filetransfer->nodestatus[i] = LFTNS_WAITING;
if (!luafiletransfers->next) // Only if there is no transfer already going on
{
if (FIL_ReadFileOK(filetransfer->realfilename))
SV_PrepareSendLuaFileToNextNode();
else
{
// Send a net command with 0 as its first byte to indicate the file couldn't be opened
UINT8 success = 0;
SendNetXCmd(XD_LUAFILE, &success, 1);
}
}
}
// Store the callback so it can be called once everyone has the file
filetransfer->id = id;
StoreLuaFileCallback(id);
id++;
if (waitingforluafiletransfer)
{
waitingforluafiletransfer = false;
CL_PrepareDownloadLuaFile();
}
}
void SV_PrepareSendLuaFileToNextNode(void)
{
INT32 i;
UINT8 success = 1;
// Find a client to send the file to
for (i = 1; i < MAXNETNODES; i++)
if (nodeingame[i] && luafiletransfers->nodestatus[i] == LFTNS_WAITING) // Node waiting
{
// Tell the client we're about to send them the file
netbuffer->packettype = PT_SENDINGLUAFILE;
if (!HSendPacket(i, true, 0, 0))
I_Error("Failed to send a PT_SENDINGLUAFILE packet\n"); // !!! Todo: Handle failure a bit better lol
luafiletransfers->nodestatus[i] = LFTNS_ASKED;
return;
}
// No client found, everyone has the file
// Send a net command with 1 as its first byte to indicate the file could be opened
SendNetXCmd(XD_LUAFILE, &success, 1);
}
void SV_HandleLuaFileSent(UINT8 node)
{
luafiletransfers->nodestatus[node] = LFTNS_SENT;
SV_PrepareSendLuaFileToNextNode();
}
void RemoveLuaFileTransfer(void)
{
luafiletransfer_t *filetransfer = luafiletransfers;
RemoveLuaFileCallback(filetransfer->id);
luafiletransfers = filetransfer->next;
free(filetransfer->filename);
free(filetransfer->realfilename);
free(filetransfer);
}
void RemoveAllLuaFileTransfers(void)
{
while (luafiletransfers)
RemoveLuaFileTransfer();
}
void SV_AbortLuaFileTransfer(INT32 node)
{
if (luafiletransfers
&& (luafiletransfers->nodestatus[node] == LFTNS_ASKED
|| luafiletransfers->nodestatus[node] == LFTNS_SENDING))
{
luafiletransfers->nodestatus[node] = LFTNS_WAITING;
SV_PrepareSendLuaFileToNextNode();
}
}
void CL_PrepareDownloadLuaFile(void)
{
// If there is no transfer in the list, this normally means the server
// called io.open before us, so we have to wait until we call it too
if (!luafiletransfers)
{
waitingforluafiletransfer = true;
return;
}
// Tell the server we are ready to receive the file
netbuffer->packettype = PT_ASKLUAFILE;
HSendPacket(servernode, true, 0, 0);
fileneedednum = 1;
fileneeded[0].status = FS_REQUESTED;
fileneeded[0].totalsize = UINT32_MAX;
fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, luafiletransfers->realfilename);
fileneeded[0].textmode = !strchr(luafiletransfers->mode, 'b');
// Make sure all directories in the file path exist
MakePathDirs(fileneeded[0].filename);
}
#endif
// Number of files to send
// Little optimization to quickly test if there is a file in the queue
static INT32 filestosend = 0;
@ -458,6 +626,7 @@ static INT32 filestosend = 0;
* \param filename The file to send
* \param fileid ???
* \sa SV_SendRam
* \sa SV_SendLuaFile
*
*/
static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
@ -548,6 +717,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
* \param freemethod How to free the block after it has been sent
* \param fileid ???
* \sa SV_SendFile
* \sa SV_SendLuaFile
*
*/
void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
@ -579,6 +749,57 @@ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UI
filestosend++;
}
#ifdef HAVE_BLUA
/** Adds a file requested by Lua to the file list for a node
*
* \param node The node to send the file to
* \param filename The file to send
* \sa SV_SendFile
* \sa SV_SendRam
*
*/
boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode)
{
filetx_t **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request
//INT32 i;
//char wadfilename[MAX_WADPATH];
luafiletransfers->nodestatus[node] = LFTNS_SENDING;
// Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist;
while (*q)
q = &((*q)->next);
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (!p)
I_Error("SV_SendLuaFile: No more memory\n");
// Initialise with zeros
memset(p, 0, sizeof (filetx_t));
// Allocate the file name
p->id.filename = (char *)malloc(MAX_WADPATH); // !!!
if (!p->id.filename)
I_Error("SV_SendLuaFile: No more memory\n");
// Set the file name and get rid of the path
strlcpy(p->id.filename, filename, MAX_WADPATH); // !!!
//nameonly(p->id.filename);
// Open in text mode if required by the Lua script
p->textmode = textmode;
DEBFILE(va("Sending Lua file %s to %d\n", filename, node));
p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it
p->next = NULL; // End of list
filestosend++;
return true;
}
#endif
/** Stops sending a file for a node, and removes the file request from the list,
* either because the file has been fully sent or because the node was disconnected
*
@ -684,7 +905,7 @@ void SV_FileSendTicker(void)
long filesize;
transfer[i].currentfile =
fopen(f->id.filename, "rb");
fopen(f->id.filename, f->textmode ? "r" : "rb");
if (!transfer[i].currentfile)
I_Error("File %s does not exist",
@ -715,11 +936,20 @@ void SV_FileSendTicker(void)
size = f->size-transfer[i].position;
if (ram)
M_Memcpy(p->data, &f->id.ram[transfer[i].position], size);
else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
else
{
size_t n = fread(p->data, 1, size, transfer[i].currentfile);
if (n != size) // Either an error or Windows turning CR-LF into LF
{
if (f->textmode && feof(transfer[i].currentfile))
size = n;
else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
}
}
p->position = LONG(transfer[i].position);
// Put flag so receiver knows the total size
if (transfer[i].position + size == f->size)
if (transfer[i].position + size == f->size || (f->textmode && feof(transfer[i].currentfile)))
p->position |= LONG(0x80000000);
p->fileid = f->fileid;
p->size = SHORT((UINT16)size);
@ -728,7 +958,7 @@ void SV_FileSendTicker(void)
if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND
{ // Success
transfer[i].position = (UINT32)(transfer[i].position + size);
if (transfer[i].position == f->size) // Finish?
if (transfer[i].position == f->size || (f->textmode && feof(transfer[i].currentfile))) // Finish?
SV_EndFileSend(i);
}
else
@ -772,7 +1002,7 @@ void Got_Filetxpak(void)
{
if (file->file)
I_Error("Got_Filetxpak: already open file\n");
file->file = fopen(filename, "wb");
file->file = fopen(filename, file->textmode ? "w" : "wb");
if (!file->file)
I_Error("Can't create file %s: %s", filename, strerror(errno));
CONS_Printf("\r%s...\n",filename);
@ -793,7 +1023,7 @@ void Got_Filetxpak(void)
}
// We can receive packet in the wrong order, anyway all os support gaped file
fseek(file->file, pos, SEEK_SET);
if (fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1)
if (size && fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1)
I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file));
file->currentsize += size;
@ -805,6 +1035,14 @@ void Got_Filetxpak(void)
file->status = FS_FOUND;
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
filename);
#ifdef HAVE_BLUA
if (luafiletransfers)
{
// Tell the server we have received the file
netbuffer->packettype = PT_HASLUAFILE;
HSendPacket(servernode, true, 0, 0);
}
#endif
}
}
else

View File

@ -13,6 +13,7 @@
#ifndef __D_NETFIL__
#define __D_NETFIL__
#include "d_net.h"
#include "w_wad.h"
typedef enum
@ -43,6 +44,7 @@ typedef struct
UINT32 currentsize;
UINT32 totalsize;
filestatus_t status; // The value returned by recsearch
boolean textmode; // For files requested by Lua without the "b" option
} fileneeded_t;
extern INT32 fileneedednum;
@ -70,6 +72,44 @@ boolean CL_CheckDownloadable(void);
boolean CL_SendRequestFile(void);
boolean Got_RequestFilePak(INT32 node);
#ifdef HAVE_BLUA
typedef enum
{
LFTNS_WAITING, // This node is waiting for the server to send the file
LFTNS_ASKED, // The server has told the node they're ready to send the file
LFTNS_SENDING, // The server is sending the file to this node
LFTNS_SENT // The node already has the file
} luafiletransfernodestatus_t;
typedef struct luafiletransfer_s
{
char *filename;
char *realfilename;
char mode[4]; // rb+/wb+/ab+ + null character
INT32 id; // Callback ID
luafiletransfernodestatus_t nodestatus[MAXNETNODES];
struct luafiletransfer_s *next;
} luafiletransfer_t;
extern luafiletransfer_t *luafiletransfers;
extern boolean waitingforluafiletransfer;
extern char luafiledir[256 + 16];
void AddLuaFileTransfer(const char *filename, const char *mode);
void SV_PrepareSendLuaFileToNextNode(void);
boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode);
void SV_PrepareSendLuaFile(const char *filename);
void SV_HandleLuaFileSent(UINT8 node);
void RemoveLuaFileTransfer(void);
void RemoveAllLuaFileTransfers(void);
void SV_AbortLuaFileTransfer(INT32 node);
void CL_PrepareDownloadLuaFile(void);
void Got_LuaFile(UINT8 **cp, INT32 playernum);
void StoreLuaFileCallback(INT32 id);
void RemoveLuaFileCallback(INT32 id);
void MakePathDirs(char *path);
#endif
void SV_AbortSendFiles(INT32 node);
void CloseNetFile(void);

View File

@ -31,6 +31,7 @@
#include "r_data.h"
#include "r_draw.h"
#include "r_patch.h"
#include "r_things.h" // R_Char2Frame
#include "r_sky.h"
#include "fastcmp.h"
#include "lua_script.h"
@ -1249,7 +1250,7 @@ static void readgametype(MYFILE *f, char *gtname)
newgttol = (UINT32)i;
else
{
UINT16 tol = 0;
UINT32 tol = 0;
tmp = strtok(word2,",");
do {
for (i = 0; TYPEOFLEVEL[i].name; i++)
@ -1591,7 +1592,7 @@ static void readlevelheader(MYFILE *f, INT32 num)
mapheaderinfo[num-1]->typeoflevel = (UINT32)i;
else
{
UINT16 tol = 0;
UINT32 tol = 0;
tmp = strtok(word2,",");
do {
for (i = 0; TYPEOFLEVEL[i].name; i++)
@ -7477,7 +7478,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
// Got Flag Sign
"S_GOTFLAG",
// Finish flag
"S_FINISHFLAG",
@ -10236,7 +10237,7 @@ static fixed_t find_const(const char **rword)
free(word);
return r;
}
else if (fastncmp("GT_",word,4)) {
else if (fastncmp("GT_",word,3)) {
r = get_gametype(word);
free(word);
return r;

View File

@ -16,6 +16,7 @@
#include "i_video.h"
#include "v_video.h"
#include "r_state.h" // fadecolormap
#include "r_draw.h" // transtable
#include "p_pspr.h" // tr_transxxx
#include "p_local.h"

View File

@ -38,7 +38,7 @@
#include "byteptr.h"
#include "i_joy.h"
#include "r_local.h"
#include "r_things.h"
#include "r_skins.h"
#include "y_inter.h"
#include "v_video.h"
#include "dehacked.h" // get_number (for ghost thok)
@ -1044,18 +1044,17 @@ static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone)
{
const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT;
INT32 deadzoneAppliedValue = 0;
INT32 adjustedMagnitude = abs(magnitude);
if (jdeadzone > 0)
if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%...
return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0
else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone
{
if (magnitude > jdeadzone)
{
INT32 adjustedMagnitude = abs(magnitude);
adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE);
adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE);
adjustedMagnitude -= jdeadzone;
adjustedMagnitude -= jdeadzone;
deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone);
}
deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone);
}
return deadzoneAppliedValue;
@ -1718,7 +1717,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
#ifdef HAVE_BLUA
// Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(player, &players[displayplayer], true);
LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
#endif
displayplayer = consoleplayer;
}
@ -3592,7 +3591,7 @@ boolean G_CompetitionGametype(void)
* \return The typeoflevel flag to check for that gametype.
* \author Graue <graue@oceanbase.org>
*/
INT16 G_TOLFlag(INT32 pgametype)
UINT32 G_TOLFlag(INT32 pgametype)
{
if (!multiplayer)
return TOL_SP;
@ -3723,7 +3722,7 @@ static void G_DoCompleted(void)
&& (nextmap >= 0 && nextmap < NUMMAPS))
{
register INT16 cm = nextmap;
INT16 tolflag = G_TOLFlag(gametype);
UINT32 tolflag = G_TOLFlag(gametype);
UINT8 visitedmap[(NUMMAPS+7)/8];
memset(visitedmap, 0, sizeof (visitedmap));

View File

@ -309,6 +309,6 @@ FUNCMATH INT32 G_TicsToCentiseconds(tic_t tics);
FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics);
// Don't split up TOL handling
INT16 G_TOLFlag(INT32 pgametype);
UINT32 G_TOLFlag(INT32 pgametype);
#endif

View File

@ -28,7 +28,7 @@
#include "../tables.h"
#include "../sounds.h"
#include "../r_main.h"
#include "../r_things.h"
#include "../r_skins.h"
#include "../m_random.h"
#include "../p_local.h"
#include "hw3dsdrv.h"

View File

@ -234,11 +234,11 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block,
if (mipmap->colormap)
texel = mipmap->colormap[texel];
// transparent pixel
if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)
// If the mipmap is chromakeyed, check if the texel's color
// is equivalent to the chroma key's color index.
alpha = 0xff;
if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX))
alpha = 0x00;
else
alpha = 0xff;
// hope compiler will get this switch out of the loops (dreams...)
// gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)

View File

@ -49,8 +49,7 @@ static const GLubyte white[4] = { 255, 255, 255, 255 };
// ==========================================================================
// With OpenGL 1.1+, the first texture should be 1
#define NOTEXTURE_NUM 1 // small white texture
#define FIRST_TEX_AVAIL (NOTEXTURE_NUM + 1)
static GLuint NOTEXTURE_NUM = 0;
#define N_PI_DEMI (M_PIl/2.0f) //(1.5707963268f)
@ -63,7 +62,6 @@ static float NEAR_CLIPPING_PLANE = NZCLIP_PLANE;
// **************************************************************************
static GLuint NextTexAvail = FIRST_TEX_AVAIL;
static GLuint tex_downloaded = 0;
static GLfloat fov = 90.0f;
#if 0
@ -72,8 +70,8 @@ static FRGBAFloat const_pal_col;
#endif
static FBITFIELD CurrentPolyFlags;
static FTextureInfo* gr_cachetail = NULL;
static FTextureInfo* gr_cachehead = NULL;
static FTextureInfo *gr_cachetail = NULL;
static FTextureInfo *gr_cachehead = NULL;
RGBA_t myPaletteData[256];
GLint screen_width = 0; // used by Draw2DLine()
@ -94,16 +92,10 @@ static GLfloat modelMatrix[16];
static GLfloat projMatrix[16];
static GLint viewport[4];
// Yay for arbitrary numbers! NextTexAvail is buggy for some reason.
// Sryder: NextTexAvail is broken for these because palette changes or changes to the texture filter or antialiasing
// flush all of the stored textures, leaving them unavailable at times such as between levels
// These need to start at 0 and be set to their number, and be reset to 0 when deleted so that intel GPUs
// can know when the textures aren't there, as textures are always considered resident in their virtual memory
// TODO: Store them in a more normal way
#define SCRTEX_SCREENTEXTURE 4294967295U
#define SCRTEX_STARTSCREENWIPE 4294967294U
#define SCRTEX_ENDSCREENWIPE 4294967293U
#define SCRTEX_FINALSCREENTEXTURE 4294967292U
static GLuint screentexture = 0;
static GLuint startScreenWipe = 0;
static GLuint endScreenWipe = 0;
@ -243,6 +235,7 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...)
/* 1.1 functions */
/* texture objects */ //GL_EXT_texture_object
#define pglGenTextures glGenTextures
#define pglDeleteTextures glDeleteTextures
#define pglBindTexture glBindTexture
/* texture mapping */ //GL_EXT_copy_texture
@ -359,6 +352,8 @@ static PFNglFogfv pglFogfv;
/* 1.1 functions */
/* texture objects */ //GL_EXT_texture_object
typedef void (APIENTRY * PFNglGenTextures) (GLsizei n, const GLuint *textures);
static PFNglGenTextures pglGenTextures;
typedef void (APIENTRY * PFNglDeleteTextures) (GLsizei n, const GLuint *textures);
static PFNglDeleteTextures pglDeleteTextures;
typedef void (APIENTRY * PFNglBindTexture) (GLenum target, GLuint texture);
@ -487,6 +482,7 @@ boolean SetupGLfunc(void)
GETOPENGLFUNC(pglFogf , glFogf)
GETOPENGLFUNC(pglFogfv , glFogfv)
GETOPENGLFUNC(pglGenTextures , glGenTextures)
GETOPENGLFUNC(pglDeleteTextures , glDeleteTextures)
GETOPENGLFUNC(pglBindTexture , glBindTexture)
@ -527,6 +523,8 @@ static void SetNoTexture(void)
// Set small white texture.
if (tex_downloaded != NOTEXTURE_NUM)
{
if (NOTEXTURE_NUM == 0)
pglGenTextures(1, &NOTEXTURE_NUM);
pglBindTexture(GL_TEXTURE_2D, NOTEXTURE_NUM);
tex_downloaded = NOTEXTURE_NUM;
}
@ -641,11 +639,6 @@ void SetModelView(GLint w, GLint h)
// -----------------+
void SetStates(void)
{
// Bind little white RGBA texture to ID NOTEXTURE_NUM.
/*
FUINT Data[8*8];
INT32 i;
*/
#ifdef GL_LIGHT_MODEL_AMBIENT
GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
#endif
@ -679,16 +672,8 @@ void SetStates(void)
CurrentPolyFlags = 0xffffffff;
SetBlend(0);
/*
for (i = 0; i < 64; i++)
Data[i] = 0xffFFffFF; // white pixel
*/
tex_downloaded = (GLuint)-1;
tex_downloaded = 0;
SetNoTexture();
//pglBindTexture(GL_TEXTURE_2D, NOTEXTURE_NUM);
//tex_downloaded = NOTEXTURE_NUM;
//pglTexImage2D(GL_TEXTURE_2D, 0, 4, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, Data);
pglPolygonOffset(-1.0f, -1.0f);
@ -723,33 +708,12 @@ void Flush(void)
while (gr_cachehead)
{
// this is not necessary at all, because you have loaded them normally,
// and so they already are in your list!
#if 0
//Hurdler: 25/04/2000: now support colormap in hardware mode
FTextureInfo *tmp = gr_cachehead->nextskin;
// The memory should be freed in the main code
while (tmp)
{
pglDeleteTextures(1, &tmp->downloaded);
tmp->downloaded = 0;
tmp = tmp->nextcolormap;
}
#endif
pglDeleteTextures(1, (GLuint *)&gr_cachehead->downloaded);
if (gr_cachehead->downloaded)
pglDeleteTextures(1, (GLuint *)&gr_cachehead->downloaded);
gr_cachehead->downloaded = 0;
gr_cachehead = gr_cachehead->nextmipmap;
}
gr_cachetail = gr_cachehead = NULL; //Hurdler: well, gr_cachehead is already NULL
NextTexAvail = FIRST_TEX_AVAIL;
#if 0
if (screentexture != FIRST_TEX_AVAIL)
{
pglDeleteTextures(1, &screentexture);
screentexture = FIRST_TEX_AVAIL;
}
#endif
tex_downloaded = 0;
}
@ -1128,8 +1092,10 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo)
static RGBA_t tex[2048*2048];
const GLvoid *ptex = tex;
INT32 w, h;
GLuint texnum = 0;
//DBG_Printf ("DownloadMipmap %d %x\n",NextTexAvail,pTexInfo->grInfo.data);
pglGenTextures(1, &texnum);
//DBG_Printf ("DownloadMipmap %d %x\n",(INT32)texnum,pTexInfo->grInfo.data);
w = pTexInfo->width;
h = pTexInfo->height;
@ -1217,9 +1183,10 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo)
else
DBG_Printf ("SetTexture(bad format) %ld\n", pTexInfo->grInfo.format);
pTexInfo->downloaded = NextTexAvail++;
tex_downloaded = pTexInfo->downloaded;
pglBindTexture(GL_TEXTURE_2D, pTexInfo->downloaded);
// the texture number was already generated by pglGenTextures
pglBindTexture(GL_TEXTURE_2D, texnum);
pTexInfo->downloaded = texnum;
tex_downloaded = texnum;
// disable texture filtering on any texture that has holes so there's no dumb borders or blending issues
if (pTexInfo->flags & TF_TRANSPARENT)
@ -2419,7 +2386,7 @@ EXPORT void HWRAPI(StartScreenWipe) (void)
// Create screen texture
if (firstTime)
startScreenWipe = SCRTEX_STARTSCREENWIPE;
pglGenTextures(1, &startScreenWipe);
pglBindTexture(GL_TEXTURE_2D, startScreenWipe);
if (firstTime)
@ -2450,7 +2417,7 @@ EXPORT void HWRAPI(EndScreenWipe)(void)
// Create screen texture
if (firstTime)
endScreenWipe = SCRTEX_ENDSCREENWIPE;
pglGenTextures(1, &endScreenWipe);
pglBindTexture(GL_TEXTURE_2D, endScreenWipe);
if (firstTime)
@ -2621,7 +2588,7 @@ EXPORT void HWRAPI(MakeScreenTexture) (void)
// Create screen texture
if (firstTime)
screentexture = SCRTEX_SCREENTEXTURE;
pglGenTextures(1, &screentexture);
pglBindTexture(GL_TEXTURE_2D, screentexture);
if (firstTime)
@ -2651,7 +2618,7 @@ EXPORT void HWRAPI(MakeScreenFinalTexture) (void)
// Create screen texture
if (firstTime)
finalScreenTexture = SCRTEX_FINALSCREENTEXTURE;
pglGenTextures(1, &finalScreenTexture);
pglBindTexture(GL_TEXTURE_2D, finalScreenTexture);
if (firstTime)

View File

@ -787,10 +787,12 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
case SKINCOLOR_RED:
case SKINCOLOR_CRIMSON:
case SKINCOLOR_FLAME:
case SKINCOLOR_KETCHUP:
cstart = "\x85"; // V_REDMAP
break;
case SKINCOLOR_YOGURT:
case SKINCOLOR_BROWN:
case SKINCOLOR_BRONZE:
case SKINCOLOR_TAN:
case SKINCOLOR_BEIGE:
case SKINCOLOR_QUAIL:
@ -818,6 +820,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
cstart = "\x8e"; // V_ROSYMAP
break;
case SKINCOLOR_SUNSET:
case SKINCOLOR_COPPER:
case SKINCOLOR_APRICOT:
case SKINCOLOR_ORANGE:
case SKINCOLOR_RUST:
@ -831,6 +834,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
break;
case SKINCOLOR_LIME:
case SKINCOLOR_PERIDOT:
case SKINCOLOR_APPLE:
cstart = "\x8b"; // V_PERIDOTMAP
break;
case SKINCOLOR_SEAFOAM:
@ -851,12 +855,14 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
case SKINCOLOR_BLUE:
case SKINCOLOR_COBALT:
case SKINCOLOR_DUSK:
case SKINCOLOR_BLUEBELL:
cstart = "\x84"; // V_BLUEMAP
break;
case SKINCOLOR_BUBBLEGUM:
case SKINCOLOR_MAGENTA:
case SKINCOLOR_NEON:
case SKINCOLOR_VIOLET:
case SKINCOLOR_RASPBERRY:
cstart = "\x81"; // V_MAGENTAMAP
break;
}

View File

@ -32,10 +32,14 @@ typedef enum
render_none = 3 // for dedicated server
} rendermode_t;
/** \brief currect render mode
/** \brief current render mode
*/
extern rendermode_t rendermode;
/** \brief hardware renderer loaded
0 = never loaded, 1 = loaded successfully, -1 = failed loading
*/
extern INT32 hwrenderloaded;
/** \brief use highcolor modes if true
*/
@ -44,6 +48,9 @@ extern boolean highcolor;
/** \brief setup video mode
*/
void I_StartupGraphics(void);
/** \brief setup hardware mode
*/
void I_StartupHardwareGraphics(void);
/** \brief restore old video mode
@ -82,9 +89,12 @@ INT32 VID_GetModeForSize(INT32 w, INT32 h);
\param modenum video mode to set to
\return currect video mode
\return current video mode
*/
INT32 VID_SetMode(INT32 modenum);
/** \brief Checks the render state
*/
void VID_CheckRenderer(void);
/** \brief The VID_GetModeName function

View File

@ -21,7 +21,7 @@
#include "z_zone.h"
#include "r_main.h"
#include "r_draw.h"
#include "r_things.h"
#include "r_things.h" // R_Frame2Char etc
#include "m_random.h"
#include "s_sound.h"
#include "g_game.h"

View File

@ -15,7 +15,7 @@
#include "doomstat.h"
#include "p_mobj.h"
#include "g_game.h"
#include "r_things.h"
#include "r_skins.h"
#include "b_bot.h"
#include "z_zone.h"
@ -144,6 +144,7 @@ static int lib_addHook(lua_State *L)
case hook_HurtMsg:
case hook_MobjMoveBlocked:
case hook_MapThingSpawn:
case hook_FollowMobj:
hook.s.mt = MT_NULL;
if (lua_isnumber(L, 2))
hook.s.mt = lua_tonumber(L, 2);
@ -206,6 +207,7 @@ static int lib_addHook(lua_State *L)
case hook_MobjRemoved:
case hook_MobjMoveBlocked:
case hook_MapThingSpawn:
case hook_FollowMobj:
lastp = &mobjhooks[hook.s.mt];
break;
case hook_JumpSpecial:
@ -213,7 +215,6 @@ static int lib_addHook(lua_State *L)
case hook_SpinSpecial:
case hook_JumpSpinSpecial:
case hook_PlayerSpawn:
case hook_FollowMobj:
case hook_PlayerCanDamage:
case hook_TeamSwitch:
case hook_ViewpointSwitch:
@ -1367,7 +1368,34 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj)
lua_settop(gL, 0);
for (hookp = playerhooks; hookp; hookp = hookp->next)
// Look for all generic mobj follow item hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
{
if (hookp->type != hook_FollowMobj)
continue;
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, player, META_PLAYER);
LUA_PushUserdata(gL, mobj, META_MOBJ);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -3);
lua_pushvalue(gL, -3);
if (lua_pcall(gL, 2, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (lua_toboolean(gL, -1))
hooked = true;
lua_pop(gL, 1);
}
for (hookp = mobjhooks[mobj->type]; hookp; hookp = hookp->next)
{
if (hookp->type != hook_FollowMobj)
continue;

View File

@ -13,7 +13,7 @@
#include "doomdef.h"
#ifdef HAVE_BLUA
#include "fastcmp.h"
#include "r_things.h"
#include "r_skins.h"
#include "p_local.h"
#include "g_game.h"
#include "p_setup.h"

View File

@ -97,6 +97,10 @@ static int player_get(lua_State *L)
lua_pushboolean(L, true);
else if (fastcmp(field,"name"))
lua_pushstring(L, player_names[plr-players]);
else if (fastcmp(field,"realmo"))
LUA_PushUserdata(L, plr->mo, META_MOBJ);
// Kept for backward-compatibility
// Should be fixed to work like "realmo" later
else if (fastcmp(field,"mo"))
{
if (plr->spectator)
@ -398,7 +402,7 @@ static int player_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
if (fastcmp(field,"mo")) {
if (fastcmp(field,"mo") || fastcmp(field,"realmo")) {
mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
plr->mo->player = NULL; // remove player pointer from old mobj
(newmo->player = plr)->mo = newmo; // set player pointer for new mobj, and set new mobj as the player's mobj

View File

@ -13,7 +13,7 @@
#include "doomdef.h"
#ifdef HAVE_BLUA
#include "fastcmp.h"
#include "r_things.h"
#include "r_skins.h"
#include "sounds.h"
#include "lua_script.h"

View File

@ -18,7 +18,7 @@
#include "v_video.h" // video flags
#include "g_game.h" // record info
#include "r_things.h" // numskins
#include "r_skins.h" // numskins
#include "r_draw.h" // R_GetColorByName
// Map triggers for linedef executors

View File

@ -312,6 +312,7 @@ static void M_AssignJoystick(INT32 choice);
static void M_ChangeControl(INT32 choice);
// Video & Sound
static void M_VideoOptions(INT32 choice);
menu_t OP_VideoOptionsDef, OP_VideoModeDef, OP_ColorOptionsDef;
#ifdef HWRENDER
static void M_OpenGLOptionsMenu(void);
@ -1034,7 +1035,7 @@ static menuitem_t OP_MainMenu[] =
{IT_SUBMENU | IT_STRING, NULL, "Player 2 Controls...", &OP_P2ControlsDef, 20},
{IT_CVAR | IT_STRING, NULL, "Controls per key", &cv_controlperkey, 30},
{IT_SUBMENU | IT_STRING, NULL, "Video Options...", &OP_VideoOptionsDef, 50},
{IT_CALL | IT_STRING, NULL, "Video Options...", M_VideoOptions, 50},
{IT_SUBMENU | IT_STRING, NULL, "Sound Options...", &OP_SoundOptionsDef, 60},
{IT_CALL | IT_STRING, NULL, "Server Options...", M_ServerOptions, 80},
@ -1285,6 +1286,16 @@ static menuitem_t OP_Camera2ExtendedOptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair2, 126},
};
enum
{
op_video_resolution = 1,
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
op_video_fullscreen,
#endif
op_video_vsync,
op_video_renderer,
};
static menuitem_t OP_VideoOptionsMenu[] =
{
{IT_HEADER, NULL, "Screen", NULL, 0},
@ -2083,6 +2094,20 @@ menu_t OP_PlaystyleDef = {
0, 0, 0, NULL
};
static void M_VideoOptions(INT32 choice)
{
(void)choice;
#ifdef HWRENDER
if (hwrenderloaded == -1)
{
OP_VideoOptionsMenu[op_video_renderer].status = (IT_TRANSTEXT | IT_PAIR);
OP_VideoOptionsMenu[op_video_renderer].patch = "Renderer";
OP_VideoOptionsMenu[op_video_renderer].text = "Software";
}
#endif
M_SetupNextMenu(&OP_VideoOptionsDef);
}
menu_t OP_VideoOptionsDef =
{
@ -2179,7 +2204,7 @@ menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE(
menu_t OP_ScreenshotOptionsDef =
{
MN_OP_MAIN + (MN_OP_DATA << 6) + (MN_OP_SCREENSHOTS << 12),
"M_DATA",
"M_SCREEN",
sizeof (OP_ScreenshotOptionsMenu)/sizeof (menuitem_t),
&OP_DataOptionsDef,
OP_ScreenshotOptionsMenu,
@ -3153,6 +3178,9 @@ boolean M_Responder(event_t *ev)
if (gamestate == GS_TITLESCREEN && finalecount < TICRATE)
return false;
if (CON_Ready())
return false;
if (noFurtherInput)
{
// Ignore input after enter/escape/other buttons
@ -3512,6 +3540,7 @@ boolean M_Responder(event_t *ev)
return false;
default:
CON_Responder(ev);
break;
}
@ -5711,6 +5740,8 @@ static void M_DrawNightsAttackSuperSonic(void)
const UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_YELLOW, GTC_CACHE);
INT32 timer = (ntsatkdrawtimer/4) % 2;
angle_t fa = (FixedAngle(((ntsatkdrawtimer * 4) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK;
ntssupersonic[0] = W_CachePatchName("NTSSONC1", PU_PATCH);
ntssupersonic[1] = W_CachePatchName("NTSSONC2", PU_PATCH);
V_DrawFixedPatch(235<<FRACBITS, (120<<FRACBITS) - (8*FINESINE(fa)), FRACUNIT, 0, ntssupersonic[timer], colormap);
}
@ -9949,9 +9980,6 @@ static void M_NightsAttack(INT32 choice)
// This is really just to make sure Sonic is the played character, just in case
M_PatchSkinNameTable();
ntssupersonic[0] = W_CachePatchName("NTSSONC1", PU_PATCH);
ntssupersonic[1] = W_CachePatchName("NTSSONC2", PU_PATCH);
G_SetGamestate(GS_TIMEATTACK); // do this before M_SetupNextMenu so that menu meta state knows that we're switching
titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please
M_SetupNextMenu(&SP_NightsAttackDef);
@ -10071,13 +10099,13 @@ static void M_ReplayTimeAttack(INT32 choice)
static void M_EraseGuest(INT32 choice)
{
const char *rguest = va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value));
(void)choice;
if (FIL_FileExists(rguest))
remove(rguest);
if (currentMenu == &SP_NightsGuestReplayDef)
M_SetupNextMenu(&SP_NightsAttackDef);
else
M_SetupNextMenu(&SP_TimeAttackDef);
if (choice == 'y' || choice == KEY_ENTER)
{
if (FIL_FileExists(rguest))
remove(rguest);
}
M_SetupNextMenu(currentMenu->prevMenu->prevMenu);
Nextmap_OnChange();
M_StartMessage(M_GetText("Guest replay data erased.\n"),NULL,MM_NOTHING);
}
@ -10366,7 +10394,7 @@ static void M_DrawConnectMenu(void)
for (i = 0; i < min(serverlistcount - serverlistpage * SERVERS_PER_PAGE, SERVERS_PER_PAGE); i++)
{
INT32 slindex = i + serverlistpage * SERVERS_PER_PAGE;
UINT32 globalflags = ((serverlist[slindex].info.numberofplayer >= serverlist[slindex].info.maxplayer) ? V_TRANSLUCENT : 0)
UINT32 globalflags = (serverlist[slindex].info.refusereason ? V_TRANSLUCENT : 0)
|((itemOn == FIRSTSERVERLINE+i) ? V_YELLOWMAP : 0)|V_ALLOWLOWERCASE;
V_DrawString(currentMenu->x, S_LINEY(i), globalflags, serverlist[slindex].info.servername);
@ -12110,7 +12138,6 @@ static void M_VideoModeMenu(INT32 choice)
static void M_DrawMainVideoMenu(void)
{
M_DrawGenericScrollMenu();
if (itemOn < 8) // where it starts to go offscreen; change this number if you change the layout of the video menu
{

View File

@ -15,9 +15,10 @@
#ifndef __X_MENU__
#define __X_MENU__
#include "doomstat.h" // for NUMGAMETYPES
#include "d_event.h"
#include "command.h"
#include "r_things.h" // for SKINNAMESIZE
#include "r_skins.h" // for SKINNAMESIZE
#include "f_finale.h" // for ttmode_enum
//

View File

@ -18,7 +18,7 @@
#include <time.h>
#if (defined (NOMD5) || defined (NOMSERV)) && !defined (NONET)
#if (defined (NOMSERV)) && !defined (NONET)
#define NONET
#endif

View File

@ -21,7 +21,7 @@
#include "s_sound.h"
#include "m_random.h"
#include "m_misc.h"
#include "r_things.h"
#include "r_skins.h"
#include "i_video.h"
#include "z_zone.h"
#include "lua_hook.h"

View File

@ -3441,7 +3441,7 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source)
if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super])
return;
if (!cv_friendlyfire.value)
if (!cv_friendlyfire.value && source && source->player)
{
if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
{
@ -3456,7 +3456,7 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source)
return;
}
if (inflictor->type == MT_LHRT)
if (inflictor && inflictor->type == MT_LHRT)
return;
if (player->powers[pw_shield] || player->bot) //If One-Hit Shield

View File

@ -19,7 +19,7 @@
#include "p_local.h"
#include "p_setup.h"
#include "r_main.h"
#include "r_things.h"
#include "r_skins.h"
#include "r_sky.h"
#include "r_splats.h"
#include "s_sound.h"
@ -8001,8 +8001,9 @@ static void P_MobjSceneryThink(mobj_t *mobj)
}
if (mobj->fuse < 0)
return;
if ((--mobj->fuse) < 6)
if (mobj->fuse < 6)
mobj->frame = (mobj->frame & ~FF_TRANSMASK) | ((10 - (mobj->fuse*2)) << (FF_TRANSSHIFT));
mobj->fuse--;
}
break;
case MT_FINISHFLAG:
@ -11594,7 +11595,7 @@ void P_AfterPlayerSpawn(INT32 playernum)
if (CheckForReverseGravity)
P_CheckGravity(mobj, false);
if (p->pflags & PF_FINISHED)
P_GiveFinishFlags(p);
}

View File

@ -22,7 +22,7 @@
#include "p_setup.h"
#include "p_saveg.h"
#include "r_data.h"
#include "r_things.h"
#include "r_skins.h"
#include "r_state.h"
#include "w_wad.h"
#include "y_inter.h"
@ -1406,7 +1406,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
diff |= MD_TICS;
if (mobj->sprite != mobj->state->sprite)
diff |= MD_SPRITE;
if (mobj->sprite == SPR_PLAY && mobj->sprite2 != 0)
if (mobj->sprite == SPR_PLAY && mobj->sprite2 != (mobj->state->frame&FF_FRAMEMASK))
diff |= MD_SPRITE;
if (mobj->frame != mobj->state->frame)
diff |= MD_FRAME;

View File

@ -27,7 +27,7 @@
#include "i_system.h"
#include "r_data.h"
#include "r_things.h"
#include "r_things.h" // for R_AddSpriteDefs
#include "r_patch.h"
#include "r_sky.h"
#include "r_draw.h"
@ -3096,7 +3096,7 @@ static void P_InitTagGametype(void)
//Also, you'd never have to loop through all 32 players slots to find anything ever again.
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !(players[i].spectator && players[i].quittime))
if (playeringame[i] && !(players[i].spectator || players[i].quittime))
{
playersactive[realnumplayers] = i; //stores the player's node in the array.
realnumplayers++;

View File

@ -36,7 +36,7 @@
#include "m_cond.h" //unlock triggers
#include "lua_hook.h" // LUAh_LinedefExecute
#include "f_finale.h" // control text prompt
#include "r_things.h" // skins
#include "r_skins.h" // skins
#ifdef HW3SOUND
#include "hardware/hw3sound.h"

View File

@ -22,7 +22,7 @@
#include "p_local.h"
#include "r_main.h"
#include "s_sound.h"
#include "r_things.h"
#include "r_skins.h"
#include "d_think.h"
#include "r_sky.h"
#include "p_setup.h"
@ -384,6 +384,9 @@ void P_GiveFinishFlags(player_t *player)
if (!player->mo)
return;
if (!(netgame||multiplayer))
return;
for (i = 0; i < 3; i++)
{
angle_t fa = (angle >> ANGLETOFINESHIFT) & FINEMASK;
@ -10655,7 +10658,7 @@ boolean P_SpectatorJoinGame(player_t *player)
#ifdef HAVE_BLUA
// Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(player, &players[displayplayer], true);
LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
#endif
displayplayer = consoleplayer;
}
@ -10704,7 +10707,7 @@ boolean P_SpectatorJoinGame(player_t *player)
#ifdef HAVE_BLUA
// Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(player, &players[displayplayer], true);
LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
#endif
displayplayer = consoleplayer;
}

View File

@ -230,25 +230,30 @@ static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, tex
UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha)
{
RGBA_t output;
INT16 fullalpha = (alpha - (0xFF - foreground.s.alpha));
if (style == AST_TRANSLUCENT)
{
if (alpha == 0)
if (fullalpha <= 0)
output.rgba = background.rgba;
else if (alpha == 0xFF)
output.rgba = foreground.rgba;
else if (alpha < 0xFF)
{
UINT8 beta = (0xFF - alpha);
output.s.red = ((background.s.red * beta) + (foreground.s.red * alpha)) / 0xFF;
output.s.green = ((background.s.green * beta) + (foreground.s.green * alpha)) / 0xFF;
output.s.blue = ((background.s.blue * beta) + (foreground.s.blue * alpha)) / 0xFF;
}
// write foreground pixel alpha
// if there's no pixel in here
if (!background.rgba)
output.s.alpha = foreground.s.alpha;
else
output.s.alpha = 0xFF;
{
// don't go too high
if (fullalpha >= 0xFF)
fullalpha = 0xFF;
alpha = (UINT8)fullalpha;
// if the background pixel is empty, match software and don't blend anything
if (!background.s.alpha)
output.rgba = 0;
else
{
UINT8 beta = (0xFF - alpha);
output.s.red = ((background.s.red * beta) + (foreground.s.red * alpha)) / 0xFF;
output.s.green = ((background.s.green * beta) + (foreground.s.green * alpha)) / 0xFF;
output.s.blue = ((background.s.blue * beta) + (foreground.s.blue * alpha)) / 0xFF;
output.s.alpha = 0xFF;
}
}
return output.rgba;
}
#define clamp(c) max(min(c, 0xFF), 0x00);

825
src/r_skins.c Normal file
View File

@ -0,0 +1,825 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// 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_skins.c
/// \brief Loading skins
#include "doomdef.h"
#include "console.h"
#include "g_game.h"
#include "r_local.h"
#include "st_stuff.h"
#include "w_wad.h"
#include "z_zone.h"
#include "m_misc.h"
#include "info.h" // spr2names
#include "i_video.h" // rendermode
#include "i_system.h"
#include "r_things.h"
#include "r_skins.h"
#include "p_local.h"
#include "dehacked.h" // get_number (for thok)
#include "m_cond.h"
#ifdef HWRENDER
#include "hardware/hw_md2.h"
#endif
#ifdef PC_DOS
#include <stdio.h> // for snprintf
int snprintf(char *str, size_t n, const char *fmt, ...);
//int vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
#endif
INT32 numskins = 0;
skin_t skins[MAXSKINS];
// FIXTHIS: don't work because it must be inistilised before the config load
//#define SKINVALUES
#ifdef SKINVALUES
CV_PossibleValue_t skin_cons_t[MAXSKINS+1];
#endif
//
// P_GetSkinSprite2
// For non-super players, tries each sprite2's immediate predecessor until it finds one with a number of frames or ends up at standing.
// For super players, does the same as above - but tries the super equivalent for each sprite2 before the non-super version.
//
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player)
{
UINT8 super = 0, i = 0;
if (!skin)
return 0;
if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2)
return 0;
while (!skin->sprites[spr2].numframes
&& spr2 != SPR2_STND
&& ++i < 32) // recursion limiter
{
if (spr2 & FF_SPR2SUPER)
{
super = FF_SPR2SUPER;
spr2 &= ~FF_SPR2SUPER;
continue;
}
switch(spr2)
{
// Normal special cases.
case SPR2_JUMP:
spr2 = ((player
? player->charflags
: skin->flags)
& SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL;
break;
case SPR2_TIRE:
spr2 = ((player
? player->charability
: skin->ability)
== CA_SWIM) ? SPR2_SWIM : SPR2_FLY;
break;
// Use the handy list, that's what it's there for!
default:
spr2 = spr2defaults[spr2];
break;
}
spr2 |= super;
}
if (i >= 32) // probably an infinite loop...
return 0;
return spr2;
}
static void Sk_SetDefaultValue(skin_t *skin)
{
INT32 i;
//
// set default skin values
//
memset(skin, 0, sizeof (skin_t));
snprintf(skin->name,
sizeof skin->name, "skin %u", (UINT32)(skin-skins));
skin->name[sizeof skin->name - 1] = '\0';
skin->wadnum = INT16_MAX;
skin->flags = 0;
strcpy(skin->realname, "Someone");
strcpy(skin->hudname, "???");
skin->starttranscolor = 96;
skin->prefcolor = SKINCOLOR_GREEN;
skin->supercolor = SKINCOLOR_SUPERGOLD1;
skin->prefoppositecolor = 0; // use tables
skin->normalspeed = 36<<FRACBITS;
skin->runspeed = 28<<FRACBITS;
skin->thrustfactor = 5;
skin->accelstart = 96;
skin->acceleration = 40;
skin->ability = CA_NONE;
skin->ability2 = CA2_SPINDASH;
skin->jumpfactor = FRACUNIT;
skin->actionspd = 30<<FRACBITS;
skin->mindash = 15<<FRACBITS;
skin->maxdash = 70<<FRACBITS;
skin->radius = mobjinfo[MT_PLAYER].radius;
skin->height = mobjinfo[MT_PLAYER].height;
skin->spinheight = FixedMul(skin->height, 2*FRACUNIT/3);
skin->shieldscale = FRACUNIT;
skin->camerascale = FRACUNIT;
skin->thokitem = -1;
skin->spinitem = -1;
skin->revitem = -1;
skin->followitem = 0;
skin->highresscale = FRACUNIT;
skin->contspeed = 17;
skin->contangle = 0;
skin->availability = 0;
for (i = 0; i < sfx_skinsoundslot0; i++)
if (S_sfx[i].skinsound != -1)
skin->soundsid[S_sfx[i].skinsound] = i;
}
//
// Initialize the basic skins
//
void R_InitSkins(void)
{
#ifdef SKINVALUES
INT32 i;
for (i = 0; i <= MAXSKINS; i++)
{
skin_cons_t[i].value = 0;
skin_cons_t[i].strvalue = NULL;
}
#endif
// no default skin!
numskins = 0;
}
UINT32 R_GetSkinAvailabilities(void)
{
INT32 s;
UINT32 response = 0;
for (s = 0; s < MAXSKINS; s++)
{
if (skins[s].availability && unlockables[skins[s].availability - 1].unlocked)
response |= (1 << s);
}
return response;
}
// returns true if available in circumstances, otherwise nope
// warning don't use with an invalid skinnum other than -1 which always returns true
boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
{
return ((skinnum == -1) // Simplifies things elsewhere, since there's already plenty of checks for less-than-0...
|| (!skins[skinnum].availability)
|| (((netgame || multiplayer) && playernum != -1) ? (players[playernum].availabilities & (1 << skinnum)) : (unlockables[skins[skinnum].availability - 1].unlocked))
|| (modeattacking) // If you have someone else's run you might as well take a look
|| (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1.
|| (netgame && (cv_forceskin.value == skinnum)) // Force 2.
|| (metalrecording && skinnum == 5) // Force 3.
);
}
// returns true if the skin name is found (loaded from pwad)
// warning return -1 if not found
INT32 R_SkinAvailable(const char *name)
{
INT32 i;
for (i = 0; i < numskins; i++)
{
// search in the skin list
if (stricmp(skins[i].name,name)==0)
return i;
}
return -1;
}
// network code calls this when a 'skin change' is received
void SetPlayerSkin(INT32 playernum, const char *skinname)
{
INT32 i = R_SkinAvailable(skinname);
player_t *player = &players[playernum];
if ((i != -1) && R_SkinUsable(playernum, i))
{
SetPlayerSkinByNum(playernum, i);
return;
}
if (P_IsLocalPlayer(player))
CONS_Alert(CONS_WARNING, M_GetText("Skin '%s' not found.\n"), skinname);
else if(server || IsPlayerAdmin(consoleplayer))
CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname);
SetPlayerSkinByNum(playernum, 0);
}
// Same as SetPlayerSkin, but uses the skin #.
// network code calls this when a 'skin change' is received
void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
{
player_t *player = &players[playernum];
skin_t *skin = &skins[skinnum];
UINT8 newcolor = 0;
if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists!
{
player->skin = skinnum;
player->camerascale = skin->camerascale;
player->shieldscale = skin->shieldscale;
player->charability = (UINT8)skin->ability;
player->charability2 = (UINT8)skin->ability2;
player->charflags = (UINT32)skin->flags;
player->thokitem = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem;
player->spinitem = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem;
player->revitem = skin->revitem < 0 ? (mobjtype_t)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem;
player->followitem = skin->followitem;
if (((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK) && (player->revitem == MT_LHRT || player->spinitem == MT_LHRT || player->thokitem == MT_LHRT)) // Healers can't keep their buff.
player->powers[pw_shield] &= SH_STACK;
player->actionspd = skin->actionspd;
player->mindash = skin->mindash;
player->maxdash = skin->maxdash;
player->normalspeed = skin->normalspeed;
player->runspeed = skin->runspeed;
player->thrustfactor = skin->thrustfactor;
player->accelstart = skin->accelstart;
player->acceleration = skin->acceleration;
player->jumpfactor = skin->jumpfactor;
player->height = skin->height;
player->spinheight = skin->spinheight;
if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback))
{
if (playernum == consoleplayer)
CV_StealthSetValue(&cv_playercolor, skin->prefcolor);
else if (playernum == secondarydisplayplayer)
CV_StealthSetValue(&cv_playercolor2, skin->prefcolor);
player->skincolor = newcolor = skin->prefcolor;
}
if (player->followmobj)
{
P_RemoveMobj(player->followmobj);
P_SetTarget(&player->followmobj, NULL);
}
if (player->mo)
{
fixed_t radius = FixedMul(skin->radius, player->mo->scale);
if ((player->powers[pw_carry] == CR_NIGHTSMODE) && (skin->sprites[SPR2_NFLY].numframes == 0)) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin.
{
skin = &skins[DEFAULTNIGHTSSKIN];
player->followitem = skin->followitem;
if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback))
newcolor = skin->prefcolor; // will be updated in thinker to flashing
}
player->mo->skin = skin;
if (newcolor)
player->mo->color = newcolor;
P_SetScale(player->mo, player->mo->scale);
player->mo->radius = radius;
P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames
}
return;
}
if (P_IsLocalPlayer(player))
CONS_Alert(CONS_WARNING, M_GetText("Requested skin %d not found\n"), skinnum);
else if(server || IsPlayerAdmin(consoleplayer))
CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum);
SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin
}
//
// Add skins from a pwad, each skin preceded by 'S_SKIN' marker
//
// Does the same is in w_wad, but check only for
// the first 6 characters (this is so we can have S_SKIN1, S_SKIN2..
// for wad editors that don't like multiple resources of the same name)
//
static UINT16 W_CheckForSkinMarkerInPwad(UINT16 wadid, UINT16 startlump)
{
UINT16 i;
const char *S_SKIN = "S_SKIN";
lumpinfo_t *lump_p;
// scan forward, start at <startlump>
if (startlump < wadfiles[wadid]->numlumps)
{
lump_p = wadfiles[wadid]->lumpinfo + startlump;
for (i = startlump; i < wadfiles[wadid]->numlumps; i++, lump_p++)
if (memcmp(lump_p->name,S_SKIN,6)==0)
return i;
}
return INT16_MAX; // not found
}
#define HUDNAMEWRITE(value) STRBUFCPY(skin->hudname, value)
// turn _ into spaces and . into katana dot
#define SYMBOLCONVERT(name) for (value = name; *value; value++)\
{\
if (*value == '_') *value = ' ';\
else if (*value == '.') *value = '\x1E';\
}
//
// Patch skins from a pwad, each skin preceded by 'P_SKIN' marker
//
// Does the same is in w_wad, but check only for
// the first 6 characters (this is so we can have P_SKIN1, P_SKIN2..
// for wad editors that don't like multiple resources of the same name)
//
static UINT16 W_CheckForPatchSkinMarkerInPwad(UINT16 wadid, UINT16 startlump)
{
UINT16 i;
const char *P_SKIN = "P_SKIN";
lumpinfo_t *lump_p;
// scan forward, start at <startlump>
if (startlump < wadfiles[wadid]->numlumps)
{
lump_p = wadfiles[wadid]->lumpinfo + startlump;
for (i = startlump; i < wadfiles[wadid]->numlumps; i++, lump_p++)
if (memcmp(lump_p->name,P_SKIN,6)==0)
return i;
}
return INT16_MAX; // not found
}
static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, skin_t *skin)
{
UINT16 newlastlump;
UINT8 sprite2;
*lump += 1; // start after S_SKIN
*lastlump = W_CheckNumForNamePwad("S_END",wadnum,*lump); // stop at S_END
// old wadding practices die hard -- stop at S_SKIN (or P_SKIN) or S_START if they come before S_END.
newlastlump = W_CheckForSkinMarkerInPwad(wadnum,*lump);
if (newlastlump < *lastlump) *lastlump = newlastlump;
newlastlump = W_CheckForPatchSkinMarkerInPwad(wadnum,*lump);
if (newlastlump < *lastlump) *lastlump = newlastlump;
newlastlump = W_CheckNumForNamePwad("S_START",wadnum,*lump);
if (newlastlump < *lastlump) *lastlump = newlastlump;
// ...and let's handle super, too
newlastlump = W_CheckNumForNamePwad("S_SUPER",wadnum,*lump);
if (newlastlump < *lastlump)
{
newlastlump++;
// load all sprite sets we are aware of... for super!
for (sprite2 = 0; sprite2 < free_spr2; sprite2++)
R_AddSingleSpriteDef(spr2names[sprite2], &skin->sprites[FF_SPR2SUPER|sprite2], wadnum, newlastlump, *lastlump);
newlastlump--;
*lastlump = newlastlump; // okay, make the normal sprite set loading end there
}
// load all sprite sets we are aware of... for normal stuff.
for (sprite2 = 0; sprite2 < free_spr2; sprite2++)
R_AddSingleSpriteDef(spr2names[sprite2], &skin->sprites[sprite2], wadnum, *lump, *lastlump);
if (skin->sprites[0].numframes == 0)
I_Error("R_LoadSkinSprites: no frames found for sprite SPR2_%s\n", spr2names[0]);
}
// returns whether found appropriate property
static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
{
// custom translation table
if (!stricmp(stoken, "startcolor"))
skin->starttranscolor = atoi(value);
#define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value);
// character type identification
FULLPROCESS(flags)
FULLPROCESS(ability)
FULLPROCESS(ability2)
FULLPROCESS(thokitem)
FULLPROCESS(spinitem)
FULLPROCESS(revitem)
FULLPROCESS(followitem)
#undef FULLPROCESS
#define GETFRACBITS(field) else if (!stricmp(stoken, #field)) skin->field = atoi(value)<<FRACBITS;
GETFRACBITS(normalspeed)
GETFRACBITS(runspeed)
GETFRACBITS(mindash)
GETFRACBITS(maxdash)
GETFRACBITS(actionspd)
GETFRACBITS(radius)
GETFRACBITS(height)
GETFRACBITS(spinheight)
#undef GETFRACBITS
#define GETINT(field) else if (!stricmp(stoken, #field)) skin->field = atoi(value);
GETINT(thrustfactor)
GETINT(accelstart)
GETINT(acceleration)
GETINT(contspeed)
GETINT(contangle)
#undef GETINT
#define GETSKINCOLOR(field) else if (!stricmp(stoken, #field)) skin->field = R_GetColorByName(value);
GETSKINCOLOR(prefcolor)
GETSKINCOLOR(prefoppositecolor)
#undef GETSKINCOLOR
else if (!stricmp(stoken, "supercolor"))
skin->supercolor = R_GetSuperColorByName(value);
#define GETFLOAT(field) else if (!stricmp(stoken, #field)) skin->field = FLOAT_TO_FIXED(atof(value));
GETFLOAT(jumpfactor)
GETFLOAT(highresscale)
GETFLOAT(shieldscale)
GETFLOAT(camerascale)
#undef GETFLOAT
#define GETFLAG(field) else if (!stricmp(stoken, #field)) { \
strupr(value); \
if (atoi(value) || value[0] == 'T' || value[0] == 'Y') \
skin->flags |= (SF_##field); \
else \
skin->flags &= ~(SF_##field); \
}
// parameters for individual character flags
// these are uppercase so they can be concatenated with SF_
// 1, true, yes are all valid values
GETFLAG(SUPER)
GETFLAG(NOSUPERSPIN)
GETFLAG(NOSPINDASHDUST)
GETFLAG(HIRES)
GETFLAG(NOSKID)
GETFLAG(NOSPEEDADJUST)
GETFLAG(RUNONWATER)
GETFLAG(NOJUMPSPIN)
GETFLAG(NOJUMPDAMAGE)
GETFLAG(STOMPDAMAGE)
GETFLAG(MARIODAMAGE)
GETFLAG(MACHINE)
GETFLAG(DASHMODE)
GETFLAG(FASTEDGE)
GETFLAG(MULTIABILITY)
GETFLAG(NONIGHTSROTATION)
#undef GETFLAG
else // let's check if it's a sound, otherwise error out
{
boolean found = false;
sfxenum_t i;
size_t stokenadjust;
// Remove the prefix. (We need to affect an adjusting variable so that we can print error messages if it's not actually a sound.)
if ((stoken[0] == 'D' || stoken[0] == 'd') && (stoken[1] == 'S' || stoken[1] == 's')) // DS*
stokenadjust = 2;
else // sfx_*
stokenadjust = 4;
// Remove the prefix. (We can affect this directly since we're not going to use it again.)
if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS*
value += 2;
else // sfx_*
value += 4;
// copy name of sounds that are remapped
// for this skin
for (i = 0; i < sfx_skinsoundslot0; i++)
{
if (!S_sfx[i].name)
continue;
if (S_sfx[i].skinsound != -1
&& !stricmp(S_sfx[i].name,
stoken + stokenadjust))
{
skin->soundsid[S_sfx[i].skinsound] =
S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].pitch, true);
found = true;
}
}
return found;
}
return true;
}
//
// Find skin sprites, sounds & optional status bar face, & add them
//
void R_AddSkins(UINT16 wadnum)
{
UINT16 lump, lastlump = 0;
char *buf;
char *buf2;
char *stoken;
char *value;
size_t size;
skin_t *skin;
boolean hudname, realname;
//
// search for all skin markers in pwad
//
while ((lump = W_CheckForSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX)
{
// advance by default
lastlump = lump + 1;
if (numskins >= MAXSKINS)
{
CONS_Debug(DBG_RENDER, "ignored skin (%d skins maximum)\n", MAXSKINS);
continue; // so we know how many skins couldn't be added
}
buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE);
size = W_LumpLengthPwad(wadnum, lump);
// for strtok
buf2 = malloc(size+1);
if (!buf2)
I_Error("R_AddSkins: No more free memory\n");
M_Memcpy(buf2,buf,size);
buf2[size] = '\0';
// set defaults
skin = &skins[numskins];
Sk_SetDefaultValue(skin);
skin->wadnum = wadnum;
hudname = realname = false;
// parse
stoken = strtok (buf2, "\r\n= ");
while (stoken)
{
if ((stoken[0] == '/' && stoken[1] == '/')
|| (stoken[0] == '#'))// skip comments
{
stoken = strtok(NULL, "\r\n"); // skip end of line
goto next_token; // find the real next token
}
value = strtok(NULL, "\r\n= ");
if (!value)
I_Error("R_AddSkins: syntax error in S_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename);
// Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines.
// Others can't go in there because we don't want them to be patchable.
if (!stricmp(stoken, "name"))
{
INT32 skinnum = R_SkinAvailable(value);
strlwr(value);
if (skinnum == -1)
STRBUFCPY(skin->name, value);
// the skin name must uniquely identify a single skin
// if the name is already used I make the name 'namex'
// using the default skin name's number set above
else
{
const size_t stringspace =
strlen(value) + sizeof (numskins) + 1;
char *value2 = Z_Malloc(stringspace, PU_STATIC, NULL);
snprintf(value2, stringspace,
"%s%d", value, numskins);
value2[stringspace - 1] = '\0';
if (R_SkinAvailable(value2) == -1)
// I'm lazy so if NEW name is already used I leave the 'skin x'
// default skin name set in Sk_SetDefaultValue
STRBUFCPY(skin->name, value2);
Z_Free(value2);
}
// copy to hudname and fullname as a default.
if (!realname)
{
STRBUFCPY(skin->realname, skin->name);
for (value = skin->realname; *value; value++)
{
if (*value == '_') *value = ' '; // turn _ into spaces.
else if (*value == '.') *value = '\x1E'; // turn . into katana dot.
}
}
if (!hudname)
{
HUDNAMEWRITE(skin->name);
strupr(skin->hudname);
SYMBOLCONVERT(skin->hudname)
}
}
else if (!stricmp(stoken, "realname"))
{ // Display name (eg. "Knuckles")
realname = true;
STRBUFCPY(skin->realname, value);
SYMBOLCONVERT(skin->realname)
if (!hudname)
HUDNAMEWRITE(skin->realname);
}
else if (!stricmp(stoken, "hudname"))
{ // Life icon name (eg. "K.T.E")
hudname = true;
HUDNAMEWRITE(value);
SYMBOLCONVERT(skin->hudname)
if (!realname)
STRBUFCPY(skin->realname, skin->hudname);
}
else if (!stricmp(stoken, "availability"))
{
skin->availability = atoi(value);
if (skin->availability >= MAXUNLOCKABLES)
skin->availability = 0;
}
else if (!R_ProcessPatchableFields(skin, stoken, value))
CONS_Debug(DBG_SETUP, "R_AddSkins: Unknown keyword '%s' in S_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename);
next_token:
stoken = strtok(NULL, "\r\n= ");
}
free(buf2);
// Add sprites
R_LoadSkinSprites(wadnum, &lump, &lastlump, skin);
//ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere
R_FlushTranslationColormapCache();
if (!skin->availability) // Safe to print...
CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name);
#ifdef SKINVALUES
skin_cons_t[numskins].value = numskins;
skin_cons_t[numskins].strvalue = skin->name;
#endif
#ifdef HWRENDER
if (rendermode == render_opengl)
HWR_AddPlayerModel(numskins);
#endif
numskins++;
}
return;
}
//
// Patch skin sprites
//
void R_PatchSkins(UINT16 wadnum)
{
UINT16 lump, lastlump = 0;
char *buf;
char *buf2;
char *stoken;
char *value;
size_t size;
skin_t *skin;
boolean noskincomplain, realname, hudname;
//
// search for all skin patch markers in pwad
//
while ((lump = W_CheckForPatchSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX)
{
INT32 skinnum = 0;
// advance by default
lastlump = lump + 1;
buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE);
size = W_LumpLengthPwad(wadnum, lump);
// for strtok
buf2 = malloc(size+1);
if (!buf2)
I_Error("R_PatchSkins: No more free memory\n");
M_Memcpy(buf2,buf,size);
buf2[size] = '\0';
skin = NULL;
noskincomplain = realname = hudname = false;
/*
Parse. Has more phases than the parser in R_AddSkins because it needs to have the patching name first (no default skin name is acceptible for patching, unlike skin creation)
*/
stoken = strtok(buf2, "\r\n= ");
while (stoken)
{
if ((stoken[0] == '/' && stoken[1] == '/')
|| (stoken[0] == '#'))// skip comments
{
stoken = strtok(NULL, "\r\n"); // skip end of line
goto next_token; // find the real next token
}
value = strtok(NULL, "\r\n= ");
if (!value)
I_Error("R_PatchSkins: syntax error in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename);
if (!skin) // Get the name!
{
if (!stricmp(stoken, "name"))
{
strlwr(value);
skinnum = R_SkinAvailable(value);
if (skinnum != -1)
skin = &skins[skinnum];
else
{
CONS_Debug(DBG_SETUP, "R_PatchSkins: unknown skin name in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename);
noskincomplain = true;
}
}
}
else // Get the properties!
{
// Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines.
if (!stricmp(stoken, "realname"))
{ // Display name (eg. "Knuckles")
realname = true;
STRBUFCPY(skin->realname, value);
SYMBOLCONVERT(skin->realname)
if (!hudname)
HUDNAMEWRITE(skin->realname);
}
else if (!stricmp(stoken, "hudname"))
{ // Life icon name (eg. "K.T.E")
hudname = true;
HUDNAMEWRITE(value);
SYMBOLCONVERT(skin->hudname)
if (!realname)
STRBUFCPY(skin->realname, skin->hudname);
}
else if (!R_ProcessPatchableFields(skin, stoken, value))
CONS_Debug(DBG_SETUP, "R_PatchSkins: Unknown keyword '%s' in P_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename);
}
if (!skin)
break;
next_token:
stoken = strtok(NULL, "\r\n= ");
}
free(buf2);
if (!skin) // Didn't include a name parameter? What a waste.
{
if (!noskincomplain)
CONS_Debug(DBG_SETUP, "R_PatchSkins: no skin name given in P_SKIN lump #%d (WAD %s)\n", lump, wadfiles[wadnum]->filename);
continue;
}
// Patch sprites
R_LoadSkinSprites(wadnum, &lump, &lastlump, skin);
//ST_LoadFaceGraphics(skinnum); -- nah let's do this elsewhere
R_FlushTranslationColormapCache();
if (!skin->availability) // Safe to print...
CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name);
}
return;
}
#undef HUDNAMEWRITE
#undef SYMBOLCONVERT

103
src/r_skins.h Normal file
View File

@ -0,0 +1,103 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// 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_skins.h
/// \brief Skins stuff
#ifndef __R_SKINS__
#define __R_SKINS__
#include "info.h"
#include "sounds.h"
#include "d_player.h" // skinflags
#include "r_patch.h" // spriteinfo_t
#include "r_defs.h" // spritedef_t
/// Defaults
#define SKINNAMESIZE 16
// should be all lowercase!! S_SKIN processing does a strlwr
#define DEFAULTSKIN "sonic"
#define DEFAULTSKIN2 "tails" // secondary player
#define DEFAULTNIGHTSSKIN 0
/// The skin_t struct
typedef struct
{
char name[SKINNAMESIZE+1]; // INT16 descriptive name of the skin
UINT16 wadnum;
skinflags_t flags;
char realname[SKINNAMESIZE+1]; // Display name for level completion.
char hudname[SKINNAMESIZE+1]; // HUD name to display (officially exactly 5 characters long)
UINT8 ability; // ability definition
UINT8 ability2; // secondary ability definition
INT32 thokitem;
INT32 spinitem;
INT32 revitem;
INT32 followitem;
fixed_t actionspd;
fixed_t mindash;
fixed_t maxdash;
fixed_t normalspeed; // Normal ground
fixed_t runspeed; // Speed that you break into your run animation
UINT8 thrustfactor; // Thrust = thrustfactor * acceleration
UINT8 accelstart; // Acceleration if speed = 0
UINT8 acceleration; // Acceleration
fixed_t jumpfactor; // multiple of standard jump height
fixed_t radius; // Bounding box changes.
fixed_t height;
fixed_t spinheight;
fixed_t shieldscale; // no change to bounding box, but helps set the shield's sprite size
fixed_t camerascale;
// Definable color translation table
UINT8 starttranscolor;
UINT8 prefcolor;
UINT8 supercolor;
UINT8 prefoppositecolor; // if 0 use tables instead
fixed_t highresscale; // scale of highres, default is 0.5
UINT8 contspeed; // continue screen animation speed
UINT8 contangle; // initial angle on continue screen
// specific sounds per skin
sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table
// contains super versions too
spritedef_t sprites[NUMPLAYERSPRITES*2];
spriteinfo_t sprinfo[NUMPLAYERSPRITES*2];
UINT8 availability; // lock?
} skin_t;
/// Externs
extern INT32 numskins;
extern skin_t skins[MAXSKINS];
/// Function prototypes
void R_InitSkins(void);
void SetPlayerSkin(INT32 playernum,const char *skinname);
void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002
boolean R_SkinUsable(INT32 playernum, INT32 skinnum);
UINT32 R_GetSkinAvailabilities(void);
INT32 R_SkinAvailable(const char *name);
void R_PatchSkins(UINT16 wadnum);
void R_AddSkins(UINT16 wadnum);
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player);
#endif //__R_SKINS__

View File

@ -30,11 +30,8 @@
#include "p_tick.h"
#include "p_local.h"
#include "p_slopes.h"
#include "dehacked.h" // get_number (for thok)
#include "d_netfil.h" // blargh. for nameonly().
#include "m_cheat.h" // objectplace
#include "m_cond.h"
#include "fastcmp.h"
#ifdef HWRENDER
#include "hardware/hw_md2.h"
#include "hardware/hw_glob.h"
@ -42,14 +39,6 @@
#include "hardware/hw_drv.h"
#endif
#ifdef PC_DOS
#include <stdio.h> // for snprintf
int snprintf(char *str, size_t n, const char *fmt, ...);
//int vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
#endif
static void R_InitSkins(void);
#define MINZ (FRACUNIT*4)
#define BASEYCENTER (BASEVIDHEIGHT/2)
@ -233,7 +222,7 @@ static void R_InstallSpriteLump(UINT16 wad, // graphics patch
//
// Returns true if the sprite was succesfully added
//
static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 wadnum, UINT16 startlump, UINT16 endlump)
boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 wadnum, UINT16 startlump, UINT16 endlump)
{
UINT16 l;
UINT8 frame;
@ -245,6 +234,8 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef,
memset(sprtemp,0xFF, sizeof (sprtemp));
maxframe = (size_t)-1;
spritename = sprname;
// are we 'patching' a sprite already loaded ?
// if so, it might patch only certain frames, not all
if (spritedef->numframes) // (then spriteframes is not null)
@ -476,11 +467,10 @@ void R_AddSpriteDefs(UINT16 wadnum)
//
for (i = 0; i < numsprites; i++)
{
spritename = sprnames[i];
if (spritename[4] && wadnum >= (UINT16)spritename[4])
if (sprnames[i][4] && wadnum >= (UINT16)sprnames[i][4])
continue;
if (R_AddSingleSpriteDef(spritename, &sprites[i], wadnum, start, end))
if (R_AddSingleSpriteDef(sprnames[i], &sprites[i], wadnum, start, end))
{
#ifdef HWRENDER
if (rendermode == render_opengl)
@ -489,7 +479,7 @@ void R_AddSpriteDefs(UINT16 wadnum)
// if a new sprite was added (not just replaced)
addsprites++;
#ifndef ZDEBUG
CONS_Debug(DBG_SETUP, "sprite %s set in pwad %d\n", spritename, wadnum);
CONS_Debug(DBG_SETUP, "sprite %s set in pwad %d\n", sprnames[i], wadnum);
#endif
}
}
@ -2964,795 +2954,3 @@ void R_DrawMasked(maskcount_t* masks, UINT8 nummasks)
free(heads);
}
// ==========================================================================
//
// SKINS CODE
//
// ==========================================================================
INT32 numskins = 0;
skin_t skins[MAXSKINS];
// FIXTHIS: don't work because it must be inistilised before the config load
//#define SKINVALUES
#ifdef SKINVALUES
CV_PossibleValue_t skin_cons_t[MAXSKINS+1];
#endif
//
// P_GetSkinSprite2
// For non-super players, tries each sprite2's immediate predecessor until it finds one with a number of frames or ends up at standing.
// For super players, does the same as above - but tries the super equivalent for each sprite2 before the non-super version.
//
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player)
{
UINT8 super = 0, i = 0;
if (!skin)
return 0;
if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2)
return 0;
while (!skin->sprites[spr2].numframes
&& spr2 != SPR2_STND
&& ++i < 32) // recursion limiter
{
if (spr2 & FF_SPR2SUPER)
{
super = FF_SPR2SUPER;
spr2 &= ~FF_SPR2SUPER;
continue;
}
switch(spr2)
{
// Normal special cases.
case SPR2_JUMP:
spr2 = ((player
? player->charflags
: skin->flags)
& SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL;
break;
case SPR2_TIRE:
spr2 = ((player
? player->charability
: skin->ability)
== CA_SWIM) ? SPR2_SWIM : SPR2_FLY;
break;
// Use the handy list, that's what it's there for!
default:
spr2 = spr2defaults[spr2];
break;
}
spr2 |= super;
}
if (i >= 32) // probably an infinite loop...
return 0;
return spr2;
}
static void Sk_SetDefaultValue(skin_t *skin)
{
INT32 i;
//
// set default skin values
//
memset(skin, 0, sizeof (skin_t));
snprintf(skin->name,
sizeof skin->name, "skin %u", (UINT32)(skin-skins));
skin->name[sizeof skin->name - 1] = '\0';
skin->wadnum = INT16_MAX;
skin->flags = 0;
strcpy(skin->realname, "Someone");
strcpy(skin->hudname, "???");
skin->starttranscolor = 96;
skin->prefcolor = SKINCOLOR_GREEN;
skin->supercolor = SKINCOLOR_SUPERGOLD1;
skin->prefoppositecolor = 0; // use tables
skin->normalspeed = 36<<FRACBITS;
skin->runspeed = 28<<FRACBITS;
skin->thrustfactor = 5;
skin->accelstart = 96;
skin->acceleration = 40;
skin->ability = CA_NONE;
skin->ability2 = CA2_SPINDASH;
skin->jumpfactor = FRACUNIT;
skin->actionspd = 30<<FRACBITS;
skin->mindash = 15<<FRACBITS;
skin->maxdash = 70<<FRACBITS;
skin->radius = mobjinfo[MT_PLAYER].radius;
skin->height = mobjinfo[MT_PLAYER].height;
skin->spinheight = FixedMul(skin->height, 2*FRACUNIT/3);
skin->shieldscale = FRACUNIT;
skin->camerascale = FRACUNIT;
skin->thokitem = -1;
skin->spinitem = -1;
skin->revitem = -1;
skin->followitem = 0;
skin->highresscale = FRACUNIT;
skin->contspeed = 17;
skin->contangle = 0;
skin->availability = 0;
for (i = 0; i < sfx_skinsoundslot0; i++)
if (S_sfx[i].skinsound != -1)
skin->soundsid[S_sfx[i].skinsound] = i;
}
//
// Initialize the basic skins
//
void R_InitSkins(void)
{
#ifdef SKINVALUES
INT32 i;
for (i = 0; i <= MAXSKINS; i++)
{
skin_cons_t[i].value = 0;
skin_cons_t[i].strvalue = NULL;
}
#endif
// no default skin!
numskins = 0;
}
UINT32 R_GetSkinAvailabilities(void)
{
INT32 s;
UINT32 response = 0;
for (s = 0; s < MAXSKINS; s++)
{
if (skins[s].availability && unlockables[skins[s].availability - 1].unlocked)
response |= (1 << s);
}
return response;
}
// returns true if available in circumstances, otherwise nope
// warning don't use with an invalid skinnum other than -1 which always returns true
boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
{
return ((skinnum == -1) // Simplifies things elsewhere, since there's already plenty of checks for less-than-0...
|| (!skins[skinnum].availability)
|| (((netgame || multiplayer) && playernum != -1) ? (players[playernum].availabilities & (1 << skinnum)) : (unlockables[skins[skinnum].availability - 1].unlocked))
|| (modeattacking) // If you have someone else's run you might as well take a look
|| (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1.
|| (netgame && (cv_forceskin.value == skinnum)) // Force 2.
|| (metalrecording && skinnum == 5) // Force 3.
);
}
// returns true if the skin name is found (loaded from pwad)
// warning return -1 if not found
INT32 R_SkinAvailable(const char *name)
{
INT32 i;
for (i = 0; i < numskins; i++)
{
// search in the skin list
if (stricmp(skins[i].name,name)==0)
return i;
}
return -1;
}
// network code calls this when a 'skin change' is received
void SetPlayerSkin(INT32 playernum, const char *skinname)
{
INT32 i = R_SkinAvailable(skinname);
player_t *player = &players[playernum];
if ((i != -1) && R_SkinUsable(playernum, i))
{
SetPlayerSkinByNum(playernum, i);
return;
}
if (P_IsLocalPlayer(player))
CONS_Alert(CONS_WARNING, M_GetText("Skin '%s' not found.\n"), skinname);
else if(server || IsPlayerAdmin(consoleplayer))
CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname);
SetPlayerSkinByNum(playernum, 0);
}
// Same as SetPlayerSkin, but uses the skin #.
// network code calls this when a 'skin change' is received
void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
{
player_t *player = &players[playernum];
skin_t *skin = &skins[skinnum];
UINT8 newcolor = 0;
if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists!
{
player->skin = skinnum;
player->camerascale = skin->camerascale;
player->shieldscale = skin->shieldscale;
player->charability = (UINT8)skin->ability;
player->charability2 = (UINT8)skin->ability2;
player->charflags = (UINT32)skin->flags;
player->thokitem = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem;
player->spinitem = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem;
player->revitem = skin->revitem < 0 ? (mobjtype_t)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem;
player->followitem = skin->followitem;
if (((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK) && (player->revitem == MT_LHRT || player->spinitem == MT_LHRT || player->thokitem == MT_LHRT)) // Healers can't keep their buff.
player->powers[pw_shield] &= SH_STACK;
player->actionspd = skin->actionspd;
player->mindash = skin->mindash;
player->maxdash = skin->maxdash;
player->normalspeed = skin->normalspeed;
player->runspeed = skin->runspeed;
player->thrustfactor = skin->thrustfactor;
player->accelstart = skin->accelstart;
player->acceleration = skin->acceleration;
player->jumpfactor = skin->jumpfactor;
player->height = skin->height;
player->spinheight = skin->spinheight;
if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback))
{
if (playernum == consoleplayer)
CV_StealthSetValue(&cv_playercolor, skin->prefcolor);
else if (playernum == secondarydisplayplayer)
CV_StealthSetValue(&cv_playercolor2, skin->prefcolor);
player->skincolor = newcolor = skin->prefcolor;
}
if (player->followmobj)
{
P_RemoveMobj(player->followmobj);
P_SetTarget(&player->followmobj, NULL);
}
if (player->mo)
{
fixed_t radius = FixedMul(skin->radius, player->mo->scale);
if ((player->powers[pw_carry] == CR_NIGHTSMODE) && (skin->sprites[SPR2_NFLY].numframes == 0)) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin.
{
skin = &skins[DEFAULTNIGHTSSKIN];
player->followitem = skin->followitem;
if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback))
newcolor = skin->prefcolor; // will be updated in thinker to flashing
}
player->mo->skin = skin;
if (newcolor)
player->mo->color = newcolor;
P_SetScale(player->mo, player->mo->scale);
player->mo->radius = radius;
P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames
}
return;
}
if (P_IsLocalPlayer(player))
CONS_Alert(CONS_WARNING, M_GetText("Requested skin %d not found\n"), skinnum);
else if(server || IsPlayerAdmin(consoleplayer))
CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum);
SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin
}
//
// Add skins from a pwad, each skin preceded by 'S_SKIN' marker
//
// Does the same is in w_wad, but check only for
// the first 6 characters (this is so we can have S_SKIN1, S_SKIN2..
// for wad editors that don't like multiple resources of the same name)
//
static UINT16 W_CheckForSkinMarkerInPwad(UINT16 wadid, UINT16 startlump)
{
UINT16 i;
const char *S_SKIN = "S_SKIN";
lumpinfo_t *lump_p;
// scan forward, start at <startlump>
if (startlump < wadfiles[wadid]->numlumps)
{
lump_p = wadfiles[wadid]->lumpinfo + startlump;
for (i = startlump; i < wadfiles[wadid]->numlumps; i++, lump_p++)
if (memcmp(lump_p->name,S_SKIN,6)==0)
return i;
}
return INT16_MAX; // not found
}
#define HUDNAMEWRITE(value) STRBUFCPY(skin->hudname, value)
// turn _ into spaces and . into katana dot
#define SYMBOLCONVERT(name) for (value = name; *value; value++)\
{\
if (*value == '_') *value = ' ';\
else if (*value == '.') *value = '\x1E';\
}
//
// Patch skins from a pwad, each skin preceded by 'P_SKIN' marker
//
// Does the same is in w_wad, but check only for
// the first 6 characters (this is so we can have P_SKIN1, P_SKIN2..
// for wad editors that don't like multiple resources of the same name)
//
static UINT16 W_CheckForPatchSkinMarkerInPwad(UINT16 wadid, UINT16 startlump)
{
UINT16 i;
const char *P_SKIN = "P_SKIN";
lumpinfo_t *lump_p;
// scan forward, start at <startlump>
if (startlump < wadfiles[wadid]->numlumps)
{
lump_p = wadfiles[wadid]->lumpinfo + startlump;
for (i = startlump; i < wadfiles[wadid]->numlumps; i++, lump_p++)
if (memcmp(lump_p->name,P_SKIN,6)==0)
return i;
}
return INT16_MAX; // not found
}
static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, skin_t *skin)
{
UINT16 newlastlump;
UINT8 sprite2;
*lump += 1; // start after S_SKIN
*lastlump = W_CheckNumForNamePwad("S_END",wadnum,*lump); // stop at S_END
// old wadding practices die hard -- stop at S_SKIN (or P_SKIN) or S_START if they come before S_END.
newlastlump = W_CheckForSkinMarkerInPwad(wadnum,*lump);
if (newlastlump < *lastlump) *lastlump = newlastlump;
newlastlump = W_CheckForPatchSkinMarkerInPwad(wadnum,*lump);
if (newlastlump < *lastlump) *lastlump = newlastlump;
newlastlump = W_CheckNumForNamePwad("S_START",wadnum,*lump);
if (newlastlump < *lastlump) *lastlump = newlastlump;
// ...and let's handle super, too
newlastlump = W_CheckNumForNamePwad("S_SUPER",wadnum,*lump);
if (newlastlump < *lastlump)
{
newlastlump++;
// load all sprite sets we are aware of... for super!
for (sprite2 = 0; sprite2 < free_spr2; sprite2++)
R_AddSingleSpriteDef((spritename = spr2names[sprite2]), &skin->sprites[FF_SPR2SUPER|sprite2], wadnum, newlastlump, *lastlump);
newlastlump--;
*lastlump = newlastlump; // okay, make the normal sprite set loading end there
}
// load all sprite sets we are aware of... for normal stuff.
for (sprite2 = 0; sprite2 < free_spr2; sprite2++)
R_AddSingleSpriteDef((spritename = spr2names[sprite2]), &skin->sprites[sprite2], wadnum, *lump, *lastlump);
if (skin->sprites[0].numframes == 0)
I_Error("R_LoadSkinSprites: no frames found for sprite SPR2_%s\n", spr2names[0]);
}
// returns whether found appropriate property
static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
{
// custom translation table
if (!stricmp(stoken, "startcolor"))
skin->starttranscolor = atoi(value);
#define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value);
// character type identification
FULLPROCESS(flags)
FULLPROCESS(ability)
FULLPROCESS(ability2)
FULLPROCESS(thokitem)
FULLPROCESS(spinitem)
FULLPROCESS(revitem)
FULLPROCESS(followitem)
#undef FULLPROCESS
#define GETFRACBITS(field) else if (!stricmp(stoken, #field)) skin->field = atoi(value)<<FRACBITS;
GETFRACBITS(normalspeed)
GETFRACBITS(runspeed)
GETFRACBITS(mindash)
GETFRACBITS(maxdash)
GETFRACBITS(actionspd)
GETFRACBITS(radius)
GETFRACBITS(height)
GETFRACBITS(spinheight)
#undef GETFRACBITS
#define GETINT(field) else if (!stricmp(stoken, #field)) skin->field = atoi(value);
GETINT(thrustfactor)
GETINT(accelstart)
GETINT(acceleration)
GETINT(contspeed)
GETINT(contangle)
#undef GETINT
#define GETSKINCOLOR(field) else if (!stricmp(stoken, #field)) skin->field = R_GetColorByName(value);
GETSKINCOLOR(prefcolor)
GETSKINCOLOR(prefoppositecolor)
#undef GETSKINCOLOR
else if (!stricmp(stoken, "supercolor"))
skin->supercolor = R_GetSuperColorByName(value);
#define GETFLOAT(field) else if (!stricmp(stoken, #field)) skin->field = FLOAT_TO_FIXED(atof(value));
GETFLOAT(jumpfactor)
GETFLOAT(highresscale)
GETFLOAT(shieldscale)
GETFLOAT(camerascale)
#undef GETFLOAT
#define GETFLAG(field) else if (!stricmp(stoken, #field)) { \
strupr(value); \
if (atoi(value) || value[0] == 'T' || value[0] == 'Y') \
skin->flags |= (SF_##field); \
else \
skin->flags &= ~(SF_##field); \
}
// parameters for individual character flags
// these are uppercase so they can be concatenated with SF_
// 1, true, yes are all valid values
GETFLAG(SUPER)
GETFLAG(NOSUPERSPIN)
GETFLAG(NOSPINDASHDUST)
GETFLAG(HIRES)
GETFLAG(NOSKID)
GETFLAG(NOSPEEDADJUST)
GETFLAG(RUNONWATER)
GETFLAG(NOJUMPSPIN)
GETFLAG(NOJUMPDAMAGE)
GETFLAG(STOMPDAMAGE)
GETFLAG(MARIODAMAGE)
GETFLAG(MACHINE)
GETFLAG(DASHMODE)
GETFLAG(FASTEDGE)
GETFLAG(MULTIABILITY)
GETFLAG(NONIGHTSROTATION)
#undef GETFLAG
else // let's check if it's a sound, otherwise error out
{
boolean found = false;
sfxenum_t i;
size_t stokenadjust;
// Remove the prefix. (We need to affect an adjusting variable so that we can print error messages if it's not actually a sound.)
if ((stoken[0] == 'D' || stoken[0] == 'd') && (stoken[1] == 'S' || stoken[1] == 's')) // DS*
stokenadjust = 2;
else // sfx_*
stokenadjust = 4;
// Remove the prefix. (We can affect this directly since we're not going to use it again.)
if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS*
value += 2;
else // sfx_*
value += 4;
// copy name of sounds that are remapped
// for this skin
for (i = 0; i < sfx_skinsoundslot0; i++)
{
if (!S_sfx[i].name)
continue;
if (S_sfx[i].skinsound != -1
&& !stricmp(S_sfx[i].name,
stoken + stokenadjust))
{
skin->soundsid[S_sfx[i].skinsound] =
S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].pitch, true);
found = true;
}
}
return found;
}
return true;
}
//
// Find skin sprites, sounds & optional status bar face, & add them
//
void R_AddSkins(UINT16 wadnum)
{
UINT16 lump, lastlump = 0;
char *buf;
char *buf2;
char *stoken;
char *value;
size_t size;
skin_t *skin;
boolean hudname, realname;
//
// search for all skin markers in pwad
//
while ((lump = W_CheckForSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX)
{
// advance by default
lastlump = lump + 1;
if (numskins >= MAXSKINS)
{
CONS_Debug(DBG_RENDER, "ignored skin (%d skins maximum)\n", MAXSKINS);
continue; // so we know how many skins couldn't be added
}
buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE);
size = W_LumpLengthPwad(wadnum, lump);
// for strtok
buf2 = malloc(size+1);
if (!buf2)
I_Error("R_AddSkins: No more free memory\n");
M_Memcpy(buf2,buf,size);
buf2[size] = '\0';
// set defaults
skin = &skins[numskins];
Sk_SetDefaultValue(skin);
skin->wadnum = wadnum;
hudname = realname = false;
// parse
stoken = strtok (buf2, "\r\n= ");
while (stoken)
{
if ((stoken[0] == '/' && stoken[1] == '/')
|| (stoken[0] == '#'))// skip comments
{
stoken = strtok(NULL, "\r\n"); // skip end of line
goto next_token; // find the real next token
}
value = strtok(NULL, "\r\n= ");
if (!value)
I_Error("R_AddSkins: syntax error in S_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename);
// Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines.
// Others can't go in there because we don't want them to be patchable.
if (!stricmp(stoken, "name"))
{
INT32 skinnum = R_SkinAvailable(value);
strlwr(value);
if (skinnum == -1)
STRBUFCPY(skin->name, value);
// the skin name must uniquely identify a single skin
// if the name is already used I make the name 'namex'
// using the default skin name's number set above
else
{
const size_t stringspace =
strlen(value) + sizeof (numskins) + 1;
char *value2 = Z_Malloc(stringspace, PU_STATIC, NULL);
snprintf(value2, stringspace,
"%s%d", value, numskins);
value2[stringspace - 1] = '\0';
if (R_SkinAvailable(value2) == -1)
// I'm lazy so if NEW name is already used I leave the 'skin x'
// default skin name set in Sk_SetDefaultValue
STRBUFCPY(skin->name, value2);
Z_Free(value2);
}
// copy to hudname and fullname as a default.
if (!realname)
{
STRBUFCPY(skin->realname, skin->name);
for (value = skin->realname; *value; value++)
{
if (*value == '_') *value = ' '; // turn _ into spaces.
else if (*value == '.') *value = '\x1E'; // turn . into katana dot.
}
}
if (!hudname)
{
HUDNAMEWRITE(skin->name);
strupr(skin->hudname);
SYMBOLCONVERT(skin->hudname)
}
}
else if (!stricmp(stoken, "realname"))
{ // Display name (eg. "Knuckles")
realname = true;
STRBUFCPY(skin->realname, value);
SYMBOLCONVERT(skin->realname)
if (!hudname)
HUDNAMEWRITE(skin->realname);
}
else if (!stricmp(stoken, "hudname"))
{ // Life icon name (eg. "K.T.E")
hudname = true;
HUDNAMEWRITE(value);
SYMBOLCONVERT(skin->hudname)
if (!realname)
STRBUFCPY(skin->realname, skin->hudname);
}
else if (!stricmp(stoken, "availability"))
{
skin->availability = atoi(value);
if (skin->availability >= MAXUNLOCKABLES)
skin->availability = 0;
}
else if (!R_ProcessPatchableFields(skin, stoken, value))
CONS_Debug(DBG_SETUP, "R_AddSkins: Unknown keyword '%s' in S_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename);
next_token:
stoken = strtok(NULL, "\r\n= ");
}
free(buf2);
// Add sprites
R_LoadSkinSprites(wadnum, &lump, &lastlump, skin);
//ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere
R_FlushTranslationColormapCache();
if (!skin->availability) // Safe to print...
CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name);
#ifdef SKINVALUES
skin_cons_t[numskins].value = numskins;
skin_cons_t[numskins].strvalue = skin->name;
#endif
#ifdef HWRENDER
if (rendermode == render_opengl)
HWR_AddPlayerModel(numskins);
#endif
numskins++;
}
return;
}
//
// Patch skin sprites
//
void R_PatchSkins(UINT16 wadnum)
{
UINT16 lump, lastlump = 0;
char *buf;
char *buf2;
char *stoken;
char *value;
size_t size;
skin_t *skin;
boolean noskincomplain, realname, hudname;
//
// search for all skin patch markers in pwad
//
while ((lump = W_CheckForPatchSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX)
{
INT32 skinnum = 0;
// advance by default
lastlump = lump + 1;
buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE);
size = W_LumpLengthPwad(wadnum, lump);
// for strtok
buf2 = malloc(size+1);
if (!buf2)
I_Error("R_PatchSkins: No more free memory\n");
M_Memcpy(buf2,buf,size);
buf2[size] = '\0';
skin = NULL;
noskincomplain = realname = hudname = false;
/*
Parse. Has more phases than the parser in R_AddSkins because it needs to have the patching name first (no default skin name is acceptible for patching, unlike skin creation)
*/
stoken = strtok(buf2, "\r\n= ");
while (stoken)
{
if ((stoken[0] == '/' && stoken[1] == '/')
|| (stoken[0] == '#'))// skip comments
{
stoken = strtok(NULL, "\r\n"); // skip end of line
goto next_token; // find the real next token
}
value = strtok(NULL, "\r\n= ");
if (!value)
I_Error("R_PatchSkins: syntax error in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename);
if (!skin) // Get the name!
{
if (!stricmp(stoken, "name"))
{
strlwr(value);
skinnum = R_SkinAvailable(value);
if (skinnum != -1)
skin = &skins[skinnum];
else
{
CONS_Debug(DBG_SETUP, "R_PatchSkins: unknown skin name in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename);
noskincomplain = true;
}
}
}
else // Get the properties!
{
// Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines.
if (!stricmp(stoken, "realname"))
{ // Display name (eg. "Knuckles")
realname = true;
STRBUFCPY(skin->realname, value);
SYMBOLCONVERT(skin->realname)
if (!hudname)
HUDNAMEWRITE(skin->realname);
}
else if (!stricmp(stoken, "hudname"))
{ // Life icon name (eg. "K.T.E")
hudname = true;
HUDNAMEWRITE(value);
SYMBOLCONVERT(skin->hudname)
if (!realname)
STRBUFCPY(skin->realname, skin->hudname);
}
else if (!R_ProcessPatchableFields(skin, stoken, value))
CONS_Debug(DBG_SETUP, "R_PatchSkins: Unknown keyword '%s' in P_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename);
}
if (!skin)
break;
next_token:
stoken = strtok(NULL, "\r\n= ");
}
free(buf2);
if (!skin) // Didn't include a name parameter? What a waste.
{
if (!noskincomplain)
CONS_Debug(DBG_SETUP, "R_PatchSkins: no skin name given in P_SKIN lump #%d (WAD %s)\n", lump, wadfiles[wadnum]->filename);
continue;
}
// Patch sprites
R_LoadSkinSprites(wadnum, &lump, &lastlump, skin);
//ST_LoadFaceGraphics(skinnum); -- nah let's do this elsewhere
R_FlushTranslationColormapCache();
if (!skin->availability) // Safe to print...
CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name);
}
return;
}
#undef HUDNAMEWRITE
#undef SYMBOLCONVERT

View File

@ -14,26 +14,27 @@
#ifndef __R_THINGS__
#define __R_THINGS__
#include "sounds.h"
#include "r_plane.h"
#include "r_patch.h"
#include "r_portal.h"
#include "r_defs.h"
#include "r_skins.h"
// number of sprite lumps for spritewidth,offset,topoffset lookup tables
// Fab: this is a hack : should allocate the lookup tables per sprite
#define MAXVISSPRITES 2048 // added 2-2-98 was 128
#define VISSPRITECHUNKBITS 6 // 2^6 = 64 sprites per chunk
#define VISSPRITESPERCHUNK (1 << VISSPRITECHUNKBITS)
#define VISSPRITEINDEXMASK (VISSPRITESPERCHUNK - 1)
// --------------
// SPRITE LOADING
// --------------
#define FEETADJUST (4<<FRACBITS) // R_AddSingleSpriteDef
// Constant arrays used for psprite clipping
// and initializing clipping.
extern INT16 negonearray[MAXVIDWIDTH];
extern INT16 screenheightarray[MAXVIDWIDTH];
boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 wadnum, UINT16 startlump, UINT16 endlump);
//faB: find sprites in wadfile, replace existing, add new ones
// (only sprites from namelist are added or replaced)
void R_AddSpriteDefs(UINT16 wadnum);
// ---------------------
// MASKED COLUMN DRAWING
// ---------------------
// vars for R_DrawMaskedColumn
extern INT16 *mfloorclip;
@ -47,9 +48,14 @@ extern fixed_t windowbottom;
void R_DrawMaskedColumn(column_t *column);
void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight);
//faB: find sprites in wadfile, replace existing, add new ones
// (only sprites from namelist are added or replaced)
void R_AddSpriteDefs(UINT16 wadnum);
// ----------------
// SPRITE RENDERING
// ----------------
// Constant arrays used for psprite clipping
// and initializing clipping.
extern INT16 negonearray[MAXVIDWIDTH];
extern INT16 screenheightarray[MAXVIDWIDTH];
fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope);
@ -68,6 +74,9 @@ boolean R_ThingVisibleWithinDist (mobj_t *thing,
boolean R_PrecipThingVisible (precipmobj_t *precipthing,
fixed_t precip_draw_dist);
// --------------
// MASKED DRAWING
// --------------
/** Used to count the amount of masked elements
* per portal to later group them in separate
* drawnode lists.
@ -82,73 +91,18 @@ typedef struct
void R_DrawMasked(maskcount_t* masks, UINT8 nummasks);
// -----------
// SKINS STUFF
// -----------
#define SKINNAMESIZE 16
// should be all lowercase!! S_SKIN processing does a strlwr
#define DEFAULTSKIN "sonic"
#define DEFAULTSKIN2 "tails" // secondary player
#define DEFAULTNIGHTSSKIN 0
// ----------
// VISSPRITES
// ----------
typedef struct
{
char name[SKINNAMESIZE+1]; // INT16 descriptive name of the skin
UINT16 wadnum;
skinflags_t flags;
// number of sprite lumps for spritewidth,offset,topoffset lookup tables
// Fab: this is a hack : should allocate the lookup tables per sprite
#define MAXVISSPRITES 2048 // added 2-2-98 was 128
char realname[SKINNAMESIZE+1]; // Display name for level completion.
char hudname[SKINNAMESIZE+1]; // HUD name to display (officially exactly 5 characters long)
#define VISSPRITECHUNKBITS 6 // 2^6 = 64 sprites per chunk
#define VISSPRITESPERCHUNK (1 << VISSPRITECHUNKBITS)
#define VISSPRITEINDEXMASK (VISSPRITESPERCHUNK - 1)
UINT8 ability; // ability definition
UINT8 ability2; // secondary ability definition
INT32 thokitem;
INT32 spinitem;
INT32 revitem;
INT32 followitem;
fixed_t actionspd;
fixed_t mindash;
fixed_t maxdash;
fixed_t normalspeed; // Normal ground
fixed_t runspeed; // Speed that you break into your run animation
UINT8 thrustfactor; // Thrust = thrustfactor * acceleration
UINT8 accelstart; // Acceleration if speed = 0
UINT8 acceleration; // Acceleration
fixed_t jumpfactor; // multiple of standard jump height
fixed_t radius; // Bounding box changes.
fixed_t height;
fixed_t spinheight;
fixed_t shieldscale; // no change to bounding box, but helps set the shield's sprite size
fixed_t camerascale;
// Definable color translation table
UINT8 starttranscolor;
UINT8 prefcolor;
UINT8 supercolor;
UINT8 prefoppositecolor; // if 0 use tables instead
fixed_t highresscale; // scale of highres, default is 0.5
UINT8 contspeed; // continue screen animation speed
UINT8 contangle; // initial angle on continue screen
// specific sounds per skin
sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table
// contains super versions too
spritedef_t sprites[NUMPLAYERSPRITES*2];
spriteinfo_t sprinfo[NUMPLAYERSPRITES*2];
UINT8 availability; // lock?
} skin_t;
// -----------
// NOT SKINS STUFF !
// -----------
typedef enum
{
// actual cuts
@ -227,6 +181,12 @@ typedef struct vissprite_s
INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing
} vissprite_t;
extern UINT32 visspritecount;
// ----------
// DRAW NODES
// ----------
// A drawnode is something that points to a 3D floor, 3D side, or masked
// middle texture. This is used for sorting with sprites.
typedef struct drawnode_s
@ -241,23 +201,11 @@ typedef struct drawnode_s
struct drawnode_s *prev;
} drawnode_t;
extern INT32 numskins;
extern skin_t skins[MAXSKINS];
extern UINT32 visspritecount;
void SetPlayerSkin(INT32 playernum,const char *skinname);
void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002
boolean R_SkinUsable(INT32 playernum, INT32 skinnum);
UINT32 R_GetSkinAvailabilities(void);
INT32 R_SkinAvailable(const char *name);
void R_PatchSkins(UINT16 wadnum);
void R_AddSkins(UINT16 wadnum);
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player);
void R_InitDrawNodes(void);
char *GetPlayerFacePic(INT32 skinnum);
// -----------------------
// SPRITE FRAME CHARACTERS
// -----------------------
// Functions to go from sprite character ID to frame number
// for 2.1 compatibility this still uses the old 'A' + frame code

View File

@ -27,7 +27,7 @@ extern INT32 msg_id;
#include "g_game.h"
#include "m_argv.h"
#include "r_main.h" // R_PointToAngle2() used to calc stereo sep.
#include "r_things.h" // for skins
#include "r_skins.h" // for skins
#include "i_system.h"
#include "i_sound.h"
#include "s_sound.h"

View File

@ -450,6 +450,20 @@ static int target_renderer = 0;
void SCR_ActuallyChangeRenderer(void)
{
setrenderneeded = target_renderer;
#ifdef HWRENDER
// Well, it didn't even load anyway.
if ((hwrenderloaded == -1) && (setrenderneeded == render_opengl))
{
if (M_CheckParm("-nogl"))
CONS_Alert(CONS_ERROR, "OpenGL rendering was disabled!\n");
else
CONS_Alert(CONS_ERROR, "OpenGL never loaded\n");
setrenderneeded = 0;
return;
}
#endif
// setting the same renderer twice WILL crash your game, so let's not, please
if (rendermode == setrenderneeded)
setrenderneeded = 0;
@ -464,7 +478,7 @@ void SCR_ChangeRenderer(void)
{
target_renderer = cv_renderer.value;
#ifdef HWRENDER
if (M_CheckParm("-opengl"))
if (M_CheckParm("-opengl") && (hwrenderloaded == 1))
target_renderer = rendermode = render_opengl;
else
#endif

View File

@ -284,6 +284,7 @@
<ClInclude Include="..\r_patch.h" />
<ClInclude Include="..\r_portal.h" />
<ClInclude Include="..\r_segs.h" />
<ClInclude Include="..\r_skins.h" />
<ClInclude Include="..\r_sky.h" />
<ClInclude Include="..\r_splats.h" />
<ClInclude Include="..\r_state.h" />
@ -446,6 +447,7 @@
<ClCompile Include="..\r_patch.c" />
<ClCompile Include="..\r_portal.c" />
<ClCompile Include="..\r_segs.c" />
<ClCompile Include="..\r_skins.c" />
<ClCompile Include="..\r_sky.c" />
<ClCompile Include="..\r_splats.c" />
<ClCompile Include="..\r_things.c" />

View File

@ -417,6 +417,9 @@
<ClInclude Include="..\r_segs.h">
<Filter>R_Rend</Filter>
</ClInclude>
<ClInclude Include="..\r_skins.h">
<Filter>R_Rend</Filter>
</ClInclude>
<ClInclude Include="..\r_sky.h">
<Filter>R_Rend</Filter>
</ClInclude>
@ -849,6 +852,9 @@
<ClCompile Include="..\r_segs.c">
<Filter>R_Rend</Filter>
</ClCompile>
<ClCompile Include="..\r_skins.c">
<Filter>R_Rend</Filter>
</ClCompile>
<ClCompile Include="..\r_sky.c">
<Filter>R_Rend</Filter>
</ClCompile>

View File

@ -93,7 +93,8 @@ static INT32 numVidModes = -1;
*/
static char vidModeName[33][32]; // allow 33 different modes
rendermode_t rendermode=render_soft;
rendermode_t rendermode = render_soft;
static rendermode_t chosenrendermode = render_soft; // set by command line arguments
boolean highcolor = false;
@ -103,6 +104,7 @@ static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff
static consvar_t cv_alwaysgrabmouse = {"alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
UINT8 graphics_started = 0; // Is used in console.c and screen.c
INT32 hwrenderloaded = 0;
// To disable fullscreen at startup; is set in VID_PrepareModeList
boolean allow_fullscreen = false;
@ -174,7 +176,7 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen);
//static void Impl_SetWindowName(const char *title);
static void Impl_SetWindowIcon(void);
static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_bool reposition)
{
static SDL_bool wasfullscreen = SDL_FALSE;
Uint32 rmask;
@ -203,10 +205,13 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
}
// Reposition window only in windowed mode
SDL_SetWindowSize(window, width, height);
SDL_SetWindowPosition(window,
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)),
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window))
);
if (reposition)
{
SDL_SetWindowPosition(window,
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)),
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window))
);
}
}
}
else
@ -1435,7 +1440,7 @@ static SDL_bool Impl_CreateContext(void)
{
// Renderer-specific stuff
#ifdef HWRENDER
if (rendermode == render_opengl)
if ((rendermode == render_opengl) && (hwrenderloaded != -1))
{
if (!sdlglcontext)
sdlglcontext = SDL_GL_CreateContext(window);
@ -1468,18 +1473,58 @@ static SDL_bool Impl_CreateContext(void)
return SDL_TRUE;
}
#ifdef HWRENDER
static void VID_CheckGLLoaded(rendermode_t oldrender)
{
if (hwrenderloaded == -1) // Well, it didn't work the first time anyway.
{
rendermode = oldrender;
if (chosenrendermode == render_opengl) // fallback to software
rendermode = render_soft;
if (setrenderneeded)
{
CV_StealthSetValue(&cv_renderer, oldrender);
CV_StealthSetValue(&cv_newrenderer, oldrender);
setrenderneeded = 0;
}
}
}
#endif
void VID_CheckRenderer(void)
{
SDL_bool rendererchanged = SDL_FALSE;
rendermode_t oldrenderer = rendermode;
if (dedicated)
return;
#ifdef HWRENDER
if (!graphics_started)
VID_CheckGLLoaded(oldrenderer);
#endif
if (setrenderneeded)
{
rendermode = setrenderneeded;
rendererchanged = SDL_TRUE;
#ifdef HWRENDER
if (rendermode == render_opengl)
{
VID_CheckGLLoaded(oldrenderer);
// Initialise OpenGL before calling SDLSetMode!!!
if (hwrenderloaded != 1)
I_StartupHardwareGraphics();
else if (hwrenderloaded == -1)
rendererchanged = SDL_FALSE;
}
#endif
Impl_CreateContext();
}
SDLSetMode(vid.width, vid.height, USE_FULLSCREEN);
SDLSetMode(vid.width, vid.height, USE_FULLSCREEN, (rendererchanged ? SDL_FALSE : SDL_TRUE));
Impl_VideoSetupBuffer();
if (rendermode == render_soft)
@ -1490,16 +1535,16 @@ void VID_CheckRenderer(void)
bufSurface = NULL;
}
#ifdef HWRENDER
HWR_FreeTextureCache();
if (hwrenderloaded == 1) // Only if OpenGL ever loaded!
HWR_FreeTextureCache();
#endif
SCR_SetDrawFuncs();
}
#ifdef HWRENDER
else if (rendermode == render_opengl)
{
I_StartupHardwareGraphics();
R_InitHardwareMode();
}
#else
(void)oldrenderer;
#endif
}
@ -1541,7 +1586,8 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
flags |= SDL_WINDOW_BORDERLESS;
#ifdef HWRENDER
flags |= SDL_WINDOW_OPENGL;
if (hwrenderloaded != -1)
flags |= SDL_WINDOW_OPENGL;
#endif
// Create a window
@ -1664,10 +1710,10 @@ void I_StartupGraphics(void)
#ifdef HWRENDER
if (M_CheckParm("-opengl"))
rendermode = render_opengl;
chosenrendermode = rendermode = render_opengl;
else if (M_CheckParm("-software"))
#endif
rendermode = render_soft;
chosenrendermode = rendermode = render_soft;
usesdl2soft = M_CheckParm("-softblit");
borderlesswindow = M_CheckParm("-borderless");
@ -1675,7 +1721,10 @@ void I_StartupGraphics(void)
//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
VID_Command_ModeList_f();
#ifdef HWRENDER
I_StartupHardwareGraphics();
if (M_CheckParm("-nogl"))
hwrenderloaded = -1; // Don't call SDL_GL_LoadLibrary
else
I_StartupHardwareGraphics();
#endif
// Fury: we do window initialization after GL setup to allow
@ -1763,13 +1812,22 @@ void I_StartupHardwareGraphics(void)
HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL);
HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL);
HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL);
// check gl renderer lib
if (HWD.pfnGetRenderVersion() != VERSION)
I_Error("%s", M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n"));
if (!HWD.pfnInit(I_Error)) // let load the OpenGL library
rendermode = render_soft;
{
CONS_Alert(CONS_ERROR, M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n"));
hwrenderloaded = -1;
}
else
glstartup = true;
hwrenderloaded = HWD.pfnInit(I_Error) ? 1 : -1; // let load the OpenGL library
if (hwrenderloaded == -1)
{
rendermode = render_soft;
setrenderneeded = 0;
}
glstartup = true;
}
#endif
}

View File

@ -95,10 +95,10 @@ boolean LoadGL(void)
if (SDL_GL_LoadLibrary(OGLLibname) != 0)
{
I_OutputMsg("Could not load OpenGL Library: %s\n"
CONS_Alert(CONS_ERROR, "Could not load OpenGL Library: %s\n"
"Falling back to Software mode.\n", SDL_GetError());
if (!M_CheckParm ("-OGLlib"))
I_OutputMsg("If you know what is the OpenGL library's name, use -OGLlib\n");
CONS_Alert(CONS_ERROR, "If you know what is the OpenGL library's name, use -OGLlib\n");
return 0;
}
@ -128,15 +128,15 @@ boolean LoadGL(void)
return SetupGLfunc();
else
{
I_OutputMsg("Could not load GLU Library: %s\n", GLULibname);
CONS_Alert(CONS_ERROR, "Could not load GLU Library: %s\n", GLULibname);
if (!M_CheckParm ("-GLUlib"))
I_OutputMsg("If you know what is the GLU library's name, use -GLUlib\n");
CONS_Alert(CONS_ERROR, "If you know what is the GLU library's name, use -GLUlib\n");
}
}
else
{
I_OutputMsg("Could not load GLU Library\n");
I_OutputMsg("If you know what is the GLU library's name, use -GLUlib\n");
CONS_Alert(CONS_ERROR, "Could not load GLU Library\n");
CONS_Alert(CONS_ERROR, "If you know what is the GLU library's name, use -GLUlib\n");
}
#endif
return SetupGLfunc();

View File

@ -15,7 +15,7 @@
#include "i_sound.h"
#include "sounds.h"
#include "r_defs.h"
#include "r_things.h"
#include "r_skins.h"
#include "z_zone.h"
#include "w_wad.h"
#include "lua_script.h"

View File

@ -19,7 +19,7 @@
#include "i_video.h"
#include "p_tick.h"
#include "r_defs.h"
#include "r_things.h"
#include "r_skins.h"
#include "s_sound.h"
#include "st_stuff.h"
#include "v_video.h"