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

View file

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

View file

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

View file

@ -17,6 +17,7 @@
static const luaL_Reg lualibs[] = { static const luaL_Reg lualibs[] = {
{"", luaopen_base}, {"", luaopen_base},
{LUA_TABLIBNAME, luaopen_table}, {LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_STRLIBNAME, luaopen_string}, {LUA_STRLIBNAME, luaopen_string},
{NULL, NULL} {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" #define LUA_TABLIBNAME "table"
LUALIB_API int (luaopen_table) (lua_State *L); LUALIB_API int (luaopen_table) (lua_State *L);
#define LUA_IOLIBNAME "io"
LUALIB_API int (luaopen_io) (lua_State *L);
#define LUA_STRLIBNAME "string" #define LUA_STRLIBNAME "string"
LUALIB_API int (luaopen_string) (lua_State *L); LUALIB_API int (luaopen_string) (lua_State *L);

View file

@ -613,15 +613,6 @@ void CON_Ticker(void)
con_tick++; con_tick++;
con_tick &= 7; 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 // console key was pushed
if (consoletoggle) if (consoletoggle)
{ {
@ -793,7 +784,7 @@ boolean CON_Responder(event_t *ev)
// check other keys only if console prompt is active // check other keys only if console prompt is active
if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!! if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!!
{ {
if (bindtable[key]) if (! menuactive && bindtable[key])
{ {
COM_BufAddText(bindtable[key]); COM_BufAddText(bindtable[key]);
COM_BufAddText("\n"); COM_BufAddText("\n");

View file

@ -1289,6 +1289,37 @@ static boolean CL_SendJoin(void)
return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak)); 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) static void SV_SendServerInfo(INT32 node, tic_t servertime)
{ {
UINT8 *p; 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.numberofplayer = (UINT8)D_NumPlayers();
netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; 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], strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
sizeof netbuffer->u.serverinfo.gametypename); sizeof netbuffer->u.serverinfo.gametypename);
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; 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. // 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(); D_QuitNetGame();
CL_Reset(); CL_Reset();
D_StartTitle(); 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; return false;
} }
@ -2439,14 +2485,14 @@ static void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
if (!playeringame[playernum]) if (!playeringame[playernum])
return; return;
if (server && !demoplayback) if (server && !demoplayback && playernode[playernum] != UINT8_MAX)
{ {
INT32 node = playernode[playernum]; INT32 node = playernode[playernum];
playerpernode[node]--; playerpernode[node]--;
if (playerpernode[node] <= 0) if (playerpernode[node] <= 0)
{ {
nodeingame[playernode[playernum]] = false; nodeingame[node] = false;
Net_CloseConnection(playernode[playernum]); Net_CloseConnection(node);
ResetNode(node); ResetNode(node);
} }
} }
@ -2513,7 +2559,7 @@ static void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
// Call ViewpointSwitch hooks here. // Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed. // The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], true); LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true);
#endif #endif
displayplayer = consoleplayer; displayplayer = consoleplayer;
} }
@ -2792,16 +2838,13 @@ static void Command_Kick(void)
if (pn == -1 || pn == 0) if (pn == -1 || pn == 0)
return; 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: Net_ConnectionTimeout(playernode[pn]);
// trigger a timeout instead of kicking them, because a kick would only return;
// take effect after they have finished downloading
if (sendingsavegame[playernode[pn]])
{
Net_ConnectionTimeout(playernode[pn]);
return;
}
} }
WRITESINT8(p, pn); WRITESINT8(p, pn);
@ -2859,7 +2902,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
// Is playernum authorized to make this kick? // Is playernum authorized to make this kick?
if (playernum != serverplayer && !IsPlayerAdmin(playernum) if (playernum != serverplayer && !IsPlayerAdmin(playernum)
&& !(playerpernode[playernode[playernum]] == 2 && !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2
&& nodetoplayer2[playernode[playernum]] == pnum)) && nodetoplayer2[playernode[playernum]] == pnum))
{ {
// We received a kick command from someone who isn't the // 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) else if (keepbody)
{ {
if (server && !demoplayback) if (server && !demoplayback && playernode[pnum] != UINT8_MAX)
{ {
INT32 node = playernode[pnum]; INT32 node = playernode[pnum];
playerpernode[node]--; playerpernode[node]--;
@ -3197,6 +3240,10 @@ void D_QuitNetGame(void)
// abort send/receive of files // abort send/receive of files
CloseNetFile(); CloseNetFile();
#ifdef HAVE_BLUA
RemoveAllLuaFileTransfers();
waitingforluafiletransfer = false;
#endif
if (server) if (server)
{ {
@ -3230,37 +3277,6 @@ void D_QuitNetGame(void)
#endif #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....) // Adds a node to the game (player will follow at map change or at savegame....)
static inline void SV_AddNode(INT32 node) static inline void SV_AddNode(INT32 node)
{ {
@ -3591,7 +3607,7 @@ static void HandleConnect(SINT8 node)
rejoinernum = FindRejoinerNum(node); rejoinernum = FindRejoinerNum(node);
if (bannednode && bannednode[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 || else if (netbuffer->u.clientcfg._255 != 255 ||
netbuffer->u.clientcfg.packetversion != PACKETVERSION) netbuffer->u.clientcfg.packetversion != PACKETVERSION)
SV_SendRefuse(node, "Incompatible packet formats."); SV_SendRefuse(node, "Incompatible packet formats.");
@ -3602,13 +3618,17 @@ static void HandleConnect(SINT8 node)
|| netbuffer->u.clientcfg.subversion != SUBVERSION) || 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)); 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) 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) else if (D_NumPlayers() >= cv_maxplayers.value && rejoinernum == -1)
SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value)); SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value));
else if (netgame && netbuffer->u.clientcfg.localplayers > 1) // Hacked client? else if (netgame && netbuffer->u.clientcfg.localplayers > 1) // Hacked client?
SV_SendRefuse(node, M_GetText("Too many players from\nthis node.")); SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join? else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join?
SV_SendRefuse(node, M_GetText("No players from\nthis node.")); 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 else
{ {
#ifndef NONET #ifndef NONET
@ -3966,7 +3986,7 @@ static void HandlePacketFromPlayer(SINT8 node)
case PT_RESYNCHGET: case PT_RESYNCHGET:
if (client) if (client)
break; break;
SV_AcknowledgeResynchAck(netconsole, netbuffer->u.resynchgot); SV_AcknowledgeResynchAck(node, netbuffer->u.resynchgot);
break; break;
case PT_CLIENTCMD: case PT_CLIENTCMD:
case PT_CLIENT2CMD: case PT_CLIENT2CMD:
@ -4195,6 +4215,20 @@ static void HandlePacketFromPlayer(SINT8 node)
Net_CloseConnection(node); Net_CloseConnection(node);
nodeingame[node] = false; nodeingame[node] = false;
break; 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 ---------- // -------------------------------------------- CLIENT RECEIVE ----------
case PT_RESYNCHEND: case PT_RESYNCHEND:
// Only accept PT_RESYNCHEND from the server. // Only accept PT_RESYNCHEND from the server.
@ -4322,6 +4356,12 @@ static void HandlePacketFromPlayer(SINT8 node)
if (client) if (client)
Got_Filetxpak(); Got_Filetxpak();
break; break;
#ifdef HAVE_BLUA
case PT_SENDINGLUAFILE:
if (client)
CL_PrepareDownloadLuaFile();
break;
#endif
default: default:
DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
netbuffer->packettype, node)); netbuffer->packettype, node));
@ -4943,7 +4983,7 @@ void NetUpdate(void)
PingUpdate(); PingUpdate();
// update node latency values so we can take an average later. // update node latency values so we can take an average later.
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i]) if (playeringame[i] && playernode[i] != UINT8_MAX)
realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
pingmeasurecount++; 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 version and subversion. It should only account for the
basic fields of the packet, and change infrequently. basic fields of the packet, and change infrequently.
*/ */
#define PACKETVERSION 2 #define PACKETVERSION 3
// Network play related stuff. // Network play related stuff.
// There is a data struct that stores network // There is a data struct that stores network
@ -36,7 +36,7 @@ basic fields of the packet, and change infrequently.
// be transmitted. // be transmitted.
// Networking and tick handling related. // Networking and tick handling related.
#define BACKUPTICS 32 #define BACKUPTICS 96
#define MAXTEXTCMD 256 #define MAXTEXTCMD 256
// //
// Packet structure // Packet structure
@ -67,6 +67,12 @@ typedef enum
PT_RESYNCHEND, // Player is now resynched and is being requested to remake the gametic PT_RESYNCHEND, // Player is now resynched and is being requested to remake the gametic
PT_RESYNCHGET, // Player got resynch packet 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. // Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
@ -361,6 +367,7 @@ typedef struct
UINT8 subversion; UINT8 subversion;
UINT8 numberofplayer; UINT8 numberofplayer;
UINT8 maxplayer; UINT8 maxplayer;
UINT8 refusereason; // 0: joinable, 1: joins disabled, 2: full
char gametypename[24]; char gametypename[24];
UINT8 modifiedgame; UINT8 modifiedgame;
UINT8 cheatsenabled; UINT8 cheatsenabled;

View file

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

View file

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

View file

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

View file

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

View file

@ -69,6 +69,7 @@ typedef struct filetx_s
UINT32 size; // Size of the file UINT32 size; // Size of the file
UINT8 fileid; UINT8 fileid;
INT32 node; // Destination INT32 node; // Destination
boolean textmode; // For files requested by Lua without the "b" option
struct filetx_s *next; // Next file in the list struct filetx_s *next; // Next file in the list
} filetx_t; } filetx_t;
@ -94,6 +95,13 @@ char downloaddir[512] = "DOWNLOAD";
INT32 lastfilenum = -1; INT32 lastfilenum = -1;
#endif #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. /** Fills a serverinfo packet with information about wad files loaded.
* *
* \todo Give this function a better name since it is in global scope. * \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 fileneeded[i].file = NULL; // The file isn't open yet
READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name 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 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; fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16); memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, tmpsave); strcpy(fileneeded[0].filename, tmpsave);
fileneeded[0].textmode = false;
} }
/** Checks the server to see if we CAN download all the files, /** 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 // Number of files to send
// Little optimization to quickly test if there is a file in the queue // Little optimization to quickly test if there is a file in the queue
static INT32 filestosend = 0; static INT32 filestosend = 0;
@ -458,6 +626,7 @@ static INT32 filestosend = 0;
* \param filename The file to send * \param filename The file to send
* \param fileid ??? * \param fileid ???
* \sa SV_SendRam * \sa SV_SendRam
* \sa SV_SendLuaFile
* *
*/ */
static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid) 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 freemethod How to free the block after it has been sent
* \param fileid ??? * \param fileid ???
* \sa SV_SendFile * \sa SV_SendFile
* \sa SV_SendLuaFile
* *
*/ */
void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid) 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++; 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, /** 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 * either because the file has been fully sent or because the node was disconnected
* *
@ -684,7 +905,7 @@ void SV_FileSendTicker(void)
long filesize; long filesize;
transfer[i].currentfile = transfer[i].currentfile =
fopen(f->id.filename, "rb"); fopen(f->id.filename, f->textmode ? "r" : "rb");
if (!transfer[i].currentfile) if (!transfer[i].currentfile)
I_Error("File %s does not exist", I_Error("File %s does not exist",
@ -715,11 +936,20 @@ void SV_FileSendTicker(void)
size = f->size-transfer[i].position; size = f->size-transfer[i].position;
if (ram) if (ram)
M_Memcpy(p->data, &f->id.ram[transfer[i].position], size); M_Memcpy(p->data, &f->id.ram[transfer[i].position], size);
else if (fread(p->data, 1, size, transfer[i].currentfile) != size) else
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)); {
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); p->position = LONG(transfer[i].position);
// Put flag so receiver knows the total size // 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->position |= LONG(0x80000000);
p->fileid = f->fileid; p->fileid = f->fileid;
p->size = SHORT((UINT16)size); p->size = SHORT((UINT16)size);
@ -728,7 +958,7 @@ void SV_FileSendTicker(void)
if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND
{ // Success { // Success
transfer[i].position = (UINT32)(transfer[i].position + size); 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); SV_EndFileSend(i);
} }
else else
@ -772,7 +1002,7 @@ void Got_Filetxpak(void)
{ {
if (file->file) if (file->file)
I_Error("Got_Filetxpak: already open file\n"); I_Error("Got_Filetxpak: already open file\n");
file->file = fopen(filename, "wb"); file->file = fopen(filename, file->textmode ? "w" : "wb");
if (!file->file) if (!file->file)
I_Error("Can't create file %s: %s", filename, strerror(errno)); I_Error("Can't create file %s: %s", filename, strerror(errno));
CONS_Printf("\r%s...\n",filename); 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 // We can receive packet in the wrong order, anyway all os support gaped file
fseek(file->file, pos, SEEK_SET); 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)); I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file));
file->currentsize += size; file->currentsize += size;
@ -805,6 +1035,14 @@ void Got_Filetxpak(void)
file->status = FS_FOUND; file->status = FS_FOUND;
CONS_Printf(M_GetText("Downloading %s...(done)\n"), CONS_Printf(M_GetText("Downloading %s...(done)\n"),
filename); 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 else

View file

@ -13,6 +13,7 @@
#ifndef __D_NETFIL__ #ifndef __D_NETFIL__
#define __D_NETFIL__ #define __D_NETFIL__
#include "d_net.h"
#include "w_wad.h" #include "w_wad.h"
typedef enum typedef enum
@ -43,6 +44,7 @@ typedef struct
UINT32 currentsize; UINT32 currentsize;
UINT32 totalsize; UINT32 totalsize;
filestatus_t status; // The value returned by recsearch filestatus_t status; // The value returned by recsearch
boolean textmode; // For files requested by Lua without the "b" option
} fileneeded_t; } fileneeded_t;
extern INT32 fileneedednum; extern INT32 fileneedednum;
@ -70,6 +72,44 @@ boolean CL_CheckDownloadable(void);
boolean CL_SendRequestFile(void); boolean CL_SendRequestFile(void);
boolean Got_RequestFilePak(INT32 node); 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 SV_AbortSendFiles(INT32 node);
void CloseNetFile(void); void CloseNetFile(void);

View file

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

View file

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

View file

@ -38,7 +38,7 @@
#include "byteptr.h" #include "byteptr.h"
#include "i_joy.h" #include "i_joy.h"
#include "r_local.h" #include "r_local.h"
#include "r_things.h" #include "r_skins.h"
#include "y_inter.h" #include "y_inter.h"
#include "v_video.h" #include "v_video.h"
#include "dehacked.h" // get_number (for ghost thok) #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; const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT;
INT32 deadzoneAppliedValue = 0; 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) adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE);
{
INT32 adjustedMagnitude = abs(magnitude);
adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE);
adjustedMagnitude -= jdeadzone; adjustedMagnitude -= jdeadzone;
deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone);
}
} }
return deadzoneAppliedValue; return deadzoneAppliedValue;
@ -1718,7 +1717,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
// Call ViewpointSwitch hooks here. // Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed. // The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(player, &players[displayplayer], true); LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
#endif #endif
displayplayer = consoleplayer; displayplayer = consoleplayer;
} }
@ -3592,7 +3591,7 @@ boolean G_CompetitionGametype(void)
* \return The typeoflevel flag to check for that gametype. * \return The typeoflevel flag to check for that gametype.
* \author Graue <graue@oceanbase.org> * \author Graue <graue@oceanbase.org>
*/ */
INT16 G_TOLFlag(INT32 pgametype) UINT32 G_TOLFlag(INT32 pgametype)
{ {
if (!multiplayer) if (!multiplayer)
return TOL_SP; return TOL_SP;
@ -3723,7 +3722,7 @@ static void G_DoCompleted(void)
&& (nextmap >= 0 && nextmap < NUMMAPS)) && (nextmap >= 0 && nextmap < NUMMAPS))
{ {
register INT16 cm = nextmap; register INT16 cm = nextmap;
INT16 tolflag = G_TOLFlag(gametype); UINT32 tolflag = G_TOLFlag(gametype);
UINT8 visitedmap[(NUMMAPS+7)/8]; UINT8 visitedmap[(NUMMAPS+7)/8];
memset(visitedmap, 0, sizeof (visitedmap)); 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); FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics);
// Don't split up TOL handling // Don't split up TOL handling
INT16 G_TOLFlag(INT32 pgametype); UINT32 G_TOLFlag(INT32 pgametype);
#endif #endif

View file

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

View file

@ -234,11 +234,11 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block,
if (mipmap->colormap) if (mipmap->colormap)
texel = mipmap->colormap[texel]; texel = mipmap->colormap[texel];
// transparent pixel // If the mipmap is chromakeyed, check if the texel's color
if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX) // is equivalent to the chroma key's color index.
alpha = 0xff;
if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX))
alpha = 0x00; alpha = 0x00;
else
alpha = 0xff;
// hope compiler will get this switch out of the loops (dreams...) // 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 ?) // 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 // With OpenGL 1.1+, the first texture should be 1
#define NOTEXTURE_NUM 1 // small white texture static GLuint NOTEXTURE_NUM = 0;
#define FIRST_TEX_AVAIL (NOTEXTURE_NUM + 1)
#define N_PI_DEMI (M_PIl/2.0f) //(1.5707963268f) #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 GLuint tex_downloaded = 0;
static GLfloat fov = 90.0f; static GLfloat fov = 90.0f;
#if 0 #if 0
@ -72,8 +70,8 @@ static FRGBAFloat const_pal_col;
#endif #endif
static FBITFIELD CurrentPolyFlags; static FBITFIELD CurrentPolyFlags;
static FTextureInfo* gr_cachetail = NULL; static FTextureInfo *gr_cachetail = NULL;
static FTextureInfo* gr_cachehead = NULL; static FTextureInfo *gr_cachehead = NULL;
RGBA_t myPaletteData[256]; RGBA_t myPaletteData[256];
GLint screen_width = 0; // used by Draw2DLine() GLint screen_width = 0; // used by Draw2DLine()
@ -94,16 +92,10 @@ static GLfloat modelMatrix[16];
static GLfloat projMatrix[16]; static GLfloat projMatrix[16];
static GLint viewport[4]; 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 // 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 // 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 // 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 // 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 screentexture = 0;
static GLuint startScreenWipe = 0; static GLuint startScreenWipe = 0;
static GLuint endScreenWipe = 0; static GLuint endScreenWipe = 0;
@ -243,6 +235,7 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...)
/* 1.1 functions */ /* 1.1 functions */
/* texture objects */ //GL_EXT_texture_object /* texture objects */ //GL_EXT_texture_object
#define pglGenTextures glGenTextures
#define pglDeleteTextures glDeleteTextures #define pglDeleteTextures glDeleteTextures
#define pglBindTexture glBindTexture #define pglBindTexture glBindTexture
/* texture mapping */ //GL_EXT_copy_texture /* texture mapping */ //GL_EXT_copy_texture
@ -359,6 +352,8 @@ static PFNglFogfv pglFogfv;
/* 1.1 functions */ /* 1.1 functions */
/* texture objects */ //GL_EXT_texture_object /* 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); typedef void (APIENTRY * PFNglDeleteTextures) (GLsizei n, const GLuint *textures);
static PFNglDeleteTextures pglDeleteTextures; static PFNglDeleteTextures pglDeleteTextures;
typedef void (APIENTRY * PFNglBindTexture) (GLenum target, GLuint texture); typedef void (APIENTRY * PFNglBindTexture) (GLenum target, GLuint texture);
@ -487,6 +482,7 @@ boolean SetupGLfunc(void)
GETOPENGLFUNC(pglFogf , glFogf) GETOPENGLFUNC(pglFogf , glFogf)
GETOPENGLFUNC(pglFogfv , glFogfv) GETOPENGLFUNC(pglFogfv , glFogfv)
GETOPENGLFUNC(pglGenTextures , glGenTextures)
GETOPENGLFUNC(pglDeleteTextures , glDeleteTextures) GETOPENGLFUNC(pglDeleteTextures , glDeleteTextures)
GETOPENGLFUNC(pglBindTexture , glBindTexture) GETOPENGLFUNC(pglBindTexture , glBindTexture)
@ -527,6 +523,8 @@ static void SetNoTexture(void)
// Set small white texture. // Set small white texture.
if (tex_downloaded != NOTEXTURE_NUM) if (tex_downloaded != NOTEXTURE_NUM)
{ {
if (NOTEXTURE_NUM == 0)
pglGenTextures(1, &NOTEXTURE_NUM);
pglBindTexture(GL_TEXTURE_2D, NOTEXTURE_NUM); pglBindTexture(GL_TEXTURE_2D, NOTEXTURE_NUM);
tex_downloaded = NOTEXTURE_NUM; tex_downloaded = NOTEXTURE_NUM;
} }
@ -641,11 +639,6 @@ void SetModelView(GLint w, GLint h)
// -----------------+ // -----------------+
void SetStates(void) void SetStates(void)
{ {
// Bind little white RGBA texture to ID NOTEXTURE_NUM.
/*
FUINT Data[8*8];
INT32 i;
*/
#ifdef GL_LIGHT_MODEL_AMBIENT #ifdef GL_LIGHT_MODEL_AMBIENT
GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
#endif #endif
@ -679,16 +672,8 @@ void SetStates(void)
CurrentPolyFlags = 0xffffffff; CurrentPolyFlags = 0xffffffff;
SetBlend(0); SetBlend(0);
/* tex_downloaded = 0;
for (i = 0; i < 64; i++)
Data[i] = 0xffFFffFF; // white pixel
*/
tex_downloaded = (GLuint)-1;
SetNoTexture(); 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); pglPolygonOffset(-1.0f, -1.0f);
@ -723,33 +708,12 @@ void Flush(void)
while (gr_cachehead) while (gr_cachehead)
{ {
// this is not necessary at all, because you have loaded them normally, if (gr_cachehead->downloaded)
// and so they already are in your list! pglDeleteTextures(1, (GLuint *)&gr_cachehead->downloaded);
#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);
gr_cachehead->downloaded = 0; gr_cachehead->downloaded = 0;
gr_cachehead = gr_cachehead->nextmipmap; gr_cachehead = gr_cachehead->nextmipmap;
} }
gr_cachetail = gr_cachehead = NULL; //Hurdler: well, gr_cachehead is already NULL 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; tex_downloaded = 0;
} }
@ -1128,8 +1092,10 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo)
static RGBA_t tex[2048*2048]; static RGBA_t tex[2048*2048];
const GLvoid *ptex = tex; const GLvoid *ptex = tex;
INT32 w, h; 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; w = pTexInfo->width;
h = pTexInfo->height; h = pTexInfo->height;
@ -1217,9 +1183,10 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo)
else else
DBG_Printf ("SetTexture(bad format) %ld\n", pTexInfo->grInfo.format); DBG_Printf ("SetTexture(bad format) %ld\n", pTexInfo->grInfo.format);
pTexInfo->downloaded = NextTexAvail++; // the texture number was already generated by pglGenTextures
tex_downloaded = pTexInfo->downloaded; pglBindTexture(GL_TEXTURE_2D, texnum);
pglBindTexture(GL_TEXTURE_2D, pTexInfo->downloaded); pTexInfo->downloaded = texnum;
tex_downloaded = texnum;
// disable texture filtering on any texture that has holes so there's no dumb borders or blending issues // disable texture filtering on any texture that has holes so there's no dumb borders or blending issues
if (pTexInfo->flags & TF_TRANSPARENT) if (pTexInfo->flags & TF_TRANSPARENT)
@ -2419,7 +2386,7 @@ EXPORT void HWRAPI(StartScreenWipe) (void)
// Create screen texture // Create screen texture
if (firstTime) if (firstTime)
startScreenWipe = SCRTEX_STARTSCREENWIPE; pglGenTextures(1, &startScreenWipe);
pglBindTexture(GL_TEXTURE_2D, startScreenWipe); pglBindTexture(GL_TEXTURE_2D, startScreenWipe);
if (firstTime) if (firstTime)
@ -2450,7 +2417,7 @@ EXPORT void HWRAPI(EndScreenWipe)(void)
// Create screen texture // Create screen texture
if (firstTime) if (firstTime)
endScreenWipe = SCRTEX_ENDSCREENWIPE; pglGenTextures(1, &endScreenWipe);
pglBindTexture(GL_TEXTURE_2D, endScreenWipe); pglBindTexture(GL_TEXTURE_2D, endScreenWipe);
if (firstTime) if (firstTime)
@ -2621,7 +2588,7 @@ EXPORT void HWRAPI(MakeScreenTexture) (void)
// Create screen texture // Create screen texture
if (firstTime) if (firstTime)
screentexture = SCRTEX_SCREENTEXTURE; pglGenTextures(1, &screentexture);
pglBindTexture(GL_TEXTURE_2D, screentexture); pglBindTexture(GL_TEXTURE_2D, screentexture);
if (firstTime) if (firstTime)
@ -2651,7 +2618,7 @@ EXPORT void HWRAPI(MakeScreenFinalTexture) (void)
// Create screen texture // Create screen texture
if (firstTime) if (firstTime)
finalScreenTexture = SCRTEX_FINALSCREENTEXTURE; pglGenTextures(1, &finalScreenTexture);
pglBindTexture(GL_TEXTURE_2D, finalScreenTexture); pglBindTexture(GL_TEXTURE_2D, finalScreenTexture);
if (firstTime) if (firstTime)

View file

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

View file

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

View file

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

View file

@ -15,7 +15,7 @@
#include "doomstat.h" #include "doomstat.h"
#include "p_mobj.h" #include "p_mobj.h"
#include "g_game.h" #include "g_game.h"
#include "r_things.h" #include "r_skins.h"
#include "b_bot.h" #include "b_bot.h"
#include "z_zone.h" #include "z_zone.h"
@ -144,6 +144,7 @@ static int lib_addHook(lua_State *L)
case hook_HurtMsg: case hook_HurtMsg:
case hook_MobjMoveBlocked: case hook_MobjMoveBlocked:
case hook_MapThingSpawn: case hook_MapThingSpawn:
case hook_FollowMobj:
hook.s.mt = MT_NULL; hook.s.mt = MT_NULL;
if (lua_isnumber(L, 2)) if (lua_isnumber(L, 2))
hook.s.mt = lua_tonumber(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_MobjRemoved:
case hook_MobjMoveBlocked: case hook_MobjMoveBlocked:
case hook_MapThingSpawn: case hook_MapThingSpawn:
case hook_FollowMobj:
lastp = &mobjhooks[hook.s.mt]; lastp = &mobjhooks[hook.s.mt];
break; break;
case hook_JumpSpecial: case hook_JumpSpecial:
@ -213,7 +215,6 @@ static int lib_addHook(lua_State *L)
case hook_SpinSpecial: case hook_SpinSpecial:
case hook_JumpSpinSpecial: case hook_JumpSpinSpecial:
case hook_PlayerSpawn: case hook_PlayerSpawn:
case hook_FollowMobj:
case hook_PlayerCanDamage: case hook_PlayerCanDamage:
case hook_TeamSwitch: case hook_TeamSwitch:
case hook_ViewpointSwitch: case hook_ViewpointSwitch:
@ -1367,7 +1368,34 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj)
lua_settop(gL, 0); 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) if (hookp->type != hook_FollowMobj)
continue; continue;

View file

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

View file

@ -97,6 +97,10 @@ static int player_get(lua_State *L)
lua_pushboolean(L, true); lua_pushboolean(L, true);
else if (fastcmp(field,"name")) else if (fastcmp(field,"name"))
lua_pushstring(L, player_names[plr-players]); 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")) else if (fastcmp(field,"mo"))
{ {
if (plr->spectator) if (plr->spectator)
@ -398,7 +402,7 @@ static int player_set(lua_State *L)
if (hud_running) if (hud_running)
return luaL_error(L, "Do not alter player_t in HUD rendering code!"); 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)); mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
plr->mo->player = NULL; // remove player pointer from old 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 (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" #include "doomdef.h"
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
#include "fastcmp.h" #include "fastcmp.h"
#include "r_things.h" #include "r_skins.h"
#include "sounds.h" #include "sounds.h"
#include "lua_script.h" #include "lua_script.h"

View file

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

View file

@ -312,6 +312,7 @@ static void M_AssignJoystick(INT32 choice);
static void M_ChangeControl(INT32 choice); static void M_ChangeControl(INT32 choice);
// Video & Sound // Video & Sound
static void M_VideoOptions(INT32 choice);
menu_t OP_VideoOptionsDef, OP_VideoModeDef, OP_ColorOptionsDef; menu_t OP_VideoOptionsDef, OP_VideoModeDef, OP_ColorOptionsDef;
#ifdef HWRENDER #ifdef HWRENDER
static void M_OpenGLOptionsMenu(void); 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_SUBMENU | IT_STRING, NULL, "Player 2 Controls...", &OP_P2ControlsDef, 20},
{IT_CVAR | IT_STRING, NULL, "Controls per key", &cv_controlperkey, 30}, {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_SUBMENU | IT_STRING, NULL, "Sound Options...", &OP_SoundOptionsDef, 60},
{IT_CALL | IT_STRING, NULL, "Server Options...", M_ServerOptions, 80}, {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}, {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[] = static menuitem_t OP_VideoOptionsMenu[] =
{ {
{IT_HEADER, NULL, "Screen", NULL, 0}, {IT_HEADER, NULL, "Screen", NULL, 0},
@ -2083,6 +2094,20 @@ menu_t OP_PlaystyleDef = {
0, 0, 0, NULL 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 = menu_t OP_VideoOptionsDef =
{ {
@ -2179,7 +2204,7 @@ menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE(
menu_t OP_ScreenshotOptionsDef = menu_t OP_ScreenshotOptionsDef =
{ {
MN_OP_MAIN + (MN_OP_DATA << 6) + (MN_OP_SCREENSHOTS << 12), MN_OP_MAIN + (MN_OP_DATA << 6) + (MN_OP_SCREENSHOTS << 12),
"M_DATA", "M_SCREEN",
sizeof (OP_ScreenshotOptionsMenu)/sizeof (menuitem_t), sizeof (OP_ScreenshotOptionsMenu)/sizeof (menuitem_t),
&OP_DataOptionsDef, &OP_DataOptionsDef,
OP_ScreenshotOptionsMenu, OP_ScreenshotOptionsMenu,
@ -3153,6 +3178,9 @@ boolean M_Responder(event_t *ev)
if (gamestate == GS_TITLESCREEN && finalecount < TICRATE) if (gamestate == GS_TITLESCREEN && finalecount < TICRATE)
return false; return false;
if (CON_Ready())
return false;
if (noFurtherInput) if (noFurtherInput)
{ {
// Ignore input after enter/escape/other buttons // Ignore input after enter/escape/other buttons
@ -3512,6 +3540,7 @@ boolean M_Responder(event_t *ev)
return false; return false;
default: default:
CON_Responder(ev);
break; break;
} }
@ -5711,6 +5740,8 @@ static void M_DrawNightsAttackSuperSonic(void)
const UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_YELLOW, GTC_CACHE); const UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_YELLOW, GTC_CACHE);
INT32 timer = (ntsatkdrawtimer/4) % 2; INT32 timer = (ntsatkdrawtimer/4) % 2;
angle_t fa = (FixedAngle(((ntsatkdrawtimer * 4) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK; 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); 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 // This is really just to make sure Sonic is the played character, just in case
M_PatchSkinNameTable(); 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 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 titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please
M_SetupNextMenu(&SP_NightsAttackDef); M_SetupNextMenu(&SP_NightsAttackDef);
@ -10071,13 +10099,13 @@ static void M_ReplayTimeAttack(INT32 choice)
static void M_EraseGuest(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)); 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)) if (choice == 'y' || choice == KEY_ENTER)
remove(rguest); {
if (currentMenu == &SP_NightsGuestReplayDef) if (FIL_FileExists(rguest))
M_SetupNextMenu(&SP_NightsAttackDef); remove(rguest);
else }
M_SetupNextMenu(&SP_TimeAttackDef); M_SetupNextMenu(currentMenu->prevMenu->prevMenu);
Nextmap_OnChange(); Nextmap_OnChange();
M_StartMessage(M_GetText("Guest replay data erased.\n"),NULL,MM_NOTHING); 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++) for (i = 0; i < min(serverlistcount - serverlistpage * SERVERS_PER_PAGE, SERVERS_PER_PAGE); i++)
{ {
INT32 slindex = i + serverlistpage * SERVERS_PER_PAGE; 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; |((itemOn == FIRSTSERVERLINE+i) ? V_YELLOWMAP : 0)|V_ALLOWLOWERCASE;
V_DrawString(currentMenu->x, S_LINEY(i), globalflags, serverlist[slindex].info.servername); 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) static void M_DrawMainVideoMenu(void)
{ {
M_DrawGenericScrollMenu(); M_DrawGenericScrollMenu();
if (itemOn < 8) // where it starts to go offscreen; change this number if you change the layout of the video menu 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__ #ifndef __X_MENU__
#define __X_MENU__ #define __X_MENU__
#include "doomstat.h" // for NUMGAMETYPES
#include "d_event.h" #include "d_event.h"
#include "command.h" #include "command.h"
#include "r_things.h" // for SKINNAMESIZE #include "r_skins.h" // for SKINNAMESIZE
#include "f_finale.h" // for ttmode_enum #include "f_finale.h" // for ttmode_enum
// //

View file

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

View file

@ -21,7 +21,7 @@
#include "s_sound.h" #include "s_sound.h"
#include "m_random.h" #include "m_random.h"
#include "m_misc.h" #include "m_misc.h"
#include "r_things.h" #include "r_skins.h"
#include "i_video.h" #include "i_video.h"
#include "z_zone.h" #include "z_zone.h"
#include "lua_hook.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]) if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super])
return; return;
if (!cv_friendlyfire.value) if (!cv_friendlyfire.value && source && source->player)
{ {
if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) 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; return;
} }
if (inflictor->type == MT_LHRT) if (inflictor && inflictor->type == MT_LHRT)
return; return;
if (player->powers[pw_shield] || player->bot) //If One-Hit Shield if (player->powers[pw_shield] || player->bot) //If One-Hit Shield

View file

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

View file

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

View file

@ -27,7 +27,7 @@
#include "i_system.h" #include "i_system.h"
#include "r_data.h" #include "r_data.h"
#include "r_things.h" #include "r_things.h" // for R_AddSpriteDefs
#include "r_patch.h" #include "r_patch.h"
#include "r_sky.h" #include "r_sky.h"
#include "r_draw.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. //Also, you'd never have to loop through all 32 players slots to find anything ever again.
for (i = 0; i < MAXPLAYERS; i++) 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. playersactive[realnumplayers] = i; //stores the player's node in the array.
realnumplayers++; realnumplayers++;

View file

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

View file

@ -22,7 +22,7 @@
#include "p_local.h" #include "p_local.h"
#include "r_main.h" #include "r_main.h"
#include "s_sound.h" #include "s_sound.h"
#include "r_things.h" #include "r_skins.h"
#include "d_think.h" #include "d_think.h"
#include "r_sky.h" #include "r_sky.h"
#include "p_setup.h" #include "p_setup.h"
@ -384,6 +384,9 @@ void P_GiveFinishFlags(player_t *player)
if (!player->mo) if (!player->mo)
return; return;
if (!(netgame||multiplayer))
return;
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
{ {
angle_t fa = (angle >> ANGLETOFINESHIFT) & FINEMASK; angle_t fa = (angle >> ANGLETOFINESHIFT) & FINEMASK;
@ -10655,7 +10658,7 @@ boolean P_SpectatorJoinGame(player_t *player)
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
// Call ViewpointSwitch hooks here. // Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed. // The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(player, &players[displayplayer], true); LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
#endif #endif
displayplayer = consoleplayer; displayplayer = consoleplayer;
} }
@ -10704,7 +10707,7 @@ boolean P_SpectatorJoinGame(player_t *player)
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
// Call ViewpointSwitch hooks here. // Call ViewpointSwitch hooks here.
// The viewpoint was forcibly changed. // The viewpoint was forcibly changed.
LUAh_ViewpointSwitch(player, &players[displayplayer], true); LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
#endif #endif
displayplayer = consoleplayer; 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) UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha)
{ {
RGBA_t output; RGBA_t output;
INT16 fullalpha = (alpha - (0xFF - foreground.s.alpha));
if (style == AST_TRANSLUCENT) if (style == AST_TRANSLUCENT)
{ {
if (alpha == 0) if (fullalpha <= 0)
output.rgba = background.rgba; 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 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; return output.rgba;
} }
#define clamp(c) max(min(c, 0xFF), 0x00); #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_tick.h"
#include "p_local.h" #include "p_local.h"
#include "p_slopes.h" #include "p_slopes.h"
#include "dehacked.h" // get_number (for thok)
#include "d_netfil.h" // blargh. for nameonly(). #include "d_netfil.h" // blargh. for nameonly().
#include "m_cheat.h" // objectplace #include "m_cheat.h" // objectplace
#include "m_cond.h"
#include "fastcmp.h"
#ifdef HWRENDER #ifdef HWRENDER
#include "hardware/hw_md2.h" #include "hardware/hw_md2.h"
#include "hardware/hw_glob.h" #include "hardware/hw_glob.h"
@ -42,14 +39,6 @@
#include "hardware/hw_drv.h" #include "hardware/hw_drv.h"
#endif #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 MINZ (FRACUNIT*4)
#define BASEYCENTER (BASEVIDHEIGHT/2) #define BASEYCENTER (BASEVIDHEIGHT/2)
@ -233,7 +222,7 @@ static void R_InstallSpriteLump(UINT16 wad, // graphics patch
// //
// Returns true if the sprite was succesfully added // 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; UINT16 l;
UINT8 frame; UINT8 frame;
@ -245,6 +234,8 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef,
memset(sprtemp,0xFF, sizeof (sprtemp)); memset(sprtemp,0xFF, sizeof (sprtemp));
maxframe = (size_t)-1; maxframe = (size_t)-1;
spritename = sprname;
// are we 'patching' a sprite already loaded ? // are we 'patching' a sprite already loaded ?
// if so, it might patch only certain frames, not all // if so, it might patch only certain frames, not all
if (spritedef->numframes) // (then spriteframes is not null) if (spritedef->numframes) // (then spriteframes is not null)
@ -476,11 +467,10 @@ void R_AddSpriteDefs(UINT16 wadnum)
// //
for (i = 0; i < numsprites; i++) for (i = 0; i < numsprites; i++)
{ {
spritename = sprnames[i]; if (sprnames[i][4] && wadnum >= (UINT16)sprnames[i][4])
if (spritename[4] && wadnum >= (UINT16)spritename[4])
continue; continue;
if (R_AddSingleSpriteDef(spritename, &sprites[i], wadnum, start, end)) if (R_AddSingleSpriteDef(sprnames[i], &sprites[i], wadnum, start, end))
{ {
#ifdef HWRENDER #ifdef HWRENDER
if (rendermode == render_opengl) if (rendermode == render_opengl)
@ -489,7 +479,7 @@ void R_AddSpriteDefs(UINT16 wadnum)
// if a new sprite was added (not just replaced) // if a new sprite was added (not just replaced)
addsprites++; addsprites++;
#ifndef ZDEBUG #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 #endif
} }
} }
@ -2964,795 +2954,3 @@ void R_DrawMasked(maskcount_t* masks, UINT8 nummasks)
free(heads); 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__ #ifndef __R_THINGS__
#define __R_THINGS__ #define __R_THINGS__
#include "sounds.h"
#include "r_plane.h" #include "r_plane.h"
#include "r_patch.h" #include "r_patch.h"
#include "r_portal.h" #include "r_portal.h"
#include "r_defs.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 // SPRITE LOADING
#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)
#define FEETADJUST (4<<FRACBITS) // R_AddSingleSpriteDef #define FEETADJUST (4<<FRACBITS) // R_AddSingleSpriteDef
// Constant arrays used for psprite clipping boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 wadnum, UINT16 startlump, UINT16 endlump);
// and initializing clipping.
extern INT16 negonearray[MAXVIDWIDTH]; //faB: find sprites in wadfile, replace existing, add new ones
extern INT16 screenheightarray[MAXVIDWIDTH]; // (only sprites from namelist are added or replaced)
void R_AddSpriteDefs(UINT16 wadnum);
// ---------------------
// MASKED COLUMN DRAWING
// ---------------------
// vars for R_DrawMaskedColumn // vars for R_DrawMaskedColumn
extern INT16 *mfloorclip; extern INT16 *mfloorclip;
@ -47,9 +48,14 @@ extern fixed_t windowbottom;
void R_DrawMaskedColumn(column_t *column); void R_DrawMaskedColumn(column_t *column);
void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight); 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) // SPRITE RENDERING
void R_AddSpriteDefs(UINT16 wadnum); // ----------------
// 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); 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, boolean R_PrecipThingVisible (precipmobj_t *precipthing,
fixed_t precip_draw_dist); fixed_t precip_draw_dist);
// --------------
// MASKED DRAWING
// --------------
/** Used to count the amount of masked elements /** Used to count the amount of masked elements
* per portal to later group them in separate * per portal to later group them in separate
* drawnode lists. * drawnode lists.
@ -82,73 +91,18 @@ typedef struct
void R_DrawMasked(maskcount_t* masks, UINT8 nummasks); void R_DrawMasked(maskcount_t* masks, UINT8 nummasks);
// ----------- // ----------
// SKINS STUFF // VISSPRITES
// ----------- // ----------
#define SKINNAMESIZE 16
// should be all lowercase!! S_SKIN processing does a strlwr
#define DEFAULTSKIN "sonic"
#define DEFAULTSKIN2 "tails" // secondary player
#define DEFAULTNIGHTSSKIN 0
typedef struct // number of sprite lumps for spritewidth,offset,topoffset lookup tables
{ // Fab: this is a hack : should allocate the lookup tables per sprite
char name[SKINNAMESIZE+1]; // INT16 descriptive name of the skin #define MAXVISSPRITES 2048 // added 2-2-98 was 128
UINT16 wadnum;
skinflags_t flags;
char realname[SKINNAMESIZE+1]; // Display name for level completion. #define VISSPRITECHUNKBITS 6 // 2^6 = 64 sprites per chunk
char hudname[SKINNAMESIZE+1]; // HUD name to display (officially exactly 5 characters long) #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 typedef enum
{ {
// actual cuts // actual cuts
@ -227,6 +181,12 @@ typedef struct vissprite_s
INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing
} vissprite_t; } vissprite_t;
extern UINT32 visspritecount;
// ----------
// DRAW NODES
// ----------
// A drawnode is something that points to a 3D floor, 3D side, or masked // A drawnode is something that points to a 3D floor, 3D side, or masked
// middle texture. This is used for sorting with sprites. // middle texture. This is used for sorting with sprites.
typedef struct drawnode_s typedef struct drawnode_s
@ -241,23 +201,11 @@ typedef struct drawnode_s
struct drawnode_s *prev; struct drawnode_s *prev;
} drawnode_t; } 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); void R_InitDrawNodes(void);
char *GetPlayerFacePic(INT32 skinnum); // -----------------------
// SPRITE FRAME CHARACTERS
// -----------------------
// Functions to go from sprite character ID to frame number // Functions to go from sprite character ID to frame number
// for 2.1 compatibility this still uses the old 'A' + frame code // 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 "g_game.h"
#include "m_argv.h" #include "m_argv.h"
#include "r_main.h" // R_PointToAngle2() used to calc stereo sep. #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_system.h"
#include "i_sound.h" #include "i_sound.h"
#include "s_sound.h" #include "s_sound.h"

View file

@ -450,6 +450,20 @@ static int target_renderer = 0;
void SCR_ActuallyChangeRenderer(void) void SCR_ActuallyChangeRenderer(void)
{ {
setrenderneeded = target_renderer; 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 // setting the same renderer twice WILL crash your game, so let's not, please
if (rendermode == setrenderneeded) if (rendermode == setrenderneeded)
setrenderneeded = 0; setrenderneeded = 0;
@ -464,7 +478,7 @@ void SCR_ChangeRenderer(void)
{ {
target_renderer = cv_renderer.value; target_renderer = cv_renderer.value;
#ifdef HWRENDER #ifdef HWRENDER
if (M_CheckParm("-opengl")) if (M_CheckParm("-opengl") && (hwrenderloaded == 1))
target_renderer = rendermode = render_opengl; target_renderer = rendermode = render_opengl;
else else
#endif #endif

View file

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

View file

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

View file

@ -93,7 +93,8 @@ static INT32 numVidModes = -1;
*/ */
static char vidModeName[33][32]; // allow 33 different modes 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; 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}; 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 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 // To disable fullscreen at startup; is set in VID_PrepareModeList
boolean allow_fullscreen = false; 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_SetWindowName(const char *title);
static void Impl_SetWindowIcon(void); 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; static SDL_bool wasfullscreen = SDL_FALSE;
Uint32 rmask; Uint32 rmask;
@ -203,10 +205,13 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
} }
// Reposition window only in windowed mode // Reposition window only in windowed mode
SDL_SetWindowSize(window, width, height); SDL_SetWindowSize(window, width, height);
SDL_SetWindowPosition(window, if (reposition)
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)), {
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)) SDL_SetWindowPosition(window,
); SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)),
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window))
);
}
} }
} }
else else
@ -1435,7 +1440,7 @@ static SDL_bool Impl_CreateContext(void)
{ {
// Renderer-specific stuff // Renderer-specific stuff
#ifdef HWRENDER #ifdef HWRENDER
if (rendermode == render_opengl) if ((rendermode == render_opengl) && (hwrenderloaded != -1))
{ {
if (!sdlglcontext) if (!sdlglcontext)
sdlglcontext = SDL_GL_CreateContext(window); sdlglcontext = SDL_GL_CreateContext(window);
@ -1468,18 +1473,58 @@ static SDL_bool Impl_CreateContext(void)
return SDL_TRUE; 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) void VID_CheckRenderer(void)
{ {
SDL_bool rendererchanged = SDL_FALSE;
rendermode_t oldrenderer = rendermode;
if (dedicated) if (dedicated)
return; return;
#ifdef HWRENDER
if (!graphics_started)
VID_CheckGLLoaded(oldrenderer);
#endif
if (setrenderneeded) if (setrenderneeded)
{ {
rendermode = 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(); Impl_CreateContext();
} }
SDLSetMode(vid.width, vid.height, USE_FULLSCREEN); SDLSetMode(vid.width, vid.height, USE_FULLSCREEN, (rendererchanged ? SDL_FALSE : SDL_TRUE));
Impl_VideoSetupBuffer(); Impl_VideoSetupBuffer();
if (rendermode == render_soft) if (rendermode == render_soft)
@ -1490,16 +1535,16 @@ void VID_CheckRenderer(void)
bufSurface = NULL; bufSurface = NULL;
} }
#ifdef HWRENDER #ifdef HWRENDER
HWR_FreeTextureCache(); if (hwrenderloaded == 1) // Only if OpenGL ever loaded!
HWR_FreeTextureCache();
#endif #endif
SCR_SetDrawFuncs(); SCR_SetDrawFuncs();
} }
#ifdef HWRENDER #ifdef HWRENDER
else if (rendermode == render_opengl) else if (rendermode == render_opengl)
{
I_StartupHardwareGraphics();
R_InitHardwareMode(); R_InitHardwareMode();
} #else
(void)oldrenderer;
#endif #endif
} }
@ -1541,7 +1586,8 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
flags |= SDL_WINDOW_BORDERLESS; flags |= SDL_WINDOW_BORDERLESS;
#ifdef HWRENDER #ifdef HWRENDER
flags |= SDL_WINDOW_OPENGL; if (hwrenderloaded != -1)
flags |= SDL_WINDOW_OPENGL;
#endif #endif
// Create a window // Create a window
@ -1664,10 +1710,10 @@ void I_StartupGraphics(void)
#ifdef HWRENDER #ifdef HWRENDER
if (M_CheckParm("-opengl")) if (M_CheckParm("-opengl"))
rendermode = render_opengl; chosenrendermode = rendermode = render_opengl;
else if (M_CheckParm("-software")) else if (M_CheckParm("-software"))
#endif #endif
rendermode = render_soft; chosenrendermode = rendermode = render_soft;
usesdl2soft = M_CheckParm("-softblit"); usesdl2soft = M_CheckParm("-softblit");
borderlesswindow = M_CheckParm("-borderless"); borderlesswindow = M_CheckParm("-borderless");
@ -1675,7 +1721,10 @@ void I_StartupGraphics(void)
//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2); //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
VID_Command_ModeList_f(); VID_Command_ModeList_f();
#ifdef HWRENDER #ifdef HWRENDER
I_StartupHardwareGraphics(); if (M_CheckParm("-nogl"))
hwrenderloaded = -1; // Don't call SDL_GL_LoadLibrary
else
I_StartupHardwareGraphics();
#endif #endif
// Fury: we do window initialization after GL setup to allow // Fury: we do window initialization after GL setup to allow
@ -1763,13 +1812,22 @@ void I_StartupHardwareGraphics(void)
HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL); HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL);
HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL); HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL);
HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL); HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL);
// check gl renderer lib // check gl renderer lib
if (HWD.pfnGetRenderVersion() != VERSION) 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 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"));
rendermode = render_soft; hwrenderloaded = -1;
}
else 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 #endif
} }

View file

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

View file

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

View file

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