Merge branch 'master' into crumble-FOF

This commit is contained in:
Monster Iestyn 2017-01-25 19:38:07 +00:00
commit d0861a084a
113 changed files with 10238 additions and 7253 deletions

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
project(SRB2 project(SRB2
VERSION 2.1.14 VERSION 2.1.17
LANGUAGES C) LANGUAGES C)
if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})

3005
SRB2.cbp

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
version: 2.1.16.{branch}-{build} version: 2.1.17.{branch}-{build}
os: MinGW os: MinGW
environment: environment:

3
debian/docs vendored
View file

@ -1,2 +1 @@
readme.txt README.md
readme.txt

View file

@ -231,6 +231,7 @@ if(${SRB2_CONFIG_HAVE_BLUA})
add_definitions(-DHAVE_BLUA) add_definitions(-DHAVE_BLUA)
set(SRB2_LUA_SOURCES set(SRB2_LUA_SOURCES
lua_baselib.c lua_baselib.c
lua_blockmaplib.c
lua_consolelib.c lua_consolelib.c
lua_hooklib.c lua_hooklib.c
lua_hudlib.c lua_hudlib.c
@ -390,18 +391,25 @@ if(${SRB2_CONFIG_HWRENDER} AND ${SRB2_CONFIG_STATIC_OPENGL})
endif() endif()
if(${SRB2_CONFIG_USEASM}) if(${SRB2_CONFIG_USEASM})
#SRB2_ASM_FLAGS can be used to pass flags to either nasm or yasm.
if(${CMAKE_SYSTEM} MATCHES "Linux")
set(SRB2_ASM_FLAGS "-DLINUX ${SRB2_ASM_FLAGS}")
endif()
if(${SRB2_CONFIG_YASM}) if(${SRB2_CONFIG_YASM})
set(CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS} nas) set(CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS} nas)
set(CMAKE_ASM_YASM_FLAGS "${SRB2_ASM_FLAGS}" CACHE STRING "Flags used by the assembler during all build types.")
enable_language(ASM_YASM) enable_language(ASM_YASM)
else() else()
set(CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS} nas) set(CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS} nas)
set(CMAKE_ASM_NASM_FLAGS "${SRB2_ASM_FLAGS}" CACHE STRING "Flags used by the assembler during all build types.")
enable_language(ASM_NASM) enable_language(ASM_NASM)
endif() endif()
set(SRB2_USEASM ON) set(SRB2_USEASM ON)
add_definitions(-DUSEASM) add_definitions(-DUSEASM)
else() else()
set(SRB2_USEASM OFF) set(SRB2_USEASM OFF)
add_definitions(-DNOASM -DNONX86) add_definitions(-DNONX86 -DNORUSEASM)
endif() endif()
# Targets # Targets

View file

@ -179,6 +179,9 @@ endif
ifdef LINUX ifdef LINUX
UNIXCOMMON=1 UNIXCOMMON=1
ifndef NOGME
HAVE_LIBGME=1
endif
endif endif
ifdef SOLARIS ifdef SOLARIS
@ -315,6 +318,13 @@ LIBS+=$(PNG_LDFLAGS)
CFLAGS+=$(PNG_CFLAGS) CFLAGS+=$(PNG_CFLAGS)
endif endif
ZLIB_PKGCONFIG?=zlib
ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags)
ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs)
LIBS+=$(ZLIB_LDFLAGS)
CFLAGS+=$(ZLIB_CFLAGS)
ifdef HAVE_LIBGME ifdef HAVE_LIBGME
OPTS+=-DHAVE_LIBGME OPTS+=-DHAVE_LIBGME
@ -366,6 +376,14 @@ endif
OPTS:=-fno-exceptions $(OPTS) OPTS:=-fno-exceptions $(OPTS)
ifdef MOBJCONSISTANCY
OPTS+=-DMOBJCONSISTANCY
endif
ifdef PACKETDROP
OPTS+=-DPACKETDROP
endif
ifdef DEBUGMODE ifdef DEBUGMODE
# build with debugging information # build with debugging information
@ -375,7 +393,7 @@ ifdef GCC48
else else
CFLAGS+=-O0 CFLAGS+=-O0
endif endif
CFLAGS+= -Wall -DPARANOIA -DRANGECHECK CFLAGS+= -Wall -DPARANOIA -DRANGECHECK -DPACKETDROP -DMOBJCONSISTANCY
else else

View file

@ -258,6 +258,18 @@ INT32 I_PutEnv(char *variable)
return -1; return -1;
} }
INT32 I_ClipboardCopy(const char *data, size_t size)
{
(void)data;
(void)size;
return -1;
}
char *I_ClipboardPaste(void)
{
return NULL;
}
void I_RegisterSysCommands(void) {} void I_RegisterSysCommands(void) {}
#include "../sdl/dosstr.c" #include "../sdl/dosstr.c"

View file

@ -48,4 +48,5 @@ OBJS:=$(OBJS) \
$(OBJDIR)/lua_skinlib.o \ $(OBJDIR)/lua_skinlib.o \
$(OBJDIR)/lua_thinkerlib.o \ $(OBJDIR)/lua_thinkerlib.o \
$(OBJDIR)/lua_maplib.o \ $(OBJDIR)/lua_maplib.o \
$(OBJDIR)/lua_blockmaplib.o \
$(OBJDIR)/lua_hudlib.o $(OBJDIR)/lua_hudlib.o

View file

@ -84,19 +84,23 @@ UINT32 con_scalefactor; // text size scale factor
// hold 32 last lines of input for history // hold 32 last lines of input for history
#define CON_MAXPROMPTCHARS 256 #define CON_MAXPROMPTCHARS 256
#define CON_PROMPTCHAR '>' #define CON_PROMPTCHAR '$'
static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
static INT32 inputline; // current input line number static INT32 inputline; // current input line number
static INT32 inputhist; // line number of history input line to restore static INT32 inputhist; // line number of history input line to restore
static size_t input_cx; // position in current input line static size_t input_cur; // position of cursor in line
static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
static size_t input_len; // length of current line, used to bound cursor and such
// notice: input does NOT include the "$" at the start of the line. - 11/3/16
// protos. // protos.
static void CON_InputInit(void); static void CON_InputInit(void);
static void CON_RecalcSize(void); static void CON_RecalcSize(void);
static void CONS_hudlines_Change(void); static void CONS_hudlines_Change(void);
static void CONS_backcolor_Change(void);
static void CON_DrawBackpic(patch_t *pic, INT32 startx, INT32 destwidth); static void CON_DrawBackpic(patch_t *pic, INT32 startx, INT32 destwidth);
//static void CON_DrawBackpic2(pic_t *pic, INT32 startx, INT32 destwidth); //static void CON_DrawBackpic2(pic_t *pic, INT32 startx, INT32 destwidth);
@ -129,10 +133,12 @@ static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}
// whether to use console background picture, or translucent mode // whether to use console background picture, or translucent mode
static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Orange"}, static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Gray"}, {2, "Brown"},
{2, "Blue"}, {3, "Green"}, {4, "Gray"}, {3, "Red"}, {4, "Orange"}, {5, "Yellow"},
{5, "Red"}, {0, NULL}}; {6, "Green"}, {7, "Blue"}, {8, "Purple"},
consvar_t cons_backcolor = {"con_backcolor", "3", CV_SAVE, backcolor_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; {9, "Magenta"}, {10, "Aqua"},
{0, NULL}};
consvar_t cons_backcolor = {"con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL};
static void CON_Print(char *msg); static void CON_Print(char *msg);
@ -219,8 +225,9 @@ static void CONS_Bind_f(void)
// CONSOLE SETUP // CONSOLE SETUP
//====================================================================== //======================================================================
// Prepare a colormap for GREEN ONLY translucency over background // Font colormap colors
// // TODO: This could probably be improved somehow...
// These colormaps are 99% identical, with just a few changed bytes
UINT8 *yellowmap; UINT8 *yellowmap;
UINT8 *purplemap; UINT8 *purplemap;
UINT8 *lgreenmap; UINT8 *lgreenmap;
@ -229,44 +236,52 @@ UINT8 *graymap;
UINT8 *redmap; UINT8 *redmap;
UINT8 *orangemap; UINT8 *orangemap;
// Console BG colors // Console BG color
UINT8 *cwhitemap; UINT8 *consolebgmap = NULL;
UINT8 *corangemap;
UINT8 *cbluemap;
UINT8 *cgreenmap;
UINT8 *cgraymap;
UINT8 *credmap;
void CON_ReSetupBackColormap(UINT16 num) void CON_SetupBackColormap(void)
{ {
UINT16 i, j; UINT16 i, palsum;
UINT8 k; UINT8 j, palindex, shift;
UINT8 *pal = W_CacheLumpName(R_GetPalname(num), PU_CACHE); UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
// setup the green translucent background colormaps if (!consolebgmap)
for (i = 0, k = 0; i < 768; i += 3, k++) consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
shift = 6; // 12 colors -- shift of 7 means 6 colors
switch (cons_backcolor.value)
{ {
j = pal[i] + pal[i+1] + pal[i+2]; case 0: palindex = 15; break; // White
cwhitemap[k] = (UINT8)(15 - (j>>6)); case 1: palindex = 31; break; // Gray
corangemap[k] = (UINT8)(63 - (j>>6)); case 2: palindex = 239; break; // Brown
cbluemap[k] = (UINT8)(159 - (j>>6)); case 3: palindex = 47; break; // Red
cgreenmap[k] = (UINT8)(111 - (j>>6)); case 4: palindex = 63; break; // Orange
cgraymap[k] = (UINT8)(31 - (j>>6)); case 5: palindex = 79; shift = 7; break; // Yellow
credmap[k] = (UINT8)(47 - (j>>6)); case 6: palindex = 111; break; // Green
case 7: palindex = 159; break; // Blue
case 8: palindex = 199; shift = 7; break; // Purple
case 9: palindex = 187; break; // Magenta
case 10: palindex = 139; break; // Aqua
// Default green
default: palindex = 175; break;
}
// setup background colormap
for (i = 0, j = 0; i < 768; i += 3, j++)
{
palsum = (pal[i] + pal[i+1] + pal[i+2]) >> shift;
consolebgmap[j] = (UINT8)(palindex - palsum);
} }
} }
static void CON_SetupBackColormap(void) static void CONS_backcolor_Change(void)
{ {
INT32 i, j, k; CON_SetupBackColormap();
UINT8 *pal; }
cwhitemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); static void CON_SetupColormaps(void)
corangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); {
cbluemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); INT32 i;
cgreenmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
cgraymap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
credmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
yellowmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); yellowmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
graymap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); graymap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
@ -276,20 +291,6 @@ static void CON_SetupBackColormap(void)
redmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); redmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
orangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); orangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
pal = W_CacheLumpName("PLAYPAL", PU_CACHE);
// setup the green translucent background colormaps
for (i = 0, k = 0; i < 768; i += 3, k++)
{
j = pal[i] + pal[i+1] + pal[i+2];
cwhitemap[k] = (UINT8)(15 - (j>>6));
corangemap[k] = (UINT8)(63 - (j>>6));
cbluemap[k] = (UINT8)(159 - (j>>6));
cgreenmap[k] = (UINT8)(111 - (j>>6));
cgraymap[k] = (UINT8)(31 - (j>>6));
credmap[k] = (UINT8)(47 - (j>>6));
}
// setup the other colormaps, for console text // setup the other colormaps, for console text
// these don't need to be aligned, unless you convert the // these don't need to be aligned, unless you convert the
@ -320,6 +321,9 @@ static void CON_SetupBackColormap(void)
redmap[9] = (UINT8)32; redmap[9] = (UINT8)32;
orangemap[3] = (UINT8)52; orangemap[3] = (UINT8)52;
orangemap[9] = (UINT8)57; orangemap[9] = (UINT8)57;
// Init back colormap
CON_SetupBackColormap();
} }
// Setup the console text buffer // Setup the console text buffer
@ -343,7 +347,7 @@ void CON_Init(void)
con_width = 0; con_width = 0;
CON_RecalcSize(); CON_RecalcSize();
CON_SetupBackColormap(); CON_SetupColormaps();
//note: CON_Ticker should always execute at least once before D_Display() //note: CON_Ticker should always execute at least once before D_Display()
con_clipviewtop = -1; // -1 does not clip con_clipviewtop = -1; // -1 does not clip
@ -386,14 +390,10 @@ void CON_Init(void)
// //
static void CON_InputInit(void) static void CON_InputInit(void)
{ {
INT32 i;
// prepare the first prompt line // prepare the first prompt line
memset(inputlines, 0, sizeof (inputlines)); memset(inputlines, 0, sizeof (inputlines));
for (i = 0; i < 32; i++)
inputlines[i][0] = CON_PROMPTCHAR;
inputline = 0; inputline = 0;
input_cx = 1; input_cur = input_sel = input_len = 0;
} }
//====================================================================== //======================================================================
@ -618,13 +618,91 @@ void CON_Ticker(void)
} }
} }
//
// ----
//
// Shortcuts for adding and deleting characters, strings, and sections
// Necessary due to moving cursor
//
static void CON_InputClear(void)
{
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
input_cur = input_sel = input_len = 0;
}
static void CON_InputSetString(const char *c)
{
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
strcpy(inputlines[inputline], c);
input_cur = input_sel = input_len = strlen(c);
}
static void CON_InputAddString(const char *c)
{
size_t csize = strlen(c);
if (input_len + csize > CON_MAXPROMPTCHARS-1)
return;
if (input_cur != input_len)
memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
memcpy(&inputlines[inputline][input_cur], c, csize);
input_len += csize;
input_sel = (input_cur += csize);
}
static void CON_InputDelSelection(void)
{
size_t start, end, len;
if (input_cur > input_sel)
{
start = input_sel;
end = input_cur;
}
else
{
start = input_cur;
end = input_sel;
}
len = (end - start);
if (end != input_len)
memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
memset(&inputlines[inputline][input_len - len], 0, len);
input_len -= len;
input_sel = input_cur = start;
}
static void CON_InputAddChar(char c)
{
if (input_len >= CON_MAXPROMPTCHARS-1)
return;
if (input_cur != input_len)
memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
inputlines[inputline][input_cur++] = c;
inputlines[inputline][++input_len] = 0;
input_sel = input_cur;
}
static void CON_InputDelChar(void)
{
if (!input_cur)
return;
if (input_cur != input_len)
memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
inputlines[inputline][--input_len] = 0;
input_sel = --input_cur;
}
//
// ----
//
// Handles console key input // Handles console key input
// //
boolean CON_Responder(event_t *ev) boolean CON_Responder(event_t *ev)
{ {
static boolean consdown; static UINT8 consdown = false; // console is treated differently due to rare usage
static boolean shiftdown;
static boolean ctrldown;
// sequential completions a la 4dos // sequential completions a la 4dos
static char completion[80]; static char completion[80];
@ -639,13 +717,8 @@ boolean CON_Responder(event_t *ev)
// let go keyup events, don't eat them // let go keyup events, don't eat them
if (ev->type != ev_keydown && ev->type != ev_console) if (ev->type != ev_keydown && ev->type != ev_console)
{ {
if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT) if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
shiftdown = false;
else if (ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL)
ctrldown = false;
else if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
consdown = false; consdown = false;
return false; return false;
} }
@ -684,94 +757,110 @@ boolean CON_Responder(event_t *ev)
consoletoggle = true; consoletoggle = true;
return true; return true;
} }
} }
// eat shift only if console active // Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
if (key == KEY_LSHIFT || key == KEY_RSHIFT) if (key == KEY_LSHIFT || key == KEY_RSHIFT
{ || key == KEY_LCTRL || key == KEY_RCTRL
shiftdown = true; || key == KEY_LALT || key == KEY_RALT)
return true; return true;
}
// same for ctrl // ctrl modifier -- changes behavior, adds shortcuts
if (key == KEY_LCTRL || key == KEY_RCTRL) if (ctrldown)
{ {
ctrldown = true; // show all cvars/commands that match what we have inputted
return true; if (key == KEY_TAB)
{
size_t i, len;
if (!completion[0])
{
if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
return true;
strcpy(completion, inputlines[inputline]);
comskips = varskips = 0;
}
len = strlen(completion);
//first check commands
CONS_Printf("\nCommands:\n");
for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i))
CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
if (i == 0) CONS_Printf(" (none)\n");
//now we move on to CVARs
CONS_Printf("Variables:\n");
for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i))
CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
if (i == 0) CONS_Printf(" (none)\n");
return true;
}
// ---
if (key == KEY_HOME) // oldest text in buffer
{
con_scrollup = (con_totallines-((con_curlines-16)>>3));
return true;
}
else if (key == KEY_END) // most recent text in buffer
{
con_scrollup = 0;
return true;
}
if (key == 'x' || key == 'X')
{
if (input_sel > input_cur)
I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
else
I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
CON_InputDelSelection();
completion[0] = 0;
return true;
}
else if (key == 'c' || key == 'C')
{
if (input_sel > input_cur)
I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
else
I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
return true;
}
else if (key == 'v' || key == 'V')
{
const char *paste = I_ClipboardPaste();
if (input_sel != input_cur)
CON_InputDelSelection();
if (paste != NULL)
CON_InputAddString(paste);
completion[0] = 0;
return true;
}
// Select all
if (key == 'a' || key == 'A')
{
input_sel = 0;
input_cur = input_len;
return true;
}
// don't eat the key
return false;
} }
// command completion forward (tab) and backward (shift-tab) // command completion forward (tab) and backward (shift-tab)
if (key == KEY_TAB) if (key == KEY_TAB)
{ {
// show all cvars/commands that match what we have inputted
if (ctrldown)
{
UINT32 i;
size_t stop = input_cx - 1;
char nameremainder[255];
if (input_cx < 2 || strlen(inputlines[inputline]+1) >= 80)
return true;
strcpy(completion, inputlines[inputline]+1);
// trimming: stop at the first newline
for (i = 0; i < input_cx - 1; ++i)
{
if (completion[i] == ' ')
{
completion[i] = '\0';
stop = i;
break;
}
}
i = 0;
//first check commands
CONS_Printf("\nCommands:\n");
for (cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, i))
{
strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop));
nameremainder[strlen(cmd)-(stop)] = '\0';
CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, nameremainder);
++i;
}
if (i == 0)
CONS_Printf(" (none)\n");
i = 0;
//now we move on to CVARs
CONS_Printf("Variables:\n");
for (cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, i))
{
strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop));
nameremainder[strlen(cmd)-(stop)] = '\0';
CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, nameremainder);
++i;
}
if (i == 0)
CONS_Printf(" (none)\n");
return true;
}
// sequential command completion forward and backward // sequential command completion forward and backward
// remember typing for several completions (a-la-4dos) // remember typing for several completions (a-la-4dos)
if (inputlines[inputline][input_cx-1] != ' ') if (!completion[0])
{ {
if (strlen(inputlines[inputline]+1) < 80) if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
strcpy(completion, inputlines[inputline]+1); return true;
else strcpy(completion, inputlines[inputline]);
completion[0] = 0;
comskips = varskips = 0; comskips = varskips = 0;
} }
else else
@ -783,37 +872,26 @@ boolean CON_Responder(event_t *ev)
if (--varskips < 0) if (--varskips < 0)
comskips = -comskips - 2; comskips = -comskips - 2;
} }
else if (comskips > 0) else if (comskips > 0) comskips--;
comskips--;
} }
else else
{ {
if (comskips < 0) if (comskips < 0) varskips++;
varskips++; else comskips++;
else
comskips++;
} }
} }
if (comskips >= 0) if (comskips >= 0)
{ {
cmd = COM_CompleteCommand(completion, comskips); cmd = COM_CompleteCommand(completion, comskips);
if (!cmd) if (!cmd) // dirty: make sure if comskips is zero, to have a neg value
// dirty: make sure if comskips is zero, to have a neg value
comskips = -comskips - 1; comskips = -comskips - 1;
} }
if (comskips < 0) if (comskips < 0)
cmd = CV_CompleteVar(completion, varskips); cmd = CV_CompleteVar(completion, varskips);
if (cmd) if (cmd)
{ CON_InputSetString(va("%s ", cmd));
memset(inputlines[inputline]+1, 0, CON_MAXPROMPTCHARS-1);
strcpy(inputlines[inputline]+1, cmd);
input_cx = strlen(cmd) + 1;
inputlines[inputline][input_cx] = ' ';
input_cx++;
inputlines[inputline][input_cx] = 0;
}
else else
{ {
if (comskips > 0) if (comskips > 0)
@ -839,47 +917,80 @@ boolean CON_Responder(event_t *ev)
return true; return true;
} }
if (key == KEY_HOME) // oldest text in buffer if (key == KEY_LEFTARROW)
{ {
con_scrollup = (con_totallines-((con_curlines-16)>>3)); if (input_cur != 0)
--input_cur;
if (!shiftdown)
input_sel = input_cur;
return true; return true;
} }
else if (key == KEY_END) // most recent text in buffer else if (key == KEY_RIGHTARROW)
{ {
con_scrollup = 0; if (input_cur < input_len)
++input_cur;
if (!shiftdown)
input_sel = input_cur;
return true; return true;
} }
else if (key == KEY_HOME)
{
input_cur = 0;
if (!shiftdown)
input_sel = input_cur;
return true;
}
else if (key == KEY_END)
{
input_cur = input_len;
if (!shiftdown)
input_sel = input_cur;
return true;
}
// At this point we're messing with input
// Clear completion
completion[0] = 0;
// command enter // command enter
if (key == KEY_ENTER) if (key == KEY_ENTER)
{ {
if (input_cx < 2) if (!input_len)
return true; return true;
// push the command // push the command
COM_BufAddText(inputlines[inputline]+1); COM_BufAddText(inputlines[inputline]);
COM_BufAddText("\n"); COM_BufAddText("\n");
CONS_Printf("%s\n", inputlines[inputline]); CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]);
inputline = (inputline+1) & 31; inputline = (inputline+1) & 31;
inputhist = inputline; inputhist = inputline;
CON_InputClear();
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
inputlines[inputline][0] = CON_PROMPTCHAR;
input_cx = 1;
return true; return true;
} }
// backspace command prompt // backspace and delete command prompt
if (key == KEY_BACKSPACE) if (input_sel != input_cur)
{ {
if (input_cx > 1) if (key == KEY_BACKSPACE || key == KEY_DEL)
{ {
input_cx--; CON_InputDelSelection();
inputlines[inputline][input_cx] = 0; return true;
} }
}
else if (key == KEY_BACKSPACE)
{
CON_InputDelChar();
return true;
}
else if (key == KEY_DEL)
{
if (input_cur == input_len)
return true;
++input_cur;
CON_InputDelChar();
return true; return true;
} }
@ -888,18 +999,15 @@ boolean CON_Responder(event_t *ev)
{ {
// copy one of the previous inputlines to the current // copy one of the previous inputlines to the current
do do
{
inputhist = (inputhist - 1) & 31; // cycle back inputhist = (inputhist - 1) & 31; // cycle back
} while (inputhist != inputline && !inputlines[inputhist][1]); while (inputhist != inputline && !inputlines[inputhist][0]);
// stop at the last history input line, which is the // stop at the last history input line, which is the
// current line + 1 because we cycle through the 32 input lines // current line + 1 because we cycle through the 32 input lines
if (inputhist == inputline) if (inputhist == inputline)
inputhist = (inputline + 1) & 31; inputhist = (inputline + 1) & 31;
M_Memcpy(inputlines[inputline], inputlines[inputhist], CON_MAXPROMPTCHARS); CON_InputSetString(inputlines[inputhist]);
input_cx = strlen(inputlines[inputline]);
return true; return true;
} }
@ -909,23 +1017,14 @@ boolean CON_Responder(event_t *ev)
if (inputhist == inputline) if (inputhist == inputline)
return true; return true;
do do
{
inputhist = (inputhist + 1) & 31; inputhist = (inputhist + 1) & 31;
} while (inputhist != inputline && !inputlines[inputhist][1]); while (inputhist != inputline && !inputlines[inputhist][0]);
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
// back to currentline // back to currentline
if (inputhist == inputline) if (inputhist == inputline)
{ CON_InputClear();
inputlines[inputline][0] = CON_PROMPTCHAR;
input_cx = 1;
}
else else
{ CON_InputSetString(inputlines[inputhist]);
strcpy(inputlines[inputline], inputlines[inputhist]);
input_cx = strlen(inputlines[inputline]);
}
return true; return true;
} }
@ -950,15 +1049,12 @@ boolean CON_Responder(event_t *ev)
return false; return false;
// add key to cmd line here // add key to cmd line here
if (input_cx < CON_MAXPROMPTCHARS) if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers
{ key = key + 'a' - 'A';
if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers
key = key + 'a' - 'A';
inputlines[inputline][input_cx] = (char)key; if (input_sel != input_cur)
inputlines[inputline][input_cx + 1] = 0; CON_InputDelSelection();
input_cx++; CON_InputAddChar(key);
}
return true; return true;
} }
@ -1242,26 +1338,89 @@ void CONS_Error(const char *msg)
// //
static void CON_DrawInput(void) static void CON_DrawInput(void)
{ {
char *p;
size_t c;
INT32 x, y;
INT32 charwidth = (INT32)con_scalefactor << 3; INT32 charwidth = (INT32)con_scalefactor << 3;
const char *p = inputlines[inputline];
// input line scrolls left if it gets too long size_t c, clen, cend;
p = inputlines[inputline]; UINT8 lellip = 0, rellip = 0;
if (input_cx >= con_width-11) INT32 x, y, i;
p += input_cx - (con_width-11) + 1;
y = con_curlines - 12 * con_scalefactor; y = con_curlines - 12 * con_scalefactor;
x = charwidth*2;
for (c = 0, x = charwidth; c < con_width-11; c++, x += charwidth) clen = con_width-13;
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
// draw the blinking cursor if (input_len <= clen)
// {
x = ((input_cx >= con_width-11) ? (INT32)(con_width-11) : (INT32)((input_cx + 1)) * charwidth); c = 0;
if (con_tick < 4) clen = input_len;
V_DrawCharacter(x, y, '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); }
else // input line scrolls left if it gets too long
{
clen -= 2; // There will always be some extra truncation -- but where is what we'll find out
if (input_cur <= clen/2)
{
// Close enough to right edge to show all
c = 0;
// Always will truncate right side from this position, so always draw right ellipsis
rellip = 1;
}
else
{
// Cursor in the middle (or right side) of input
// Move over for the ellipsis
c = input_cur - (clen/2) + 2;
x += charwidth*2;
lellip = 1;
if (c + clen >= input_len)
{
// Cursor in the right side of input
// We were too far over, so move back
c = input_len - clen;
}
else
{
// Cursor in the middle -- ellipses on both sides
clen -= 2;
rellip = 1;
}
}
}
if (lellip)
{
x -= charwidth*3;
if (input_sel < c)
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
for (i = 0; i < 3; ++i, x += charwidth)
V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
}
else
V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
for (cend = c + clen; c < cend; ++c, x += charwidth)
{
if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c))
{
V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 77 | V_NOSCALESTART);
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, !cv_allcaps.value);
}
else
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
if (c == input_cur && con_tick >= 4)
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
}
if (cend == input_cur && con_tick >= 4)
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
if (rellip)
{
if (input_sel > cend)
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
for (i = 0; i < 3; ++i, x += charwidth)
V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
}
} }
// draw the last lines of console text to the top of the screen // draw the last lines of console text to the top of the screen
@ -1417,7 +1576,7 @@ static void CON_DrawConsole(void)
{ {
// inu: no more width (was always 0 and vid.width) // inu: no more width (was always 0 and vid.width)
if (rendermode != render_none) if (rendermode != render_none)
V_DrawFadeConsBack(con_curlines, cons_backcolor.value); // translucent background V_DrawFadeConsBack(con_curlines); // translucent background
} }
// draw console text lines from top to bottom // draw console text lines from top to bottom

View file

@ -40,11 +40,10 @@ extern consvar_t cons_backcolor;
extern UINT8 *yellowmap, *purplemap, *lgreenmap, *bluemap, *graymap, *redmap, *orangemap; extern UINT8 *yellowmap, *purplemap, *lgreenmap, *bluemap, *graymap, *redmap, *orangemap;
// Console bg colors: // Console bg color (auto updated to match)
extern UINT8 *cwhitemap, *corangemap, *cbluemap, *cgreenmap, *cgraymap, extern UINT8 *consolebgmap;
*credmap;
void CON_ReSetupBackColormap(UINT16 num); void CON_SetupBackColormap(void);
void CON_ClearHUD(void); // clear heads up messages void CON_ClearHUD(void); // clear heads up messages
void CON_Ticker(void); void CON_Ticker(void);

File diff suppressed because it is too large Load diff

View file

@ -59,7 +59,7 @@ typedef enum
// 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
// allows HSendPacket(,true,,) to return false. // allows HSendPacket(*, true, *, *) to return false.
// In addition, this packet can't occupy all the available slots. // In addition, this packet can't occupy all the available slots.
PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file. PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
@ -76,11 +76,19 @@ typedef enum
NUMPACKETTYPE NUMPACKETTYPE
} packettype_t; } packettype_t;
#ifdef PACKETDROP
void Command_Drop(void);
void Command_Droprate(void);
#endif
#ifdef _DEBUG
void Command_Numnodes(void);
#endif
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma pack(1) #pragma pack(1)
#endif #endif
// client to server packet // Client to server packet
typedef struct typedef struct
{ {
UINT8 client_tic; UINT8 client_tic;
@ -89,7 +97,7 @@ typedef struct
ticcmd_t cmd; ticcmd_t cmd;
} ATTRPACK clientcmd_pak; } ATTRPACK clientcmd_pak;
// splitscreen packet // Splitscreen packet
// WARNING: must have the same format of clientcmd_pak, for more easy use // WARNING: must have the same format of clientcmd_pak, for more easy use
typedef struct typedef struct
{ {
@ -110,16 +118,16 @@ typedef struct
UINT8 starttic; UINT8 starttic;
UINT8 numtics; UINT8 numtics;
UINT8 numslots; // "Slots filled": Highest player number in use plus one. UINT8 numslots; // "Slots filled": Highest player number in use plus one.
ticcmd_t cmds[45]; // normally [BACKUPTIC][MAXPLAYERS] but too large ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large
} ATTRPACK servertics_pak; } ATTRPACK servertics_pak;
// sent to client when all consistency data // Sent to client when all consistency data
// for players has been restored // for players has been restored
typedef struct typedef struct
{ {
UINT32 randomseed; UINT32 randomseed;
//ctf flag stuff // CTF flag stuff
SINT8 flagplayer[2]; SINT8 flagplayer[2];
INT32 flagloose[2]; INT32 flagloose[2];
INT32 flagflags[2]; INT32 flagflags[2];
@ -127,11 +135,11 @@ typedef struct
fixed_t flagy[2]; fixed_t flagy[2];
fixed_t flagz[2]; fixed_t flagz[2];
UINT32 ingame; // spectator bit for each player UINT32 ingame; // Spectator bit for each player
UINT32 ctfteam; // if not spectator, then which team? INT32 ctfteam[MAXPLAYERS]; // Which team? (can't be 1 bit, since in regular Match there are no teams)
// Resynch game scores and the like all at once // Resynch game scores and the like all at once
UINT32 score[MAXPLAYERS]; // Everyone's score. UINT32 score[MAXPLAYERS]; // Everyone's score
INT16 numboxes[MAXPLAYERS]; INT16 numboxes[MAXPLAYERS];
INT16 totalring[MAXPLAYERS]; INT16 totalring[MAXPLAYERS];
tic_t realtime[MAXPLAYERS]; tic_t realtime[MAXPLAYERS];
@ -140,14 +148,14 @@ typedef struct
typedef struct typedef struct
{ {
//player stuff // Player stuff
UINT8 playernum; UINT8 playernum;
// Do not send anything visual related. // Do not send anything visual related.
// Only send data that we need to know for physics. // Only send data that we need to know for physics.
UINT8 playerstate; //playerstate_t UINT8 playerstate; // playerstate_t
UINT32 pflags; //pflags_t UINT32 pflags; // pflags_t
UINT8 panim; //panim_t UINT8 panim; // panim_t
angle_t aiming; angle_t aiming;
INT32 currentweapon; INT32 currentweapon;
@ -155,7 +163,7 @@ typedef struct
UINT16 powers[NUMPOWERS]; UINT16 powers[NUMPOWERS];
// Score is resynched in the confirm resync packet // Score is resynched in the confirm resync packet
INT32 health; INT32 rings;
SINT8 lives; SINT8 lives;
SINT8 continues; SINT8 continues;
UINT8 scoreadd; UINT8 scoreadd;
@ -176,9 +184,9 @@ typedef struct
UINT8 charability; UINT8 charability;
UINT8 charability2; UINT8 charability2;
UINT32 charflags; UINT32 charflags;
UINT32 thokitem; //mobjtype_t UINT32 thokitem; // mobjtype_t
UINT32 spinitem; //mobjtype_t UINT32 spinitem; // mobjtype_t
UINT32 revitem; //mobjtype_t UINT32 revitem; // mobjtype_t
fixed_t actionspd; fixed_t actionspd;
fixed_t mindash; fixed_t mindash;
fixed_t maxdash; fixed_t maxdash;
@ -234,8 +242,9 @@ typedef struct
INT32 onconveyor; INT32 onconveyor;
//player->mo stuff //player->mo stuff
UINT8 hasmo; //boolean UINT8 hasmo; // Boolean
INT32 health;
angle_t angle; angle_t angle;
fixed_t x; fixed_t x;
fixed_t y; fixed_t y;
@ -261,10 +270,10 @@ typedef struct
typedef struct typedef struct
{ {
UINT8 version; // different versions don't work UINT8 version; // Different versions don't work
UINT8 subversion; // contains build version UINT8 subversion; // Contains build version
// server launch stuffs // Server launch stuffs
UINT8 serverplayer; UINT8 serverplayer;
UINT8 totalslotnum; // "Slots": highest player number in use plus one. UINT8 totalslotnum; // "Slots": highest player number in use plus one.
@ -278,18 +287,18 @@ typedef struct
UINT8 gametype; UINT8 gametype;
UINT8 modifiedgame; UINT8 modifiedgame;
SINT8 adminplayer; // needs to be signed SINT8 adminplayer; // Needs to be signed
char server_context[8]; // unique context id, generated at server startup. char server_context[8]; // Unique context id, generated at server startup.
UINT8 varlengthinputs[0]; // playernames and netvars UINT8 varlengthinputs[0]; // Playernames and netvars
} ATTRPACK serverconfig_pak; } ATTRPACK serverconfig_pak;
typedef struct { typedef struct {
UINT8 fileid; UINT8 fileid;
UINT32 position; UINT32 position;
UINT16 size; UINT16 size;
UINT8 data[0]; // size is variable using hardware_MAXPACKETLENGTH UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH
} ATTRPACK filetx_pak; } ATTRPACK filetx_pak;
#ifdef _MSC_VER #ifdef _MSC_VER
@ -298,14 +307,14 @@ typedef struct {
typedef struct typedef struct
{ {
UINT8 version; // different versions don't work UINT8 version; // Different versions don't work
UINT8 subversion; // contains build version UINT8 subversion; // Contains build version
UINT8 localplayers; UINT8 localplayers;
UINT8 mode; UINT8 mode;
} ATTRPACK clientconfig_pak; } ATTRPACK clientconfig_pak;
#define MAXSERVERNAME 32 #define MAXSERVERNAME 32
// this packet is too large // This packet is too large
typedef struct typedef struct
{ {
UINT8 version; UINT8 version;
@ -371,45 +380,45 @@ typedef struct
} ATTRPACK plrconfig; } ATTRPACK plrconfig;
// //
// Network packet data. // Network packet data
// //
typedef struct typedef struct
{ {
UINT32 checksum; UINT32 checksum;
UINT8 ack; // if not null the node asks for acknowledgement, the receiver must resend the ack UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack
UINT8 ackreturn; // the return of the ack number UINT8 ackreturn; // The return of the ack number
UINT8 packettype; UINT8 packettype;
UINT8 reserved; // padding UINT8 reserved; // Padding
union union
{ {
clientcmd_pak clientpak; // 144 bytes clientcmd_pak clientpak; // 144 bytes
client2cmd_pak client2pak; // 200 bytes client2cmd_pak client2pak; // 200 bytes
servertics_pak serverpak; // 132495 bytes servertics_pak serverpak; // 132495 bytes (more around 360, no?)
serverconfig_pak servercfg; // 773 bytes serverconfig_pak servercfg; // 773 bytes
resynchend_pak resynchend; // resynchend_pak resynchend; //
resynch_pak resynchpak; // resynch_pak resynchpak; //
UINT8 resynchgot; // UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...)
filetx_pak filetxpak; // 139 bytes filetx_pak filetxpak; // 139 bytes
clientconfig_pak clientcfg; // 136 bytes clientconfig_pak clientcfg; // 136 bytes
serverinfo_pak serverinfo; // 1024 bytes serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
askinfo_pak askinfo; // 61 bytes askinfo_pak askinfo; // 61 bytes
msaskinfo_pak msaskinfo; // 22 bytes msaskinfo_pak msaskinfo; // 22 bytes
plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes (I'd say 36~38)
plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE)
#ifdef NEWPING #ifdef NEWPING
UINT32 pingtable[MAXPLAYERS]; // 128 bytes UINT32 pingtable[MAXPLAYERS]; // 128 bytes
#endif #endif
} u; // this is needed to pack diff packet types data together } u; // This is needed to pack diff packet types data together
} ATTRPACK doomdata_t; } ATTRPACK doomdata_t;
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma pack() #pragma pack()
#endif #endif
#define MAXSERVERLIST 64 // depends only on the display #define MAXSERVERLIST 64 // Depends only on the display
typedef struct typedef struct
{ {
SINT8 node; SINT8 node;
@ -420,7 +429,7 @@ extern serverelem_t serverlist[MAXSERVERLIST];
extern UINT32 serverlistcount; extern UINT32 serverlistcount;
extern INT32 mapchangepending; extern INT32 mapchangepending;
// points inside doomcom // Points inside doomcom
extern doomdata_t *netbuffer; extern doomdata_t *netbuffer;
extern consvar_t cv_playbackspeed; extern consvar_t cv_playbackspeed;
@ -441,26 +450,28 @@ extern consvar_t cv_playbackspeed;
#define KICK_MSG_CUSTOM_BAN 8 #define KICK_MSG_CUSTOM_BAN 8
extern boolean server; extern boolean server;
extern boolean dedicated; // for dedicated server #define client (!server)
extern boolean dedicated; // For dedicated server
extern UINT16 software_MAXPACKETLENGTH; extern UINT16 software_MAXPACKETLENGTH;
extern boolean acceptnewnode; extern boolean acceptnewnode;
extern SINT8 servernode; extern SINT8 servernode;
void Command_Ping_f(void); void Command_Ping_f(void);
extern tic_t connectiontimeout; extern tic_t connectiontimeout;
extern tic_t jointimeout;
#ifdef NEWPING #ifdef NEWPING
extern UINT16 pingmeasurecount; extern UINT16 pingmeasurecount;
extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS];
#endif #endif
extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend; extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed;
// used in d_net, the only dependence // Used in d_net, the only dependence
tic_t ExpandTics(INT32 low); tic_t ExpandTics(INT32 low);
void D_ClientServerInit(void); void D_ClientServerInit(void);
// initialise the other field // Initialise the other field
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)); void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam); void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam);
void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player
@ -478,14 +489,14 @@ void CL_RemoveSplitscreenPlayer(void);
void CL_Reset(void); void CL_Reset(void);
void CL_ClearPlayer(INT32 playernum); void CL_ClearPlayer(INT32 playernum);
void CL_UpdateServerList(boolean internetsearch, INT32 room); void CL_UpdateServerList(boolean internetsearch, INT32 room);
// is there a game running // Is there a game running
boolean Playing(void); boolean Playing(void);
// Broadcasts special packets to other players // Broadcasts special packets to other players
// to notify of game exit // to notify of game exit
void D_QuitNetGame(void); void D_QuitNetGame(void);
//? how many ticks to run? //? How many ticks to run?
void TryRunTics(tic_t realtic); void TryRunTics(tic_t realtic);
// extra data for lmps // extra data for lmps

View file

@ -73,6 +73,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#include "dehacked.h" // Dehacked list test #include "dehacked.h" // Dehacked list test
#include "m_cond.h" // condition initialization #include "m_cond.h" // condition initialization
#include "fastcmp.h" #include "fastcmp.h"
#include "keys.h"
#ifdef CMAKECONFIG #ifdef CMAKECONFIG
#include "config.h" #include "config.h"
@ -176,6 +177,38 @@ void D_PostEvent(const event_t *ev)
void D_PostEvent_end(void) {}; void D_PostEvent_end(void) {};
#endif #endif
// modifier keys
UINT8 shiftdown = 0; // 0x1 left, 0x2 right
UINT8 ctrldown = 0; // 0x1 left, 0x2 right
UINT8 altdown = 0; // 0x1 left, 0x2 right
//
// D_ModifierKeyResponder
// Sets global shift/ctrl/alt variables, never actually eats events
//
static inline void D_ModifierKeyResponder(event_t *ev)
{
if (ev->type == ev_keydown) switch (ev->data1)
{
case KEY_LSHIFT: shiftdown |= 0x1; return;
case KEY_RSHIFT: shiftdown |= 0x2; return;
case KEY_LCTRL: ctrldown |= 0x1; return;
case KEY_RCTRL: ctrldown |= 0x2; return;
case KEY_LALT: altdown |= 0x1; return;
case KEY_RALT: altdown |= 0x2; return;
default: return;
}
else if (ev->type == ev_keyup) switch (ev->data1)
{
case KEY_LSHIFT: shiftdown &= ~0x1; return;
case KEY_RSHIFT: shiftdown &= ~0x2; return;
case KEY_LCTRL: ctrldown &= ~0x1; return;
case KEY_RCTRL: ctrldown &= ~0x2; return;
case KEY_LALT: altdown &= ~0x1; return;
case KEY_RALT: altdown &= ~0x2; return;
default: return;
}
}
// //
// D_ProcessEvents // D_ProcessEvents
// Send all the events of the given timestamp down the responder chain // Send all the events of the given timestamp down the responder chain
@ -188,6 +221,9 @@ void D_ProcessEvents(void)
{ {
ev = &events[eventtail]; ev = &events[eventtail];
// Set global shift/ctrl/alt down variables
D_ModifierKeyResponder(ev); // never eats events
// Screenshots over everything so that they can be taken anywhere. // Screenshots over everything so that they can be taken anywhere.
if (M_ScreenshotResponder(ev)) if (M_ScreenshotResponder(ev))
continue; // ate the event continue; // ate the event

View file

@ -31,18 +31,18 @@
// //
// NETWORKING // NETWORKING
// //
// gametic is the tic about to be (or currently being) run // gametic is the tic about to (or currently being) run
// server: // Server:
// maketic is the tic that hasn't had control made for it yet // maketic is the tic that hasn't had control made for it yet
// nettics: is the tic for each node // nettics is the tic for each node
// firsttictosend: is the lowest value of nettics // firstticstosend is the lowest value of nettics
// client: // Client:
// neededtic: is the tic needed by the client to run the game // neededtic is the tic needed by the client to run the game
// firsttictosend: is used to optimize a condition // firstticstosend is used to optimize a condition
// normally maketic >= gametic > 0 // Normally maketic >= gametic > 0
#define FORCECLOSE 0x8000 #define FORCECLOSE 0x8000
tic_t connectiontimeout = (15*TICRATE); tic_t connectiontimeout = (10*TICRATE);
/// \brief network packet /// \brief network packet
doomcom_t *doomcom = NULL; doomcom_t *doomcom = NULL;
@ -62,7 +62,7 @@ INT32 net_bandwidth;
/// \brief max length per packet /// \brief max length per packet
INT16 hardware_MAXPACKETLENGTH; INT16 hardware_MAXPACKETLENGTH;
void (*I_NetGet)(void) = NULL; boolean (*I_NetGet)(void) = NULL;
void (*I_NetSend)(void) = NULL; void (*I_NetSend)(void) = NULL;
boolean (*I_NetCanSend)(void) = NULL; boolean (*I_NetCanSend)(void) = NULL;
boolean (*I_NetCanGet)(void) = NULL; boolean (*I_NetCanGet)(void) = NULL;
@ -129,9 +129,9 @@ boolean Net_GetNetStat(void)
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Some structs and functions for acknowledgement of packets // Some structs and functions for acknowledgement of packets
// ----------------------------------------------------------------- // -----------------------------------------------------------------
#define MAXACKPACKETS 96 // minimum number of nodes #define MAXACKPACKETS 96 // Minimum number of nodes (wat)
#define MAXACKTOSEND 96 #define MAXACKTOSEND 96
#define URGENTFREESLOTENUM 10 #define URGENTFREESLOTNUM 10
#define ACKTOSENDTIMEOUT (TICRATE/11) #define ACKTOSENDTIMEOUT (TICRATE/11)
#ifndef NONET #ifndef NONET
@ -139,10 +139,10 @@ typedef struct
{ {
UINT8 acknum; UINT8 acknum;
UINT8 nextacknum; UINT8 nextacknum;
UINT8 destinationnode; UINT8 destinationnode; // The node to send the ack to
tic_t senttime; tic_t senttime; // The time when the ack was sent
UINT16 length; UINT16 length; // The packet size
UINT16 resentnum; UINT16 resentnum; // The number of times the ack has been resent
union { union {
SINT8 raw[MAXPACKETLENGTH]; SINT8 raw[MAXPACKETLENGTH];
doomdata_t data; doomdata_t data;
@ -152,11 +152,12 @@ typedef struct
typedef enum typedef enum
{ {
CLOSE = 1, // flag is set when connection is closing NF_CLOSE = 1, // Flag is set when connection is closing
NF_TIMEOUT = 2, // Flag is set when the node got a timeout
} node_flags_t; } node_flags_t;
#ifndef NONET #ifndef NONET
// table of packet that was not acknowleged can be resend (the sender window) // Table of packets that were not acknowleged can be resent (the sender window)
static ackpak_t ackpak[MAXACKPACKETS]; static ackpak_t ackpak[MAXACKPACKETS];
#endif #endif
@ -212,11 +213,16 @@ FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b)
return d; return d;
} }
// return a free acknum and copy netbuffer in the ackpak table /** Sets freeack to a free acknum and copies the netbuffer in the ackpak table
*
* \param freeack The address to store the free acknum at
* \param lowtimer ???
* \return True if a free acknum was found
*/
static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
{ {
node_t *node = &nodes[doomcom->remotenode]; node_t *node = &nodes[doomcom->remotenode];
INT32 i, numfreeslote = 0; INT32 i, numfreeslot = 0;
if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0) if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0)
{ {
@ -227,10 +233,13 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
for (i = 0; i < MAXACKPACKETS; i++) for (i = 0; i < MAXACKPACKETS; i++)
if (!ackpak[i].acknum) if (!ackpak[i].acknum)
{ {
// for low priority packet, make sure let freeslotes so urgents packets can be sent // For low priority packets, make sure to let freeslots so urgent packets can be sent
numfreeslote++; if (netbuffer->packettype >= PT_CANFAIL)
if (netbuffer->packettype >= PT_CANFAIL && numfreeslote < URGENTFREESLOTENUM) {
continue; numfreeslot++;
if (numfreeslot <= URGENTFREESLOTNUM)
continue;
}
ackpak[i].acknum = node->nextacknum; ackpak[i].acknum = node->nextacknum;
ackpak[i].nextacknum = node->nextacknum; ackpak[i].nextacknum = node->nextacknum;
@ -241,7 +250,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
ackpak[i].length = doomcom->datalength; ackpak[i].length = doomcom->datalength;
if (lowtimer) if (lowtimer)
{ {
// lowtime mean can't be sent now so try it soon as possible // Lowtime means can't be sent now so try it as soon as possible
ackpak[i].senttime = 0; ackpak[i].senttime = 0;
ackpak[i].resentnum = 1; ackpak[i].resentnum = 1;
} }
@ -254,7 +263,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
*freeack = ackpak[i].acknum; *freeack = ackpak[i].acknum;
sendackpacket++; // for stat sendackpacket++; // For stat
return true; return true;
} }
@ -266,14 +275,46 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
return false; return false;
} }
// Get a ack to send in the queu of this node /** Counts how many acks are free
*
* \param urgent True if the type of the packet meant to
* use an ack is lower than PT_CANFAIL
* If for some reason you don't want use it
* for any packet type in particular,
* just set to false
* \return The number of free acks
*
*/
INT32 Net_GetFreeAcks(boolean urgent)
{
INT32 i, numfreeslot = 0;
INT32 n = 0; // Number of free acks found
for (i = 0; i < MAXACKPACKETS; i++)
if (!ackpak[i].acknum)
{
// For low priority packets, make sure to let freeslots so urgent packets can be sent
if (!urgent)
{
numfreeslot++;
if (numfreeslot <= URGENTFREESLOTNUM)
continue;
}
n++;
}
return n;
}
// Get a ack to send in the queue of this node
static UINT8 GetAcktosend(INT32 node) static UINT8 GetAcktosend(INT32 node)
{ {
nodes[node].lasttimeacktosend_sent = I_GetTime(); nodes[node].lasttimeacktosend_sent = I_GetTime();
return nodes[node].firstacktosend; return nodes[node].firstacktosend;
} }
static void Removeack(INT32 i) static void RemoveAck(INT32 i)
{ {
INT32 node = ackpak[i].destinationnode; INT32 node = ackpak[i].destinationnode;
#ifndef NEWPING #ifndef NEWPING
@ -290,31 +331,31 @@ static void Removeack(INT32 i)
DEBFILE(va("Remove ack %d\n",ackpak[i].acknum)); DEBFILE(va("Remove ack %d\n",ackpak[i].acknum));
#endif #endif
ackpak[i].acknum = 0; ackpak[i].acknum = 0;
if (nodes[node].flags & CLOSE) if (nodes[node].flags & NF_CLOSE)
Net_CloseConnection(node); Net_CloseConnection(node);
} }
// we have got a packet proceed the ack request and ack return // We have got a packet, proceed the ack request and ack return
static boolean Processackpak(void) static boolean Processackpak(void)
{ {
INT32 i; INT32 i;
boolean goodpacket = true; boolean goodpacket = true;
node_t *node = &nodes[doomcom->remotenode]; node_t *node = &nodes[doomcom->remotenode];
// received an ack return, so remove the ack in the list // Received an ack return, so remove the ack in the list
if (netbuffer->ackreturn && cmpack(node->remotefirstack, netbuffer->ackreturn) < 0) if (netbuffer->ackreturn && cmpack(node->remotefirstack, netbuffer->ackreturn) < 0)
{ {
node->remotefirstack = netbuffer->ackreturn; node->remotefirstack = netbuffer->ackreturn;
// search the ackbuffer and free it // Search the ackbuffer and free it
for (i = 0; i < MAXACKPACKETS; i++) for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes
&& cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0) && cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0)
{ {
Removeack(i); RemoveAck(i);
} }
} }
// received a packet with ack, queue it to send the ack back // Received a packet with ack, queue it to send the ack back
if (netbuffer->ack) if (netbuffer->ack)
{ {
UINT8 ack = netbuffer->ack; UINT8 ack = netbuffer->ack;
@ -323,23 +364,23 @@ static boolean Processackpak(void)
{ {
DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack)); DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack));
duppacket++; duppacket++;
goodpacket = false; // discard packet (duplicate) goodpacket = false; // Discard packet (duplicate)
} }
else else
{ {
// check if it is not already in the queue // Check if it is not already in the queue
for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND)
if (node->acktosend[i] == ack) if (node->acktosend[i] == ack)
{ {
DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack)); DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack));
duppacket++; duppacket++;
goodpacket = false; // discard packet (duplicate) goodpacket = false; // Discard packet (duplicate)
break; break;
} }
if (goodpacket) if (goodpacket)
{ {
// is a good packet so increment the acknowledge number, // Is a good packet so increment the acknowledge number,
// then search for a "hole" in the queue // Then search for a "hole" in the queue
UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1); UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1);
if (!nextfirstack) if (!nextfirstack)
nextfirstack = 1; nextfirstack = 1;
@ -383,10 +424,10 @@ static boolean Processackpak(void)
} }
} }
} }
else // out of order packet else // Out of order packet
{ {
// don't increment firsacktosend, put it in asktosend queue // Don't increment firsacktosend, put it in asktosend queue
// will be incremented when the nextfirstack comes (code above) // Will be incremented when the nextfirstack comes (code above)
UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND); UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND);
DEBFILE(va("out of order packet (%d expected)\n", nextfirstack)); DEBFILE(va("out of order packet (%d expected)\n", nextfirstack));
if (newhead != node->acktosend_tail) if (newhead != node->acktosend_tail)
@ -394,8 +435,8 @@ static boolean Processackpak(void)
node->acktosend[node->acktosend_head] = ack; node->acktosend[node->acktosend_head] = ack;
node->acktosend_head = newhead; node->acktosend_head = newhead;
} }
else // buffer full discard packet, sender will resend it else // Buffer full discard packet, sender will resend it
{ // we can admit the packet but we will not detect the duplication after :( { // We can admit the packet but we will not detect the duplication after :(
DEBFILE("no more freeackret\n"); DEBFILE("no more freeackret\n");
goodpacket = false; goodpacket = false;
} }
@ -430,25 +471,29 @@ static void GotAcks(void)
if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode) if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode)
{ {
if (ackpak[i].acknum == netbuffer->u.textcmd[j]) if (ackpak[i].acknum == netbuffer->u.textcmd[j])
Removeack(i); RemoveAck(i);
else // nextacknum is first equal to acknum, then when receiving bigger ack
// nextacknum is first equal to acknum, then when receiving bigger ack // there is big chance the packet is lost
// there is big chance the packet is lost // When resent, nextacknum = nodes[node].nextacknum
// when resent, nextacknum = nodes[node].nextacknum // will redo the same but with different value
// will redo the same but with different value else if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0 && ackpak[i].senttime > 0)
&& ackpak[i].senttime > 0) {
{ ackpak[i].senttime--; // hurry up
ackpak[i].senttime--; // hurry up }
}
} }
} }
#endif #endif
static inline void Net_ConnectionTimeout(INT32 node) void Net_ConnectionTimeout(INT32 node)
{ {
// send a very special packet to self (hack the reboundstore queue) // Don't timeout several times
// main code will handle it if (nodes[node].flags & NF_TIMEOUT)
return;
nodes[node].flags |= NF_TIMEOUT;
// Send a very special packet to self (hack the reboundstore queue)
// Main code will handle it
reboundstore[rebound_head].packettype = PT_NODETIMEOUT; reboundstore[rebound_head].packettype = PT_NODETIMEOUT;
reboundstore[rebound_head].ack = 0; reboundstore[rebound_head].ack = 0;
reboundstore[rebound_head].ackreturn = 0; reboundstore[rebound_head].ackreturn = 0;
@ -456,12 +501,12 @@ static inline void Net_ConnectionTimeout(INT32 node)
reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1); reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1);
rebound_head = (rebound_head+1) % MAXREBOUND; rebound_head = (rebound_head+1) % MAXREBOUND;
// do not redo it quickly (if we do not close connection it is // Do not redo it quickly (if we do not close connection it is
// for a good reason!) // for a good reason!)
nodes[node].lasttimepacketreceived = I_GetTime(); nodes[node].lasttimepacketreceived = I_GetTime();
} }
// resend the data if needed // Resend the data if needed
void Net_AckTicker(void) void Net_AckTicker(void)
{ {
#ifndef NONET #ifndef NONET
@ -477,7 +522,7 @@ void Net_AckTicker(void)
if (ackpak[i].acknum && ackpak[i].senttime + node->timeout < I_GetTime()) if (ackpak[i].acknum && ackpak[i].senttime + node->timeout < I_GetTime())
#endif #endif
{ {
if (ackpak[i].resentnum > 10 && (node->flags & CLOSE)) if (ackpak[i].resentnum > 10 && (node->flags & NF_CLOSE))
{ {
DEBFILE(va("ack %d sent 10 times so connection is supposed lost: node %d\n", DEBFILE(va("ack %d sent 10 times so connection is supposed lost: node %d\n",
i, nodei)); i, nodei));
@ -497,7 +542,7 @@ void Net_AckTicker(void)
ackpak[i].senttime = I_GetTime(); ackpak[i].senttime = I_GetTime();
ackpak[i].resentnum++; ackpak[i].resentnum++;
ackpak[i].nextacknum = node->nextacknum; ackpak[i].nextacknum = node->nextacknum;
retransmit++; // for stat retransmit++; // For stat
HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum, HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum,
(size_t)(ackpak[i].length - BASEPACKETSIZE)); (size_t)(ackpak[i].length - BASEPACKETSIZE));
} }
@ -505,15 +550,15 @@ void Net_AckTicker(void)
for (i = 1; i < MAXNETNODES; i++) for (i = 1; i < MAXNETNODES; i++)
{ {
// this is something like node open flag // This is something like node open flag
if (nodes[i].firstacktosend) if (nodes[i].firstacktosend)
{ {
// we haven't sent a packet for a long time // We haven't sent a packet for a long time
// acknowledge packet if needed // Acknowledge packet if needed
if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime()) if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime())
Net_SendAcks(i); Net_SendAcks(i);
if (!(nodes[i].flags & CLOSE) if (!(nodes[i].flags & NF_CLOSE)
&& nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime()) && nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime())
{ {
Net_ConnectionTimeout(i); Net_ConnectionTimeout(i);
@ -523,9 +568,9 @@ void Net_AckTicker(void)
#endif #endif
} }
// remove last packet received ack before resending the ackret // Remove last packet received ack before resending the ackreturn
// (the higher layer doesn't have room, or something else ....) // (the higher layer doesn't have room, or something else ....)
void Net_UnAcknowledgPacket(INT32 node) void Net_UnAcknowledgePacket(INT32 node)
{ {
#ifdef NONET #ifdef NONET
(void)node; (void)node;
@ -564,20 +609,29 @@ void Net_UnAcknowledgPacket(INT32 node)
#endif #endif
} }
boolean Net_AllAckReceived(void)
{
#ifndef NONET #ifndef NONET
/** Checks if all acks have been received
*
* \return True if all acks have been received
*
*/
static boolean Net_AllAcksReceived(void)
{
INT32 i; INT32 i;
for (i = 0; i < MAXACKPACKETS; i++) for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum) if (ackpak[i].acknum)
return false; return false;
#endif
return true; return true;
} }
#endif
// wait for all ackreturns with timeout in seconds /** Waits for all ackreturns
*
* \param timeout Timeout in seconds
*
*/
void Net_WaitAllAckReceived(UINT32 timeout) void Net_WaitAllAckReceived(UINT32 timeout)
{ {
#ifdef NONET #ifdef NONET
@ -587,7 +641,7 @@ void Net_WaitAllAckReceived(UINT32 timeout)
timeout = tictac + timeout*NEWTICRATE; timeout = tictac + timeout*NEWTICRATE;
HGetPacket(); HGetPacket();
while (timeout > I_GetTime() && !Net_AllAckReceived()) while (timeout > I_GetTime() && !Net_AllAcksReceived())
{ {
while (tictac == I_GetTime()) while (tictac == I_GetTime())
I_Sleep(); I_Sleep();
@ -598,18 +652,18 @@ void Net_WaitAllAckReceived(UINT32 timeout)
#endif #endif
} }
static void InitNode(INT32 node) static void InitNode(node_t *node)
{ {
nodes[node].acktosend_head = nodes[node].acktosend_tail = 0; node->acktosend_head = node->acktosend_tail = 0;
#ifndef NEWPING #ifndef NEWPING
nodes[node].ping = PINGDEFAULT; node->ping = PINGDEFAULT;
nodes[node].varping = VARPINGDEFAULT; node->varping = VARPINGDEFAULT;
nodes[node].timeout = TIMEOUT(nodes[node].ping,nodes[node].varping); node->timeout = TIMEOUT(node->ping, node->varping);
#endif #endif
nodes[node].firstacktosend = 0; node->firstacktosend = 0;
nodes[node].nextacknum = 1; node->nextacknum = 1;
nodes[node].remotefirstack = 0; node->remotefirstack = 0;
nodes[node].flags = 0; node->flags = 0;
} }
static void InitAck(void) static void InitAck(void)
@ -622,9 +676,14 @@ static void InitAck(void)
#endif #endif
for (i = 0; i < MAXNETNODES; i++) for (i = 0; i < MAXNETNODES; i++)
InitNode(i); InitNode(&nodes[i]);
} }
/** Removes all acks of a given packet type
*
* \param packettype The packet type to forget
*
*/
void Net_AbortPacketType(UINT8 packettype) void Net_AbortPacketType(UINT8 packettype)
{ {
#ifdef NONET #ifdef NONET
@ -657,7 +716,7 @@ void Net_CloseConnection(INT32 node)
if (!node) if (!node)
return; return;
nodes[node].flags |= CLOSE; nodes[node].flags |= NF_CLOSE;
// try to Send ack back (two army problem) // try to Send ack back (two army problem)
if (GetAcktosend(node)) if (GetAcktosend(node))
@ -676,8 +735,8 @@ void Net_CloseConnection(INT32 node)
ackpak[i].acknum = 0; ackpak[i].acknum = 0;
} }
InitNode(node); InitNode(&nodes[node]);
AbortSendFiles(node); SV_AbortSendFiles(node);
I_NetFreeNodenum(node); I_NetFreeNodenum(node);
#endif #endif
} }
@ -729,9 +788,15 @@ static void fprintfstring(char *s, size_t len)
} }
if (mode) if (mode)
fprintf(debugfile, "]"); fprintf(debugfile, "]");
}
static void fprintfstringnewline(char *s, size_t len)
{
fprintfstring(s, len);
fprintf(debugfile, "\n"); fprintf(debugfile, "\n");
} }
/// \warning Keep this up-to-date if you add/remove/rename packet types
static const char *packettypename[NUMPACKETTYPE] = static const char *packettypename[NUMPACKETTYPE] =
{ {
"NOTHING", "NOTHING",
@ -749,15 +814,22 @@ static const char *packettypename[NUMPACKETTYPE] =
"ASKINFO", "ASKINFO",
"SERVERINFO", "SERVERINFO",
"PLAYERINFO",
"REQUESTFILE", "REQUESTFILE",
"ASKINFOVIAMS", "ASKINFOVIAMS",
"PLAYERCONFIGS", "RESYNCHEND",
"RESYNCHGET",
"FILEFRAGMENT", "FILEFRAGMENT",
"TEXTCMD", "TEXTCMD",
"TEXTCMD2", "TEXTCMD2",
"CLIENTJOIN", "CLIENTJOIN",
"NODETIMEOUT", "NODETIMEOUT",
"RESYNCHING",
#ifdef NEWPING
"PING"
#endif
}; };
static void DebugPrintpacket(const char *header) static void DebugPrintpacket(const char *header)
@ -770,20 +842,31 @@ static void DebugPrintpacket(const char *header)
{ {
case PT_ASKINFO: case PT_ASKINFO:
case PT_ASKINFOVIAMS: case PT_ASKINFOVIAMS:
fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time) ); fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time));
break; break;
case PT_CLIENTJOIN: case PT_CLIENTJOIN:
fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers, fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers,
netbuffer->u.clientcfg.mode); netbuffer->u.clientcfg.mode);
break; break;
case PT_SERVERTICS: case PT_SERVERTICS:
{
servertics_pak *serverpak = &netbuffer->u.serverpak;
UINT8 *cmd = (UINT8 *)(&serverpak->cmds[serverpak->numslots * serverpak->numtics]);
size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - cmd;
fprintf(debugfile, " firsttic %u ply %d tics %d ntxtcmd %s\n ", fprintf(debugfile, " firsttic %u ply %d tics %d ntxtcmd %s\n ",
(UINT32)ExpandTics(netbuffer->u.serverpak.starttic), netbuffer->u.serverpak.numslots, (UINT32)ExpandTics(serverpak->starttic), serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd));
netbuffer->u.serverpak.numtics, /// \todo Display more readable information about net commands
sizeu1((size_t)(&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics]))); fprintfstringnewline((char *)cmd, ntxtcmd);
fprintfstring((char *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics],(size_t)( /*fprintfstring((char *)cmd, 3);
&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics])); if (ntxtcmd > 4)
{
fprintf(debugfile, "[%s]", netxcmdnames[*((cmd) + 3) - 1]);
fprintfstring(((char *)cmd) + 4, ntxtcmd - 4);
}
fprintf(debugfile, "\n");*/
break; break;
}
case PT_CLIENTCMD: case PT_CLIENTCMD:
case PT_CLIENT2CMD: case PT_CLIENT2CMD:
case PT_CLIENTMIS: case PT_CLIENTMIS:
@ -797,7 +880,8 @@ static void DebugPrintpacket(const char *header)
case PT_TEXTCMD: case PT_TEXTCMD:
case PT_TEXTCMD2: case PT_TEXTCMD2:
fprintf(debugfile, " length %d\n ", netbuffer->u.textcmd[0]); fprintf(debugfile, " length %d\n ", netbuffer->u.textcmd[0]);
fprintfstring((char *)netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); fprintf(debugfile, "[%s]", netxcmdnames[netbuffer->u.textcmd[1] - 1]);
fprintfstringnewline((char *)netbuffer->u.textcmd + 2, netbuffer->u.textcmd[0] - 1);
break; break;
case PT_SERVERCFG: case PT_SERVERCFG:
fprintf(debugfile, " playerslots %d clientnode %d serverplayer %d " fprintf(debugfile, " playerslots %d clientnode %d serverplayer %d "
@ -813,7 +897,7 @@ static void DebugPrintpacket(const char *header)
netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname, netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname,
netbuffer->u.serverinfo.fileneedednum, netbuffer->u.serverinfo.fileneedednum,
(UINT32)LONG(netbuffer->u.serverinfo.time)); (UINT32)LONG(netbuffer->u.serverinfo.time));
fprintfstring((char *)netbuffer->u.serverinfo.fileneeded, fprintfstringnewline((char *)netbuffer->u.serverinfo.fileneeded,
(UINT8)((UINT8 *)netbuffer + doomcom->datalength (UINT8)((UINT8 *)netbuffer + doomcom->datalength
- (UINT8 *)netbuffer->u.serverinfo.fileneeded)); - (UINT8 *)netbuffer->u.serverinfo.fileneeded));
break; break;
@ -827,20 +911,100 @@ static void DebugPrintpacket(const char *header)
break; break;
case PT_REQUESTFILE: case PT_REQUESTFILE:
default: // write as a raw packet default: // write as a raw packet
fprintfstring((char *)netbuffer->u.textcmd, fprintfstringnewline((char *)netbuffer->u.textcmd,
(UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.textcmd)); (UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.textcmd));
break; break;
} }
} }
#endif #endif
#ifdef PACKETDROP
static INT32 packetdropquantity[NUMPACKETTYPE] = {0};
static INT32 packetdroprate = 0;
void Command_Drop(void)
{
INT32 packetquantity;
const char *packetname;
size_t i;
if (COM_Argc() < 2)
{
CONS_Printf("drop <packettype> [quantity]: drop packets\n"
"drop reset: cancel all packet drops\n");
return;
}
if (!(stricmp(COM_Argv(1), "reset") && stricmp(COM_Argv(1), "cancel") && stricmp(COM_Argv(1), "stop")))
{
memset(packetdropquantity, 0, sizeof(packetdropquantity));
return;
}
if (COM_Argc() >= 3)
{
packetquantity = atoi(COM_Argv(2));
if (packetquantity <= 0 && COM_Argv(2)[0] != '0')
{
CONS_Printf("Invalid quantity\n");
return;
}
}
else
packetquantity = -1;
packetname = COM_Argv(1);
if (!(stricmp(packetname, "all") && stricmp(packetname, "any")))
for (i = 0; i < NUMPACKETTYPE; i++)
packetdropquantity[i] = packetquantity;
else
{
for (i = 0; i < NUMPACKETTYPE; i++)
if (!stricmp(packetname, packettypename[i]))
{
packetdropquantity[i] = packetquantity;
return;
}
CONS_Printf("Unknown packet name\n");
}
}
void Command_Droprate(void)
{
INT32 droprate;
if (COM_Argc() < 2)
{
CONS_Printf("Packet drop rate: %d%%\n", packetdroprate);
return;
}
droprate = atoi(COM_Argv(1));
if ((droprate <= 0 && COM_Argv(1)[0] != '0') || droprate > 100)
{
CONS_Printf("Packet drop rate must be between 0 and 100!\n");
return;
}
packetdroprate = droprate;
}
static boolean ShouldDropPacket(void)
{
return (packetdropquantity[netbuffer->packettype])
|| (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100;
}
#endif
// //
// HSendPacket // HSendPacket
// //
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength) boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength)
{ {
doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE); doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE);
if (node == 0) // packet is to go back to us if (node == 0) // Packet is to go back to us
{ {
if ((rebound_head+1) % MAXREBOUND == rebound_tail) if ((rebound_head+1) % MAXREBOUND == rebound_tail)
{ {
@ -871,7 +1035,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
(void)reliable; (void)reliable;
(void)acknum; (void)acknum;
#else #else
// do this before GetFreeAcknum because this function backup // do this before GetFreeAcknum because this function backups
// the current packet // the current packet
doomcom->remotenode = (INT16)node; doomcom->remotenode = (INT16)node;
if (doomcom->datalength <= 0) if (doomcom->datalength <= 0)
@ -884,7 +1048,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
return false; return false;
} }
if (node < MAXNETNODES) // can be a broadcast if (node < MAXNETNODES) // Can be a broadcast
netbuffer->ackreturn = GetAcktosend(node); netbuffer->ackreturn = GetAcktosend(node);
else else
netbuffer->ackreturn = 0; netbuffer->ackreturn = 0;
@ -905,20 +1069,30 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
netbuffer->ack = acknum; netbuffer->ack = acknum;
netbuffer->checksum = NetbufferChecksum(); netbuffer->checksum = NetbufferChecksum();
sendbytes += packetheaderlength + doomcom->datalength; // for stat sendbytes += packetheaderlength + doomcom->datalength; // For stat
// simulate internet :) #ifdef PACKETDROP
if (true || rand()<(INT32)RAND_MAX/5) // Simulate internet :)
//if (rand() >= (INT32)(RAND_MAX * (PACKETLOSSRATE / 100.f)))
if (!ShouldDropPacket())
{ {
#endif
#ifdef DEBUGFILE #ifdef DEBUGFILE
if (debugfile) if (debugfile)
DebugPrintpacket("SEND"); DebugPrintpacket("SENT");
#endif #endif
I_NetSend(); I_NetSend();
#ifdef PACKETDROP
} }
else
{
if (packetdropquantity[netbuffer->packettype] > 0)
packetdropquantity[netbuffer->packettype]--;
#ifdef DEBUGFILE #ifdef DEBUGFILE
else if (debugfile) if (debugfile)
DebugPrintpacket("NOTSEND"); DebugPrintpacket("NOT SENT");
#endif
}
#endif #endif
#endif // ndef NONET #endif // ndef NONET
@ -933,7 +1107,9 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
// //
boolean HGetPacket(void) boolean HGetPacket(void)
{ {
// get a packet from self //boolean nodejustjoined;
// Get a packet from self
if (rebound_tail != rebound_head) if (rebound_tail != rebound_head)
{ {
M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]); M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]);
@ -958,16 +1134,17 @@ boolean HGetPacket(void)
while(true) while(true)
{ {
//nodejustjoined = I_NetGet();
I_NetGet(); I_NetGet();
if (doomcom->remotenode == -1) if (doomcom->remotenode == -1) // No packet received
return false; return false;
getbytes += packetheaderlength + doomcom->datalength; // for stat getbytes += packetheaderlength + doomcom->datalength; // For stat
if (doomcom->remotenode >= MAXNETNODES) if (doomcom->remotenode >= MAXNETNODES)
{ {
DEBFILE(va("receive packet from node %d !\n", doomcom->remotenode)); DEBFILE(va("Received packet from node %d!\n", doomcom->remotenode));
continue; continue;
} }
@ -976,6 +1153,7 @@ boolean HGetPacket(void)
if (netbuffer->checksum != NetbufferChecksum()) if (netbuffer->checksum != NetbufferChecksum())
{ {
DEBFILE("Bad packet checksum\n"); DEBFILE("Bad packet checksum\n");
//Net_CloseConnection(nodejustjoined ? (doomcom->remotenode | FORCECLOSE) : doomcom->remotenode);
Net_CloseConnection(doomcom->remotenode); Net_CloseConnection(doomcom->remotenode);
continue; continue;
} }
@ -985,11 +1163,26 @@ boolean HGetPacket(void)
DebugPrintpacket("GET"); DebugPrintpacket("GET");
#endif #endif
// proceed the ack and ackreturn field /*// If a new node sends an unexpected packet, just ignore it
if (nodejustjoined && server
&& !(netbuffer->packettype == PT_ASKINFO
|| netbuffer->packettype == PT_SERVERINFO
|| netbuffer->packettype == PT_PLAYERINFO
|| netbuffer->packettype == PT_REQUESTFILE
|| netbuffer->packettype == PT_ASKINFOVIAMS
|| netbuffer->packettype == PT_CLIENTJOIN))
{
DEBFILE(va("New node sent an unexpected %s packet\n", packettypename[netbuffer->packettype]));
//CONS_Alert(CONS_NOTICE, "New node sent an unexpected %s packet\n", packettypename[netbuffer->packettype]);
Net_CloseConnection(doomcom->remotenode | FORCECLOSE);
continue;
}*/
// Proceed the ack and ackreturn field
if (!Processackpak()) if (!Processackpak())
continue; // discarded (duplicated) continue; // discarded (duplicated)
// a packet with just ackreturn // A packet with just ackreturn
if (netbuffer->packettype == PT_NOTHING) if (netbuffer->packettype == PT_NOTHING)
{ {
GotAcks(); GotAcks();
@ -1002,9 +1195,10 @@ boolean HGetPacket(void)
return true; return true;
} }
static void Internal_Get(void) static boolean Internal_Get(void)
{ {
doomcom->remotenode = -1; doomcom->remotenode = -1;
return false;
} }
FUNCNORETURN static ATTRNORETURN void Internal_Send(void) FUNCNORETURN static ATTRNORETURN void Internal_Send(void)
@ -1089,7 +1283,7 @@ boolean D_CheckNetGame(void)
if (netgame) if (netgame)
ret = true; ret = true;
if (!server && netgame) if (client && netgame)
netgame = false; netgame = false;
server = true; // WTF? server always true??? server = true; // WTF? server always true???
// no! The deault mode is server. Client is set elsewhere // no! The deault mode is server. Client is set elsewhere
@ -1230,4 +1424,6 @@ void D_CloseConnection(void)
netgame = false; netgame = false;
addedtogame = false; addedtogame = false;
} }
D_ResetTiccmds();
} }

View file

@ -18,10 +18,10 @@
#ifndef __D_NET__ #ifndef __D_NET__
#define __D_NET__ #define __D_NET__
// Max computers in a game. // Max computers in a game
#define MAXNETNODES 32 #define MAXNETNODES 32
#define BROADCASTADDR MAXNETNODES #define BROADCASTADDR MAXNETNODES
#define MAXSPLITSCREENPLAYERS 2 // max number of players on a single computer #define MAXSPLITSCREENPLAYERS 2 // Max number of players on a single computer
#define STATLENGTH (TICRATE*2) #define STATLENGTH (TICRATE*2)
@ -32,17 +32,17 @@ extern float lostpercent, duppercent, gamelostpercent;
extern INT32 packetheaderlength; extern INT32 packetheaderlength;
boolean Net_GetNetStat(void); boolean Net_GetNetStat(void);
extern INT32 getbytes; extern INT32 getbytes;
extern INT64 sendbytes; // realtime updated extern INT64 sendbytes; // Realtime updated
extern SINT8 nodetoplayer[MAXNETNODES]; extern SINT8 nodetoplayer[MAXNETNODES];
extern SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen)
extern UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen
extern boolean nodeingame[MAXNETNODES]; // set false as nodes leave game extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game
INT32 Net_GetFreeAcks(boolean urgent);
void Net_AckTicker(void); void Net_AckTicker(void);
boolean Net_AllAckReceived(void);
// if reliable return true if packet sent, 0 else // If reliable return true if packet sent, 0 else
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum,
size_t packetlength); size_t packetlength);
boolean HGetPacket(void); boolean HGetPacket(void);
@ -52,9 +52,11 @@ void D_SaveBan(void);
#endif #endif
boolean D_CheckNetGame(void); boolean D_CheckNetGame(void);
void D_CloseConnection(void); void D_CloseConnection(void);
void Net_UnAcknowledgPacket(INT32 node); void Net_UnAcknowledgePacket(INT32 node);
void Net_CloseConnection(INT32 node); void Net_CloseConnection(INT32 node);
void Net_ConnectionTimeout(INT32 node);
void Net_AbortPacketType(UINT8 packettype); void Net_AbortPacketType(UINT8 packettype);
void Net_SendAcks(INT32 node); void Net_SendAcks(INT32 node);
void Net_WaitAllAckReceived(UINT32 timeout); void Net_WaitAllAckReceived(UINT32 timeout);
#endif #endif

View file

@ -82,6 +82,7 @@ static void AutoBalance_OnChange(void);
static void TeamScramble_OnChange(void); static void TeamScramble_OnChange(void);
static void NetTimeout_OnChange(void); static void NetTimeout_OnChange(void);
static void JoinTimeout_OnChange(void);
static void Ringslinger_OnChange(void); static void Ringslinger_OnChange(void);
static void Gravity_OnChange(void); static void Gravity_OnChange(void);
@ -340,7 +341,9 @@ consvar_t cv_killingdead = {"killingdead", "Off", CV_NETVAR, CV_OnOff, NULL, 0,
consvar_t cv_netstat = {"netstat", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // show bandwidth statistics consvar_t cv_netstat = {"netstat", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // show bandwidth statistics
static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
consvar_t cv_nettimeout = {"nettimeout", "525", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_nettimeout = {"nettimeout", "350", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
consvar_t cv_jointimeout = {"jointimeout", "350", CV_CALL|CV_SAVE, jointimeout_cons_t, JoinTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
#ifdef NEWPING #ifdef NEWPING
consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif #endif
@ -365,6 +368,35 @@ boolean splitscreen = false;
boolean circuitmap = false; boolean circuitmap = false;
INT32 adminplayer = -1; INT32 adminplayer = -1;
/// \warning Keep this up-to-date if you add/remove/rename net text commands
const char *netxcmdnames[MAXNETXCMD - 1] =
{
"NAMEANDCOLOR",
"WEAPONPREF",
"KICK",
"NETVAR",
"SAY",
"MAP",
"EXITLEVEL",
"ADDFILE",
"PAUSE",
"ADDPLAYER",
"TEAMCHANGE",
"CLEARSCORES",
"LOGIN",
"VERIFIED",
"RANDOMSEED",
"RUNSOC",
"REQADDFILE",
"DELFILE",
"SETMOTD",
"SUICIDE",
#ifdef HAVE_BLUA
"LUACMD",
"LUAVAR"
#endif
};
// ========================================================================= // =========================================================================
// SERVER STARTUP // SERVER STARTUP
// ========================================================================= // =========================================================================
@ -517,9 +549,12 @@ void D_RegisterServerCommands(void)
// d_clisrv // d_clisrv
CV_RegisterVar(&cv_maxplayers); CV_RegisterVar(&cv_maxplayers);
CV_RegisterVar(&cv_maxsend); CV_RegisterVar(&cv_maxsend);
CV_RegisterVar(&cv_noticedownload);
CV_RegisterVar(&cv_downloadspeed);
COM_AddCommand("ping", Command_Ping_f); COM_AddCommand("ping", Command_Ping_f);
CV_RegisterVar(&cv_nettimeout); CV_RegisterVar(&cv_nettimeout);
CV_RegisterVar(&cv_jointimeout);
CV_RegisterVar(&cv_skipmapcheck); CV_RegisterVar(&cv_skipmapcheck);
CV_RegisterVar(&cv_sleep); CV_RegisterVar(&cv_sleep);
@ -976,7 +1011,7 @@ UINT8 CanChangeSkin(INT32 playernum)
return true; return true;
// Force skin in effect. // Force skin in effect.
if (!server && (cv_forceskin.value != -1) && !(adminplayer == playernum && serverplayer == -1)) if (client && (cv_forceskin.value != -1) && !(adminplayer == playernum && serverplayer == -1))
return false; return false;
// Can change skin in intermission and whatnot. // Can change skin in intermission and whatnot.
@ -1587,7 +1622,7 @@ static void Command_Map_f(void)
return; return;
} }
if (!server && !(adminplayer == consoleplayer)) if (client && !(adminplayer == consoleplayer))
{ {
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return; return;
@ -1914,7 +1949,7 @@ static void Got_Suicide(UINT8 **cp, INT32 playernum)
// You can't suicide someone else. Nice try, there. // You can't suicide someone else. Nice try, there.
if (suicideplayer != playernum || (!G_PlatformGametype())) if (suicideplayer != playernum || (!G_PlatformGametype()))
{ {
CONS_Alert(CONS_WARNING, M_GetText("Illegal suicide command recieved from %s\n"), player_names[playernum]); CONS_Alert(CONS_WARNING, M_GetText("Illegal suicide command received from %s\n"), player_names[playernum]);
if (server) if (server)
{ {
XBOXSTATIC UINT8 buf[2]; XBOXSTATIC UINT8 buf[2];
@ -2574,7 +2609,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
if (players[playernum].spectator) if (players[playernum].spectator)
{ {
players[playernum].score = 0; players[playernum].score = 0;
players[playernum].health = 1; players[playernum].rings = 0;
if (players[playernum].mo) if (players[playernum].mo)
players[playernum].mo->health = 1; players[playernum].mo->health = 1;
} }
@ -2629,7 +2664,7 @@ static void Command_Changepassword_f(void)
// If we have no MD5 support then completely disable XD_LOGIN responses for security. // If we have no MD5 support then completely disable XD_LOGIN responses for security.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n"); CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else #else
if (!server) // cannot change remotely if (client) // cannot change remotely
{ {
CONS_Printf(M_GetText("Only the server can use this.\n")); CONS_Printf(M_GetText("Only the server can use this.\n"));
return; return;
@ -2688,7 +2723,7 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
READMEM(*cp, sentmd5, 16); READMEM(*cp, sentmd5, 16);
if (!server) if (client)
return; return;
// Do the final pass to compare with the sent md5 // Do the final pass to compare with the sent md5
@ -2710,7 +2745,7 @@ static void Command_Verify_f(void)
char *temp; char *temp;
INT32 playernum; INT32 playernum;
if (!server) if (client)
{ {
CONS_Printf(M_GetText("Only the server can use this.\n")); CONS_Printf(M_GetText("Only the server can use this.\n"));
return; return;
@ -2794,7 +2829,7 @@ static void Command_MotD_f(void)
return; return;
} }
if ((netgame || multiplayer) && !server) if ((netgame || multiplayer) && client)
SendNetXCmd(XD_SETMOTD, mymotd, sizeof(motd)); SendNetXCmd(XD_SETMOTD, mymotd, sizeof(motd));
else else
{ {
@ -3051,7 +3086,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
READMEM(*cp, md5sum, 16); READMEM(*cp, md5sum, 16);
// Only the server processes this message. // Only the server processes this message.
if (!server) if (client)
return; return;
// Disallow non-printing characters and semicolons. // Disallow non-printing characters and semicolons.
@ -3318,6 +3353,11 @@ static void NetTimeout_OnChange(void)
connectiontimeout = (tic_t)cv_nettimeout.value; connectiontimeout = (tic_t)cv_nettimeout.value;
} }
static void JoinTimeout_OnChange(void)
{
jointimeout = (tic_t)cv_jointimeout.value;
}
UINT32 timelimitintics = 0; UINT32 timelimitintics = 0;
/** Deals with a timelimit change by printing the change to the console. /** Deals with a timelimit change by printing the change to the console.
@ -3960,7 +4000,7 @@ static void Command_Archivetest_f(void)
} }
// assign mobjnum // assign mobjnum
i = 0; i = 1;
for (th = thinkercap.next; th != &thinkercap; th = th->next) for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker) if (th->function.acp1 == (actionf_p1)P_MobjThinker)
((mobj_t *)th)->mobjnum = i++; ((mobj_t *)th)->mobjnum = i++;
@ -4055,8 +4095,7 @@ static void Skin_OnChange(void)
if (!Playing()) if (!Playing())
return; // do whatever you want return; // do whatever you want
if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player. if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player.
&& (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CONTINUING))
{ {
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name); CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return; return;

View file

@ -101,7 +101,6 @@ extern consvar_t cv_recycler;
extern consvar_t cv_itemfinder; extern consvar_t cv_itemfinder;
extern consvar_t cv_inttime, cv_advancemap, cv_playersforexit; extern consvar_t cv_inttime, cv_advancemap, cv_playersforexit;
extern consvar_t cv_soniccd;
extern consvar_t cv_match_scoring; extern consvar_t cv_match_scoring;
extern consvar_t cv_overtime; extern consvar_t cv_overtime;
extern consvar_t cv_startinglives; extern consvar_t cv_startinglives;
@ -162,6 +161,8 @@ typedef enum
MAXNETXCMD MAXNETXCMD
} netxcmd_t; } netxcmd_t;
extern const char *netxcmdnames[MAXNETXCMD - 1];
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma pack(1) #pragma pack(1)
#endif #endif

View file

@ -62,44 +62,48 @@
#include <errno.h> #include <errno.h>
static void SendFile(INT32 node, const char *filename, UINT8 fileid); static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid);
// sender structure // Sender structure
typedef struct filetx_s typedef struct filetx_s
{ {
INT32 ram; INT32 ram;
char *filename; // name of the file or ptr of the data in ram union {
UINT32 size; char *filename; // Name of the file
char *ram; // Pointer to the data in RAM
} id;
UINT32 size; // Size of the file
UINT8 fileid; UINT8 fileid;
INT32 node; // destination INT32 node; // Destination
struct filetx_s *next; // a queue struct filetx_s *next; // Next file in the list
} filetx_t; } filetx_t;
// current transfers (one for each node) // Current transfers (one for each node)
typedef struct filetran_s typedef struct filetran_s
{ {
filetx_t *txlist; filetx_t *txlist; // Linked list of all files for the node
UINT32 position; UINT32 position; // The current position in the file
FILE *currentfile; FILE *currentfile; // The file currently being sent/received
} filetran_t; } filetran_t;
static filetran_t transfer[MAXNETNODES]; static filetran_t transfer[MAXNETNODES];
// read time of file: stat _stmtime // Read time of file: stat _stmtime
// write time of file: utime // Write time of file: utime
// receiver structure // Receiver structure
INT32 fileneedednum; INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t fileneeded[MAX_WADFILES]; fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files
char downloaddir[256] = "DOWNLOAD"; char downloaddir[256] = "DOWNLOAD";
#ifdef CLIENT_LOADINGSCREEN #ifdef CLIENT_LOADINGSCREEN
// for cl loading screen // for cl loading screen
INT32 lastfilenum = 0; INT32 lastfilenum = -1;
#endif #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.
*
*/ */
UINT8 *PutFileNeeded(void) UINT8 *PutFileNeeded(void)
{ {
@ -111,19 +115,19 @@ UINT8 *PutFileNeeded(void)
for (i = 0; i < numwadfiles; i++) for (i = 0; i < numwadfiles; i++)
{ {
// if it has only music/sound lumps, mark it as unimportant // If it has only music/sound lumps, mark it as unimportant
if (W_VerifyNMUSlumps(wadfiles[i]->filename)) if (W_VerifyNMUSlumps(wadfiles[i]->filename))
filestatus = 0; filestatus = 0;
else else
filestatus = 1; // important filestatus = 1; // Important
// Store in the upper four bits // Store in the upper four bits
if (!cv_downloading.value) if (!cv_downloading.value)
filestatus += (2 << 4); // won't send filestatus += (2 << 4); // Won't send
else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)) else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024))
filestatus += (0 << 4); // won't send filestatus += (0 << 4); // Won't send
else else
filestatus += (1 << 4); // will send if requested filestatus += (1 << 4); // Will send if requested
bytesused += (nameonlylength(wadfilename) + 22); bytesused += (nameonlylength(wadfilename) + 22);
@ -144,7 +148,12 @@ UINT8 *PutFileNeeded(void)
return p; return p;
} }
// parse the serverinfo packet and fill fileneeded table on client /** Parses the serverinfo packet and fills the fileneeded table on client
*
* \param fileneedednum_parm The number of files needed to join the server
* \param fileneededstr The memory block containing the list of needed files
*
*/
void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr) void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
{ {
INT32 i; INT32 i;
@ -155,14 +164,14 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
p = (UINT8 *)fileneededstr; p = (UINT8 *)fileneededstr;
for (i = 0; i < fileneedednum; i++) for (i = 0; i < fileneedednum; i++)
{ {
fileneeded[i].status = FS_NOTFOUND; fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet
filestatus = READUINT8(p); filestatus = READUINT8(p); // The first byte is the file status
fileneeded[i].important = (UINT8)(filestatus & 3); fileneeded[i].important = (UINT8)(filestatus & 3);
fileneeded[i].willsend = (UINT8)(filestatus >> 4); fileneeded[i].willsend = (UINT8)(filestatus >> 4);
fileneeded[i].totalsize = READUINT32(p); fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size
fileneeded[i].phandle = NULL; fileneeded[i].file = NULL; // The file isn't open yet
READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name
READMEM(p, fileneeded[i].md5sum, 16); READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum
} }
} }
@ -171,13 +180,16 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave)
fileneedednum = 1; fileneedednum = 1;
fileneeded[0].status = FS_REQUESTED; fileneeded[0].status = FS_REQUESTED;
fileneeded[0].totalsize = UINT32_MAX; fileneeded[0].totalsize = UINT32_MAX;
fileneeded[0].phandle = 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);
} }
/** Checks the server to see if we CAN download all the files, /** Checks the server to see if we CAN download all the files,
* before starting to create them and requesting. * before starting to create them and requesting.
*
* \return True if we can download all the files
*
*/ */
boolean CL_CheckDownloadable(void) boolean CL_CheckDownloadable(void)
{ {
@ -239,8 +251,12 @@ boolean CL_CheckDownloadable(void)
return false; return false;
} }
/** Send requests for files in the ::fileneeded table with a status of /** Sends requests for files in the ::fileneeded table with a status of
* ::FS_NOTFOUND. * ::FS_NOTFOUND.
*
* \return True if the packet was successfully sent
* \note Sends a PT_REQUESTFILE packet
*
*/ */
boolean CL_SendRequestFile(void) boolean CL_SendRequestFile(void)
{ {
@ -298,11 +314,17 @@ void Got_RequestFilePak(INT32 node)
if (id == 0xFF) if (id == 0xFF)
break; break;
READSTRINGN(p, wad, MAX_WADPATH); READSTRINGN(p, wad, MAX_WADPATH);
SendFile(node, wad, id); SV_SendFile(node, wad, id);
} }
} }
// client check if the fileneeded aren't already loaded or on the disk /** Checks if the files needed aren't already loaded or on the disk
*
* \return 0 if some files are missing
* 1 if all files exist
* 2 if some already loaded files are not requested or are in a different order
*
*/
INT32 CL_CheckFiles(void) INT32 CL_CheckFiles(void)
{ {
INT32 i, j; INT32 i, j;
@ -333,7 +355,7 @@ INT32 CL_CheckFiles(void)
} }
if (j < numwadfiles && W_VerifyNMUSlumps(wadfiles[j]->filename)) if (j < numwadfiles && W_VerifyNMUSlumps(wadfiles[j]->filename))
{ {
// unimportant on our side. still don't care. // Unimportant on our side. still don't care.
++j; ++j;
continue; continue;
} }
@ -343,11 +365,11 @@ INT32 CL_CheckFiles(void)
if (i >= fileneedednum || j >= numwadfiles) if (i >= fileneedednum || j >= numwadfiles)
return 2; return 2;
// for the sake of speed, only bother with a md5 check // For the sake of speed, only bother with a md5 check
if (memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16)) if (memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16))
return 2; return 2;
// it's accounted for! let's keep going. // It's accounted for! let's keep going.
CONS_Debug(DBG_NETPLAY, "'%s' accounted for\n", fileneeded[i].filename); CONS_Debug(DBG_NETPLAY, "'%s' accounted for\n", fileneeded[i].filename);
fileneeded[i].status = FS_OPEN; fileneeded[i].status = FS_OPEN;
++i; ++i;
@ -360,7 +382,7 @@ INT32 CL_CheckFiles(void)
{ {
CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename); CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);
// check in allready loaded files // Check in already loaded files
for (j = 1; wadfiles[j]; j++) for (j = 1; wadfiles[j]; j++)
{ {
nameonly(strcpy(wadfilename, wadfiles[j]->filename)); nameonly(strcpy(wadfilename, wadfiles[j]->filename));
@ -383,7 +405,7 @@ INT32 CL_CheckFiles(void)
return ret; return ret;
} }
// load it now // Load it now
void CL_LoadServerFiles(void) void CL_LoadServerFiles(void)
{ {
INT32 i; INT32 i;
@ -394,7 +416,7 @@ void CL_LoadServerFiles(void)
for (i = 1; i < fileneedednum; i++) for (i = 1; i < fileneedednum; i++)
{ {
if (fileneeded[i].status == FS_OPEN) if (fileneeded[i].status == FS_OPEN)
continue; // already loaded continue; // Already loaded
else if (fileneeded[i].status == FS_FOUND) else if (fileneeded[i].status == FS_FOUND)
{ {
P_AddWadFile(fileneeded[i].filename, NULL); P_AddWadFile(fileneeded[i].filename, NULL);
@ -423,172 +445,269 @@ void CL_LoadServerFiles(void)
DEBFILE(va("File %s found but with different md5sum\n", fileneeded[i].filename)); DEBFILE(va("File %s found but with different md5sum\n", fileneeded[i].filename));
} }
else if (fileneeded[i].important) else if (fileneeded[i].important)
I_Error("Try to load file %s with status of %d\n", fileneeded[i].filename, {
fileneeded[i].status); const char *s;
switch(fileneeded[i].status)
{
case FS_NOTFOUND:
s = "FS_NOTFOUND";
break;
case FS_REQUESTED:
s = "FS_REQUESTED";
break;
case FS_DOWNLOADING:
s = "FS_DOWNLOADING";
break;
default:
s = "unknown";
break;
}
I_Error("Try to load file \"%s\" with status of %d (%s)\n", fileneeded[i].filename,
fileneeded[i].status, s);
}
} }
} }
// little optimization to test if there is a file in the queue // Number of files to send
static INT32 filetosend = 0; // Little optimization to quickly test if there is a file in the queue
static INT32 filestosend = 0;
static void SendFile(INT32 node, const char *filename, UINT8 fileid) /** Adds a file to the file list for a node
*
* \param node The node to send the file to
* \param filename The file to send
* \param fileid ???
* \sa SV_SendRam
*
*/
static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
{ {
filetx_t **q; filetx_t **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; filetx_t *p; // The new file request
INT32 i; INT32 i;
char wadfilename[MAX_WADPATH]; char wadfilename[MAX_WADPATH];
if (cv_noticedownload.value)
CONS_Printf("Sending file \"%s\" to node %d\n", filename, node);
// Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist; q = &transfer[node].txlist;
while (*q) while (*q)
q = &((*q)->next); q = &((*q)->next);
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t)); p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (p) if (!p)
memset(p, 0, sizeof (filetx_t)); I_Error("SV_SendFile: No more memory\n");
else
I_Error("SendFile: No more ram\n");
p->filename = (char *)malloc(MAX_WADPATH);
if (!p->filename)
I_Error("SendFile: No more ram\n");
// a minimum of security, can get only file in srb2 direcory // Initialise with zeros
strlcpy(p->filename, filename, MAX_WADPATH); memset(p, 0, sizeof (filetx_t));
nameonly(p->filename);
// check first in wads loaded the majority of case // Allocate the file name
p->id.filename = (char *)malloc(MAX_WADPATH);
if (!p->id.filename)
I_Error("SV_SendFile: 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);
// Look for the requested file through all loaded files
for (i = 0; wadfiles[i]; i++) for (i = 0; wadfiles[i]; i++)
{ {
strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH); strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH);
nameonly(wadfilename); nameonly(wadfilename);
if (!stricmp(wadfilename, p->filename)) if (!stricmp(wadfilename, p->id.filename))
{ {
// copy filename with full path // Copy file name with full path
strlcpy(p->filename, wadfiles[i]->filename, MAX_WADPATH); strlcpy(p->id.filename, wadfiles[i]->filename, MAX_WADPATH);
break; break;
} }
} }
// Handle non-loaded file requests
if (!wadfiles[i]) if (!wadfiles[i])
{ {
DEBFILE(va("%s not found in wadfiles\n", filename)); DEBFILE(va("%s not found in wadfiles\n", filename));
// this formerly checked if (!findfile(p->filename, NULL, true)) // This formerly checked if (!findfile(p->id.filename, NULL, true))
// not found // Not found
// don't inform client (probably hacker) // Don't inform client (probably someone who thought they could leak 2.2 ACZ)
DEBFILE(va("Client %d request %s: not found\n", node, filename)); DEBFILE(va("Client %d request %s: not found\n", node, filename));
free(p->filename); free(p->id.filename);
free(p); free(p);
*q = NULL; *q = NULL;
return; return;
} }
// Handle huge file requests (i.e. bigger than cv_maxsend.value KB)
if (wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024) if (wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)
{ {
// too big // Too big
// don't inform client (client sucks, man) // Don't inform client (client sucks, man)
DEBFILE(va("Client %d request %s: file too big, not sending\n", node, filename)); DEBFILE(va("Client %d request %s: file too big, not sending\n", node, filename));
free(p->filename); free(p->id.filename);
free(p); free(p);
*q = NULL; *q = NULL;
return; return;
} }
DEBFILE(va("Sending file %s (id=%d) to %d\n", filename, fileid, node)); DEBFILE(va("Sending file %s (id=%d) to %d\n", filename, fileid, node));
p->ram = SF_FILE; p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it
p->fileid = fileid; p->fileid = fileid;
p->next = NULL; // end of list p->next = NULL; // End of list
filetosend++; filestosend++;
} }
void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid) /** Adds a memory block to the file list for a node
*
* \param node The node to send the memory block to
* \param data The memory block to send
* \param size The size of the block in bytes
* \param freemethod How to free the block after it has been sent
* \param fileid ???
* \sa SV_SendFile
*
*/
void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
{ {
filetx_t **q; filetx_t **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; filetx_t *p; // The new file request
// Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist; q = &transfer[node].txlist;
while (*q) while (*q)
q = &((*q)->next); q = &((*q)->next);
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t)); p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (p) if (!p)
memset(p, 0, sizeof (filetx_t)); I_Error("SV_SendRam: No more memory\n");
else
I_Error("SendRam: No more ram\n"); // Initialise with zeros
p->ram = freemethod; memset(p, 0, sizeof (filetx_t));
p->filename = data;
p->ram = freemethod; // Remember how to free the memory block for when we're done sending it
p->id.ram = data;
p->size = (UINT32)size; p->size = (UINT32)size;
p->fileid = fileid; p->fileid = fileid;
p->next = NULL; // end of list p->next = NULL; // End of list
DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->filename,p->size,node,fileid)); DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->id.ram,p->size,node,fileid));
filetosend++; filestosend++;
} }
static void EndSend(INT32 node) /** 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
*
* \param node The destination
*
*/
static void SV_EndFileSend(INT32 node)
{ {
filetx_t *p = transfer[node].txlist; filetx_t *p = transfer[node].txlist;
// Free the file request according to the freemethod parameter used with SV_SendFile/Ram
switch (p->ram) switch (p->ram)
{ {
case SF_FILE: case SF_FILE: // It's a file, close it and free its filename
if (cv_noticedownload.value)
CONS_Printf("Ending file transfer for node %d\n", node);
if (transfer[node].currentfile) if (transfer[node].currentfile)
fclose(transfer[node].currentfile); fclose(transfer[node].currentfile);
free(p->filename); free(p->id.filename);
break; break;
case SF_Z_RAM: case SF_Z_RAM: // It's a memory block allocated with Z_Alloc or the likes, use Z_Free
Z_Free(p->filename); Z_Free(p->id.ram);
break; break;
case SF_RAM: case SF_RAM: // It's a memory block allocated with malloc, use free
free(p->filename); free(p->id.ram);
case SF_NOFREERAM: case SF_NOFREERAM: // Nothing to free
break; break;
} }
// Remove the file request from the list
transfer[node].txlist = p->next; transfer[node].txlist = p->next;
transfer[node].currentfile = NULL;
free(p); free(p);
filetosend--;
// Indicate that the transmission is over
transfer[node].currentfile = NULL;
filestosend--;
} }
#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH) #define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
void FiletxTicker(void) /** Handles file transmission
*
* \todo Use an acknowledging method more adapted to file transmission
* The current download speed suffers from lack of ack packets,
* especially when the one downloading has high latency
*
*/
void SV_FileSendTicker(void)
{ {
static INT32 currentnode = 0; static INT32 currentnode = 0;
filetx_pak *p; filetx_pak *p;
size_t size; size_t size;
filetx_t *f; filetx_t *f;
INT32 packetsent = PACKETPERTIC, ram, i; INT32 packetsent, ram, i, j;
INT32 maxpacketsent;
if (!filetosend) if (!filestosend) // No file to send
return; return;
if (!packetsent)
packetsent++; if (cv_downloadspeed.value) // New (and experimental) behavior
// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
while (packetsent-- && filetosend != 0)
{ {
for (i = currentnode, ram = 0; ram < MAXNETNODES; packetsent = cv_downloadspeed.value;
i = (i+1) % MAXNETNODES, ram++) // Don't send more packets than we have free acks
#ifndef NONET
maxpacketsent = Net_GetFreeAcks(false) - 5; // Let 5 extra acks just in case
#else
maxpacketsent = 1;
#endif
if (packetsent > maxpacketsent && maxpacketsent > 0) // Send at least one packet
packetsent = maxpacketsent;
}
else // Old behavior
{
packetsent = PACKETPERTIC;
if (!packetsent)
packetsent = 1;
}
netbuffer->packettype = PT_FILEFRAGMENT;
// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
while (packetsent-- && filestosend != 0)
{
for (i = currentnode, j = 0; j < MAXNETNODES;
i = (i+1) % MAXNETNODES, j++)
{ {
if (transfer[i].txlist) if (transfer[i].txlist)
goto found; goto found;
} }
// no transfer to do // no transfer to do
I_Error("filetosend=%d but no filetosend found\n", filetosend); I_Error("filestosend=%d but no file to send found\n", filestosend);
found: found:
currentnode = (i+1) % MAXNETNODES; currentnode = (i+1) % MAXNETNODES;
f = transfer[i].txlist; f = transfer[i].txlist;
ram = f->ram; ram = f->ram;
if (!transfer[i].currentfile) // file not already open // Open the file if it isn't open yet, or
if (!transfer[i].currentfile)
{ {
if (!ram) if (!ram) // Sending a file
{ {
long filesize; long filesize;
transfer[i].currentfile = transfer[i].currentfile =
fopen(f->filename, "rb"); fopen(f->id.filename, "rb");
if (!transfer[i].currentfile) if (!transfer[i].currentfile)
I_Error("File %s does not exist", I_Error("File %s does not exist",
f->filename); f->id.filename);
fseek(transfer[i].currentfile, 0, SEEK_END); fseek(transfer[i].currentfile, 0, SEEK_END);
filesize = ftell(transfer[i].currentfile); filesize = ftell(transfer[i].currentfile);
@ -596,45 +715,47 @@ void FiletxTicker(void)
// Nobody wants to transfer a file bigger // Nobody wants to transfer a file bigger
// than 4GB! // than 4GB!
if (filesize >= LONG_MAX) if (filesize >= LONG_MAX)
I_Error("filesize of %s is too large", f->filename); I_Error("filesize of %s is too large", f->id.filename);
if (-1 == filesize) if (filesize == -1)
I_Error("Error getting filesize of %s", f->filename); I_Error("Error getting filesize of %s", f->id.filename);
f->size = (UINT32)filesize; f->size = (UINT32)filesize;
fseek(transfer[i].currentfile, 0, SEEK_SET); fseek(transfer[i].currentfile, 0, SEEK_SET);
} }
else else // Sending RAM
transfer[i].currentfile = (FILE *)1; transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open
transfer[i].position = 0; transfer[i].position = 0;
} }
// Build a packet containing a file fragment
p = &netbuffer->u.filetxpak; p = &netbuffer->u.filetxpak;
size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE); size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE);
if (f->size-transfer[i].position < size) if (f->size-transfer[i].position < size)
size = f->size-transfer[i].position; size = f->size-transfer[i].position;
if (ram) if (ram)
M_Memcpy(p->data, &f->filename[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 if (fread(p->data, 1, size, transfer[i].currentfile) != size)
I_Error("FiletxTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->filename, transfer[i].position, strerror(ferror(transfer[i].currentfile))); I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, strerror(ferror(transfer[i].currentfile)));
p->position = LONG(transfer[i].position); p->position = LONG(transfer[i].position);
// put flag so receiver know the totalsize // Put flag so receiver knows the total size
if (transfer[i].position + size == f->size) if (transfer[i].position + size == f->size)
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);
netbuffer->packettype = PT_FILEFRAGMENT;
if (!HSendPacket(i, true, 0, FILETXHEADER + size)) // reliable SEND // Send the packet
{ // not sent for some odd reason, retry at next call if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND
if (!ram) { // Success
fseek(transfer[i].currentfile,transfer[i].position,SEEK_SET); transfer[i].position = (UINT32)(transfer[i].position + size);
// exit the while (can't send this one so why should i send the next?) if (transfer[i].position == f->size) // Finish?
break; SV_EndFileSend(i);
} }
else // success else
{ { // Not sent for some odd reason, retry at next call
transfer[i].position = (UINT32)(size+transfer[i].position); if (!ram)
if (transfer[i].position == f->size) // finish ? fseek(transfer[i].currentfile,transfer[i].position, SEEK_SET);
EndSend(i); // Exit the while (can't send this one so why should i send the next?)
break;
} }
} }
} }
@ -642,55 +763,90 @@ void FiletxTicker(void)
void Got_Filetxpak(void) void Got_Filetxpak(void)
{ {
INT32 filenum = netbuffer->u.filetxpak.fileid; INT32 filenum = netbuffer->u.filetxpak.fileid;
fileneeded_t *file = &fileneeded[filenum];
char *filename = file->filename;
static INT32 filetime = 0; static INT32 filetime = 0;
if (!(strcmp(filename, "srb2.srb")
&& strcmp(filename, "srb2.wad")
&& strcmp(filename, "zones.dta")
&& strcmp(filename, "player.dta")
&& strcmp(filename, "rings.dta")
&& strcmp(filename, "patch.dta")
&& strcmp(filename, "music.dta")
))
I_Error("Tried to download \"%s\"", filename);
if (filenum >= fileneedednum) if (filenum >= fileneedednum)
{ {
DEBFILE(va("fileframent not needed %d>%d\n",filenum, fileneedednum)); DEBFILE(va("fileframent not needed %d>%d\n", filenum, fileneedednum));
//I_Error("Received an unneeded file fragment (file id received: %d, file id needed: %d)\n", filenum, fileneedednum);
return; return;
} }
if (fileneeded[filenum].status == FS_REQUESTED) if (file->status == FS_REQUESTED)
{ {
if (fileneeded[filenum].phandle) I_Error("Got_Filetxpak: allready open file\n"); if (file->file)
fileneeded[filenum].phandle = fopen(fileneeded[filenum].filename, "wb"); I_Error("Got_Filetxpak: already open file\n");
if (!fileneeded[filenum].phandle) I_Error("Can't create file %s: %s",fileneeded[filenum].filename, strerror(errno)); file->file = fopen(filename, "wb");
CONS_Printf("\r%s...\n",fileneeded[filenum].filename); if (!file->file)
fileneeded[filenum].currentsize = 0; I_Error("Can't create file %s: %s", filename, strerror(errno));
fileneeded[filenum].status = FS_DOWNLOADING; CONS_Printf("\r%s...\n",filename);
file->currentsize = 0;
file->status = FS_DOWNLOADING;
} }
if (fileneeded[filenum].status == FS_DOWNLOADING) if (file->status == FS_DOWNLOADING)
{ {
UINT32 pos = LONG(netbuffer->u.filetxpak.position); UINT32 pos = LONG(netbuffer->u.filetxpak.position);
UINT16 size = SHORT(netbuffer->u.filetxpak.size); UINT16 size = SHORT(netbuffer->u.filetxpak.size);
// use a special tric to know when file is finished (not allways used) // Use a special trick to know when the file is complete (not always used)
// WARNING: filepak can arrive out of order so don't stop now ! // WARNING: file fragments can arrive out of order so don't stop yet!
if (pos & 0x80000000) if (pos & 0x80000000)
{ {
pos &= ~0x80000000; pos &= ~0x80000000;
fileneeded[filenum].totalsize = pos + size; file->totalsize = pos + size;
} }
// 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(fileneeded[filenum].phandle,pos,SEEK_SET); fseek(file->file, pos, SEEK_SET);
if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].phandle)!=1) if (fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1)
I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].phandle))); I_Error("Can't write to %s: %s\n",filename, strerror(ferror(file->file)));
fileneeded[filenum].currentsize += size; file->currentsize += size;
// finished? // Finished?
if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize) if (file->currentsize == file->totalsize)
{ {
fclose(fileneeded[filenum].phandle); fclose(file->file);
fileneeded[filenum].phandle = NULL; file->file = NULL;
fileneeded[filenum].status = FS_FOUND; file->status = FS_FOUND;
CONS_Printf(M_GetText("Downloading %s...(done)\n"), CONS_Printf(M_GetText("Downloading %s...(done)\n"),
fileneeded[filenum].filename); filename);
} }
} }
else else
I_Error("Received a file not requested\n"); {
// send ack back quickly const char *s;
switch(file->status)
{
case FS_NOTFOUND:
s = "FS_NOTFOUND";
break;
case FS_FOUND:
s = "FS_FOUND";
break;
case FS_OPEN:
s = "FS_OPEN";
break;
case FS_MD5SUMBAD:
s = "FS_MD5SUMBAD";
break;
default:
s = "unknown";
break;
}
I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s);
}
// Send ack back quickly
if (++filetime == 3) if (++filetime == 3)
{ {
Net_SendAcks(servernode); Net_SendAcks(servernode);
@ -702,33 +858,50 @@ void Got_Filetxpak(void)
#endif #endif
} }
void AbortSendFiles(INT32 node) /** \brief Checks if a node is downloading a file
*
* \param node The node to check for
* \return True if the node is downloading a file
*
*/
boolean SV_SendingFile(INT32 node)
{
return transfer[node].txlist != NULL;
}
/** Cancels all file requests for a node
*
* \param node The destination
* \sa SV_EndFileSend
*
*/
void SV_AbortSendFiles(INT32 node)
{ {
while (transfer[node].txlist) while (transfer[node].txlist)
EndSend(node); SV_EndFileSend(node);
} }
void CloseNetFile(void) void CloseNetFile(void)
{ {
INT32 i; INT32 i;
// is sending? // Is sending?
for (i = 0; i < MAXNETNODES; i++) for (i = 0; i < MAXNETNODES; i++)
AbortSendFiles(i); SV_AbortSendFiles(i);
// receiving a file? // Receiving a file?
for (i = 0; i < MAX_WADFILES; i++) for (i = 0; i < MAX_WADFILES; i++)
if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].phandle) if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file)
{ {
fclose(fileneeded[i].phandle); fclose(fileneeded[i].file);
// file is not complete delete it // File is not complete delete it
remove(fileneeded[i].filename); remove(fileneeded[i].filename);
} }
// remove FILEFRAGMENT from acknledge list // Remove PT_FILEFRAGMENT from acknowledge list
Net_AbortPacketType(PT_FILEFRAGMENT); Net_AbortPacketType(PT_FILEFRAGMENT);
} }
// functions cut and pasted from doomatic :) // Functions cut and pasted from Doomatic :)
void nameonly(char *s) void nameonly(char *s)
{ {

View file

@ -29,21 +29,21 @@ typedef enum
FS_FOUND, FS_FOUND,
FS_REQUESTED, FS_REQUESTED,
FS_DOWNLOADING, FS_DOWNLOADING,
FS_OPEN, // is opened and used in w_wad FS_OPEN, // Is opened and used in w_wad
FS_MD5SUMBAD FS_MD5SUMBAD
} filestatus_t; } filestatus_t;
typedef struct typedef struct
{ {
UINT8 important; UINT8 important;
UINT8 willsend; // is the server willing to send it? UINT8 willsend; // Is the server willing to send it?
char filename[MAX_WADPATH]; char filename[MAX_WADPATH];
UINT8 md5sum[16]; UINT8 md5sum[16];
// used only for download // Used only for download
FILE *phandle; FILE *file;
UINT32 currentsize; UINT32 currentsize;
UINT32 totalsize; UINT32 totalsize;
filestatus_t status; // the value returned by recsearch filestatus_t status; // The value returned by recsearch
} fileneeded_t; } fileneeded_t;
extern INT32 fileneedednum; extern INT32 fileneedednum;
@ -58,28 +58,25 @@ UINT8 *PutFileNeeded(void);
void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr); void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr);
void CL_PrepareDownloadSaveGame(const char *tmpsave); void CL_PrepareDownloadSaveGame(const char *tmpsave);
// check file list in wadfiles return 0 when a file is not found
// 1 if all file are found
// 2 if you cannot connect (different wad version or
// no enought space to download files)
INT32 CL_CheckFiles(void); INT32 CL_CheckFiles(void);
void CL_LoadServerFiles(void); void CL_LoadServerFiles(void);
void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod,
UINT8 fileid); UINT8 fileid);
void FiletxTicker(void); void SV_FileSendTicker(void);
void Got_Filetxpak(void); void Got_Filetxpak(void);
boolean SV_SendingFile(INT32 node);
boolean CL_CheckDownloadable(void); boolean CL_CheckDownloadable(void);
boolean CL_SendRequestFile(void); boolean CL_SendRequestFile(void);
void Got_RequestFilePak(INT32 node); void Got_RequestFilePak(INT32 node);
void AbortSendFiles(INT32 node); void SV_AbortSendFiles(INT32 node);
void CloseNetFile(void); void CloseNetFile(void);
boolean fileexist(char *filename, time_t ptime); boolean fileexist(char *filename, time_t ptime);
// search a file in the wadpath, return FS_FOUND when found // Search a file in the wadpath, return FS_FOUND when found
filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum,
boolean completepath); boolean completepath);
filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum); filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum);

View file

@ -44,6 +44,7 @@ typedef enum
SF_STOMPDAMAGE = 1<<9, // Always damage enemies, etc by landing on them, no matter your vunerability? SF_STOMPDAMAGE = 1<<9, // Always damage enemies, etc by landing on them, no matter your vunerability?
SF_MARIODAMAGE = SF_NOJUMPDAMAGE|SF_STOMPDAMAGE, // The Mario method of being able to damage enemies, etc. SF_MARIODAMAGE = SF_NOJUMPDAMAGE|SF_STOMPDAMAGE, // The Mario method of being able to damage enemies, etc.
SF_MACHINE = 1<<10, // Beep boop. Are you a robot? SF_MACHINE = 1<<10, // Beep boop. Are you a robot?
SF_NOSPINDASHDUST = 1<<11, // Don't spawn dust particles when charging a spindash
// free up to and including 1<<31 // free up to and including 1<<31
} skinflags_t; } skinflags_t;
@ -151,7 +152,13 @@ typedef enum
PF_ANALOGMODE = 1<<26, // Analog mode? PF_ANALOGMODE = 1<<26, // Analog mode?
// Can carry another player? // Can carry another player?
PF_CANCARRY = 1<<27 PF_CANCARRY = 1<<27,
// Used shield ability
PF_SHIELDABILITY = 1<<28,
// Force jump damage?
PF_FORCEJUMPDAMAGE = 1<<29
// free up to and including 1<<31 // free up to and including 1<<31
} pflags_t; } pflags_t;
@ -178,23 +185,34 @@ typedef enum
typedef enum typedef enum
{ {
SH_NONE = 0, SH_NONE = 0,
// Standard shields
SH_JUMP,
SH_ATTRACT,
SH_ELEMENTAL,
SH_BOMB,
// Stupid useless unimplimented Sonic 3 shields
SH_BUBBLEWRAP,
SH_THUNDERCOIN,
SH_FLAMEAURA,
// Pity shield: the world's most basic shield ever, given to players who suck at Match
SH_PITY,
// The fireflower is special, it combines with other shields.
SH_FIREFLOWER = 0x100,
// The force shield uses the lower 8 bits to count how many hits are left.
SH_FORCE = 0x200,
SH_STACK = SH_FIREFLOWER, // Shield flags
SH_PROTECTFIRE = 0x400,
SH_PROTECTWATER = 0x800,
SH_PROTECTELECTRIC = 0x1000,
// Indivisible shields
SH_PITY = 1, // the world's most basic shield ever, given to players who suck at Match
SH_WHIRLWIND,
SH_ARMAGEDDON,
// normal shields that use flags
SH_ATTRACT = SH_PROTECTELECTRIC,
SH_ELEMENTAL = SH_PROTECTFIRE|SH_PROTECTWATER,
// Sonic 3 shields
SH_FLAMEAURA = SH_PROTECTFIRE,
SH_BUBBLEWRAP = SH_PROTECTWATER,
SH_THUNDERCOIN = SH_WHIRLWIND|SH_PROTECTELECTRIC,
// The force shield uses the lower 8 bits to count how many extra hits are left.
SH_FORCE = 0x100,
SH_FORCEHP = 0xFF, // to be used as a bitmask only
// Mostly for use with Mario mode.
SH_FIREFLOWER = 0x200,
SH_STACK = SH_FIREFLOWER, // second-layer shields
SH_NOSTACK = ~SH_STACK SH_NOSTACK = ~SH_STACK
} shieldtype_t; // pw_shield } shieldtype_t; // pw_shield
@ -297,10 +315,8 @@ typedef struct player_s
// It is updated with cmd->aiming. // It is updated with cmd->aiming.
angle_t aiming; angle_t aiming;
// This is only used between levels, // player's ring count
// mo->health is used during levels. INT32 rings;
/// \todo Remove this. We don't need a second health definition for players.
INT32 health;
SINT8 pity; // i pity the fool. SINT8 pity; // i pity the fool.
INT32 currentweapon; // current weapon selected. INT32 currentweapon; // current weapon selected.
@ -361,8 +377,8 @@ typedef struct player_s
UINT8 gotcontinue; // Got continue from this stage? UINT8 gotcontinue; // Got continue from this stage?
fixed_t speed; // Player's speed (distance formula of MOMX and MOMY values) fixed_t speed; // Player's speed (distance formula of MOMX and MOMY values)
UINT8 jumping; // Jump counter UINT8 jumping; // Holding down jump button
UINT8 secondjump; UINT8 secondjump; // Jump counter
UINT8 fly1; // Tails flying UINT8 fly1; // Tails flying
UINT8 scoreadd; // Used for multiple enemy attack bonus UINT8 scoreadd; // Used for multiple enemy attack bonus

View file

@ -371,7 +371,9 @@ static void clear_levels(void)
// (no need to set num to 0, we're freeing the entire header shortly) // (no need to set num to 0, we're freeing the entire header shortly)
Z_Free(mapheaderinfo[i]->customopts); Z_Free(mapheaderinfo[i]->customopts);
P_DeleteFlickies(i);
P_DeleteGrades(i); P_DeleteGrades(i);
Z_Free(mapheaderinfo[i]); Z_Free(mapheaderinfo[i]);
mapheaderinfo[i] = NULL; mapheaderinfo[i] = NULL;
} }
@ -990,6 +992,34 @@ static const struct {
{NULL, 0} {NULL, 0}
}; };
static const struct {
const char *name;
const mobjtype_t type;
} FLICKYTYPES[] = {
{"BLUEBIRD", MT_FLICKY_01},
{"RABBIT", MT_FLICKY_02},
{"CHICKEN", MT_FLICKY_03},
{"SEAL", MT_FLICKY_04},
{"PIG", MT_FLICKY_05},
{"CHIPMUNK", MT_FLICKY_06},
{"PENGUIN", MT_FLICKY_07},
{"FISH", MT_FLICKY_08},
{"RAM", MT_FLICKY_09},
{"PUFFIN", MT_FLICKY_10},
{"COW", MT_FLICKY_11},
{"RAT", MT_FLICKY_12},
{"BEAR", MT_FLICKY_13},
{"DOVE", MT_FLICKY_14},
{"CAT", MT_FLICKY_15},
{"CANARY", MT_FLICKY_16},
{"a", 0}, // End of normal flickies - a lower case character so will never fastcmp valid with uppercase tmp
//{"FLICKER", MT_FLICKER},
{"SEED", MT_SEED},
{NULL, 0}
};
#define MAXFLICKIES 64
static void readlevelheader(MYFILE *f, INT32 num) static void readlevelheader(MYFILE *f, INT32 num)
{ {
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -1088,8 +1118,82 @@ static void readlevelheader(MYFILE *f, INT32 num)
// Now go to uppercase // Now go to uppercase
strupr(word2); strupr(word2);
// List of flickies that are be freed in this map
if (fastcmp(word, "FLICKYLIST") || fastcmp(word, "ANIMALLIST"))
{
if (fastcmp(word2, "NONE"))
P_DeleteFlickies(num-1);
else if (fastcmp(word2, "DEMO"))
P_SetDemoFlickies(num-1);
else if (fastcmp(word2, "ALL"))
{
mobjtype_t tmpflickies[MAXFLICKIES];
for (mapheaderinfo[num-1]->numFlickies = 0;
((mapheaderinfo[num-1]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type);
mapheaderinfo[num-1]->numFlickies++)
tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type;
if (mapheaderinfo[num-1]->numFlickies) // just in case...
{
size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies;
mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL);
M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize);
}
}
else
{
mobjtype_t tmpflickies[MAXFLICKIES];
mapheaderinfo[num-1]->numFlickies = 0;
tmp = strtok(word2,",");
// get up to the first MAXFLICKIES flickies
do {
if (mapheaderinfo[num-1]->numFlickies == MAXFLICKIES) // never going to get above that number
{
deh_warning("Level header %d: too many flickies\n", num);
break;
}
if (fastncmp(tmp, "MT_", 3)) // support for specified mobjtypes...
{
i = get_mobjtype(tmp);
if (!i)
{
//deh_warning("Level header %d: unknown flicky mobj type %s\n", num, tmp); -- no need for this line as get_mobjtype complains too
continue;
}
tmpflickies[mapheaderinfo[num-1]->numFlickies] = i;
}
else // ...or a quick, limited selection of default flickies!
{
for (i = 0; FLICKYTYPES[i].name; i++)
if (fastcmp(tmp, FLICKYTYPES[i].name))
break;
if (!FLICKYTYPES[i].name)
{
deh_warning("Level header %d: unknown flicky selection %s\n", num, tmp);
continue;
}
tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[i].type;
}
mapheaderinfo[num-1]->numFlickies++;
} while ((tmp = strtok(NULL,",")) != NULL);
if (mapheaderinfo[num-1]->numFlickies)
{
size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies;
mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL);
// now we add them to the list!
M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize);
}
else
deh_warning("Level header %d: no valid flicky types found\n", num);
}
}
// NiGHTS grades // NiGHTS grades
if (fastncmp(word, "GRADES", 6)) else if (fastncmp(word, "GRADES", 6))
{ {
UINT8 mare = (UINT8)atoi(word + 6); UINT8 mare = (UINT8)atoi(word + 6);
@ -1322,6 +1426,8 @@ static void readlevelheader(MYFILE *f, INT32 num)
Z_Free(s); Z_Free(s);
} }
#undef MAXFLICKIES
static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum) static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
{ {
char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL); char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
@ -1687,6 +1793,9 @@ static actionpointer_t actionpointers[] =
{{A_WaterShield}, "A_WATERSHIELD"}, {{A_WaterShield}, "A_WATERSHIELD"},
{{A_ForceShield}, "A_FORCESHIELD"}, {{A_ForceShield}, "A_FORCESHIELD"},
{{A_PityShield}, "A_PITYSHIELD"}, {{A_PityShield}, "A_PITYSHIELD"},
{{A_FlameShield}, "A_FLAMESHIELD"},
{{A_BubbleShield}, "A_BUBBLESHIELD"},
{{A_ThunderShield}, "A_THUNDERSHIELD"},
{{A_GravityBox}, "A_GRAVITYBOX"}, {{A_GravityBox}, "A_GRAVITYBOX"},
{{A_ScoreRise}, "A_SCORERISE"}, {{A_ScoreRise}, "A_SCORERISE"},
{{A_ParticleSpawn}, "A_PARTICLESPAWN"}, {{A_ParticleSpawn}, "A_PARTICLESPAWN"},
@ -1842,6 +1951,17 @@ static actionpointer_t actionpointers[] =
{{A_BrakLobShot}, "A_BRAKLOBSHOT"}, {{A_BrakLobShot}, "A_BRAKLOBSHOT"},
{{A_NapalmScatter}, "A_NAPALMSCATTER"}, {{A_NapalmScatter}, "A_NAPALMSCATTER"},
{{A_SpawnFreshCopy}, "A_SPAWNFRESHCOPY"}, {{A_SpawnFreshCopy}, "A_SPAWNFRESHCOPY"},
{{A_FlickySpawn}, "A_FLICKYSPAWN"},
{{A_FlickyAim}, "A_FLICKYAIM"},
{{A_FlickyFly}, "A_FLICKYFLY"},
{{A_FlickySoar}, "A_FLICKYSOAR"},
{{A_FlickyCoast}, "A_FLICKYCOAST"},
{{A_FlickyHop}, "A_FLICKYHOP"},
{{A_FlickyFlounder}, "A_FLICKYFLOUNDER"},
{{A_FlickyCheck}, "A_FLICKYCHECK"},
{{A_FlickyHeightCheck}, "A_FLICKYHEIGHTCHECK"},
{{A_FlickyFlutter}, "A_FLICKYFLUTTER"},
{{A_FlameParticle}, "A_FLAMEPARTICLE"},
{{NULL}, "NONE"}, {{NULL}, "NONE"},
@ -2778,7 +2898,7 @@ static void readpatch(MYFILE *f, const char *name, UINT16 wad)
char *word2; char *word2;
char *tmp; char *tmp;
INT32 i = 0, j = 0, value; INT32 i = 0, j = 0, value;
texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX}; texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX, 0};
// Jump to the texture this patch belongs to, which, // Jump to the texture this patch belongs to, which,
// coincidentally, is always the last one on the buffer cache. // coincidentally, is always the last one on the buffer cache.
@ -3378,11 +3498,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
{ {
if (i == 0 && word2[0] != '0') // If word2 isn't a number if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_mobjtype(word2); // find a thing by name i = get_mobjtype(word2); // find a thing by name
if (i < NUMMOBJTYPES && i >= 0) if (i < NUMMOBJTYPES && i > 0)
readthing(f, i); readthing(f, i);
else else
{ {
deh_warning("Thing %d out of range (0 - %d)", i, NUMMOBJTYPES-1); deh_warning("Thing %d out of range (1 - %d)", i, NUMMOBJTYPES-1);
ignorelines(f); ignorelines(f);
} }
DEH_WriteUndoline(word, word2, UNDO_HEADER); DEH_WriteUndoline(word, word2, UNDO_HEADER);
@ -4279,6 +4399,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_GOOP1", "S_GOOP1",
"S_GOOP2", "S_GOOP2",
"S_GOOP3", "S_GOOP3",
"S_GOOPTRAIL",
// Boss 3 // Boss 3
"S_EGGMOBILE3_STND", "S_EGGMOBILE3_STND",
@ -4834,7 +4955,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SPIKEBALL7", "S_SPIKEBALL7",
"S_SPIKEBALL8", "S_SPIKEBALL8",
// Fire Shield's Spawn // Elemental Shield's Spawn
"S_SPINFIRE1", "S_SPINFIRE1",
"S_SPINFIRE2", "S_SPINFIRE2",
"S_SPINFIRE3", "S_SPINFIRE3",
@ -4855,7 +4976,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
// Starpost // Starpost
"S_STARPOST_IDLE", "S_STARPOST_IDLE",
"S_STARPOST_FLASH", "S_STARPOST_FLASH",
"S_STARPOST_STARTSPIN",
"S_STARPOST_SPIN", "S_STARPOST_SPIN",
"S_STARPOST_ENDSPIN",
// Big floating mine // Big floating mine
"S_BIGMINE1", "S_BIGMINE1",
@ -4908,6 +5031,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_RECYCLER_BOX", "S_RECYCLER_BOX",
"S_SCORE1K_BOX", "S_SCORE1K_BOX",
"S_SCORE10K_BOX", "S_SCORE10K_BOX",
"S_FLAMEAURA_BOX",
"S_BUBBLEWRAP_BOX",
"S_THUNDERCOIN_BOX",
// Gold Repeat Monitor States (one per box) // Gold Repeat Monitor States (one per box)
"S_PITY_GOLDBOX", "S_PITY_GOLDBOX",
@ -4920,6 +5046,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_INVULN_GOLDBOX", "S_INVULN_GOLDBOX",
"S_EGGMAN_GOLDBOX", "S_EGGMAN_GOLDBOX",
"S_GRAVITY_GOLDBOX", "S_GRAVITY_GOLDBOX",
"S_FLAMEAURA_GOLDBOX",
"S_BUBBLEWRAP_GOLDBOX",
"S_THUNDERCOIN_GOLDBOX",
// Team Ring Boxes (these are special) // Team Ring Boxes (these are special)
"S_RING_REDBOX1", "S_RING_REDBOX1",
@ -4981,6 +5110,15 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SCORE10K_ICON1", "S_SCORE10K_ICON1",
"S_SCORE10K_ICON2", "S_SCORE10K_ICON2",
"S_FLAMEAURA_ICON1",
"S_FLAMEAURA_ICON2",
"S_BUBBLEWRAP_ICON1",
"S_BUBBLEWRAP_ICON2",
"S_THUNDERCOIN_ICON1",
"S_THUNDERCOIN_ICON2",
"S_ROCKET", "S_ROCKET",
"S_LASER", "S_LASER",
@ -5022,21 +5160,15 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_DEMONFIRE6", "S_DEMONFIRE6",
"S_GFZFLOWERA", "S_GFZFLOWERA",
"S_GFZFLOWERA2", "S_GFZFLOWERB",
"S_GFZFLOWERC",
"S_GFZFLOWERB1",
"S_GFZFLOWERB2",
"S_GFZFLOWERC1",
"S_BERRYBUSH", "S_BERRYBUSH",
"S_BUSH", "S_BUSH",
// THZ Plant // THZ Plant
"S_THZPLANT1", "S_THZFLOWERA",
"S_THZPLANT2", "S_THZFLOWERB",
"S_THZPLANT3",
"S_THZPLANT4",
// THZ Alarm // THZ Alarm
"S_ALARM1", "S_ALARM1",
@ -5081,6 +5213,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FLAME2", "S_FLAME2",
"S_FLAME3", "S_FLAME3",
"S_FLAME4", "S_FLAME4",
"S_FLAME5",
"S_FLAME6",
"S_FLAMEPARTICLE",
"S_FLAMEREST",
// Eggman Statue // Eggman Statue
"S_EGGSTATUE1", "S_EGGSTATUE1",
@ -5142,36 +5279,13 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
// Spinning flame jets // Spinning flame jets
"S_FJSPINAXISA1", // Counter-clockwise "S_FJSPINAXISA1", // Counter-clockwise
"S_FJSPINAXISA2", "S_FJSPINAXISA2",
"S_FJSPINAXISA3",
"S_FJSPINAXISA4",
"S_FJSPINAXISA5",
"S_FJSPINAXISA6",
"S_FJSPINAXISA7",
"S_FJSPINAXISA8",
"S_FJSPINAXISA9",
"S_FJSPINHELPERA1",
"S_FJSPINHELPERA2",
"S_FJSPINHELPERA3",
"S_FJSPINAXISB1", // Clockwise "S_FJSPINAXISB1", // Clockwise
"S_FJSPINAXISB2", "S_FJSPINAXISB2",
"S_FJSPINAXISB3",
"S_FJSPINAXISB4",
"S_FJSPINAXISB5",
"S_FJSPINAXISB6",
"S_FJSPINAXISB7",
"S_FJSPINAXISB8",
"S_FJSPINAXISB9",
"S_FJSPINHELPERB1",
"S_FJSPINHELPERB2",
"S_FJSPINHELPERB3",
// Blade's flame // Blade's flame
"S_FLAMEJETFLAMEB1", "S_FLAMEJETFLAMEB1",
"S_FLAMEJETFLAMEB2", "S_FLAMEJETFLAMEB2",
"S_FLAMEJETFLAMEB3", "S_FLAMEJETFLAMEB3",
"S_FLAMEJETFLAMEB4",
"S_FLAMEJETFLAMEB5",
"S_FLAMEJETFLAMEB6",
// Trapgoyles // Trapgoyles
"S_TRAPGOYLE", "S_TRAPGOYLE",
@ -5266,8 +5380,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_BSZVINE_ORANGE", "S_BSZVINE_ORANGE",
"S_BSZSHRUB", "S_BSZSHRUB",
"S_BSZCLOVER", "S_BSZCLOVER",
"S_BSZFISH", "S_BIG_PALMTREE_TRUNK",
"S_BSZSUNFLOWER", "S_BIG_PALMTREE_TOP",
"S_PALMTREE_TRUNK",
"S_PALMTREE_TOP",
"S_DBALL1", "S_DBALL1",
"S_DBALL2", "S_DBALL2",
@ -5350,6 +5466,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_MAGN10", "S_MAGN10",
"S_MAGN11", "S_MAGN11",
"S_MAGN12", "S_MAGN12",
"S_MAGN13",
"S_FORC1", "S_FORC1",
"S_FORC2", "S_FORC2",
@ -5373,6 +5490,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FORC19", "S_FORC19",
"S_FORC20", "S_FORC20",
"S_FORC21",
"S_ELEM1", "S_ELEM1",
"S_ELEM2", "S_ELEM2",
"S_ELEM3", "S_ELEM3",
@ -5386,6 +5505,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_ELEM11", "S_ELEM11",
"S_ELEM12", "S_ELEM12",
"S_ELEM13",
"S_ELEM14",
"S_ELEMF1", "S_ELEMF1",
"S_ELEMF2", "S_ELEMF2",
"S_ELEMF3", "S_ELEMF3",
@ -5394,6 +5516,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_ELEMF6", "S_ELEMF6",
"S_ELEMF7", "S_ELEMF7",
"S_ELEMF8", "S_ELEMF8",
"S_ELEMF9",
"S_ELEMF10",
"S_PITY1", "S_PITY1",
"S_PITY2", "S_PITY2",
@ -5406,6 +5530,84 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_PITY9", "S_PITY9",
"S_PITY10", "S_PITY10",
"S_FIRS1",
"S_FIRS2",
"S_FIRS3",
"S_FIRS4",
"S_FIRS5",
"S_FIRS6",
"S_FIRS7",
"S_FIRS8",
"S_FIRS9",
"S_FIRS10",
"S_FIRS11",
"S_FIRSB1",
"S_FIRSB2",
"S_FIRSB3",
"S_FIRSB4",
"S_FIRSB5",
"S_FIRSB6",
"S_FIRSB7",
"S_FIRSB8",
"S_FIRSB9",
"S_FIRSB10",
"S_BUBS1",
"S_BUBS2",
"S_BUBS3",
"S_BUBS4",
"S_BUBS5",
"S_BUBS6",
"S_BUBS7",
"S_BUBS8",
"S_BUBS9",
"S_BUBS10",
"S_BUBS11",
"S_BUBSB1",
"S_BUBSB2",
"S_BUBSB3",
"S_BUBSB4",
"S_BUBSB5",
"S_BUBSB6",
"S_ZAPS1",
"S_ZAPS2",
"S_ZAPS3",
"S_ZAPS4",
"S_ZAPS5",
"S_ZAPS6",
"S_ZAPS7",
"S_ZAPS8",
"S_ZAPS9",
"S_ZAPS10",
"S_ZAPS11",
"S_ZAPS12",
"S_ZAPS13", // blank frame
"S_ZAPS14",
"S_ZAPS15",
"S_ZAPS16",
"S_ZAPSB1", // blank frame
"S_ZAPSB2",
"S_ZAPSB3",
"S_ZAPSB4",
"S_ZAPSB5",
"S_ZAPSB6",
"S_ZAPSB7",
"S_ZAPSB8",
"S_ZAPSB9",
"S_ZAPSB10",
"S_ZAPSB11", // blank frame
// Thunder spark
"S_THUNDERCOIN_SPARK",
// Invincibility Sparkles // Invincibility Sparkles
"S_IVSP", "S_IVSP",
@ -5416,43 +5618,133 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SSPK4", "S_SSPK4",
"S_SSPK5", "S_SSPK5",
// Freed Birdie // Flicky-sized bubble
"S_BIRD1", "S_FLICKY_BUBBLE",
"S_BIRD2",
"S_BIRD3",
// Freed Bunny // Bluebird
"S_BUNNY1", "S_FLICKY_01_OUT",
"S_BUNNY2", "S_FLICKY_01_FLAP1",
"S_BUNNY3", "S_FLICKY_01_FLAP2",
"S_BUNNY4", "S_FLICKY_01_FLAP3",
"S_BUNNY5",
"S_BUNNY6",
"S_BUNNY7",
"S_BUNNY8",
"S_BUNNY9",
"S_BUNNY10",
// Freed Mouse // Rabbit
"S_MOUSE1", "S_FLICKY_02_OUT",
"S_MOUSE2", "S_FLICKY_02_AIM",
"S_FLICKY_02_HOP",
"S_FLICKY_02_UP",
"S_FLICKY_02_DOWN",
// Freed Chicken // Chicken
"S_CHICKEN1", "S_FLICKY_03_OUT",
"S_CHICKENHOP", "S_FLICKY_03_AIM",
"S_CHICKENFLY1", "S_FLICKY_03_HOP",
"S_CHICKENFLY2", "S_FLICKY_03_UP",
"S_FLICKY_03_FLAP1",
"S_FLICKY_03_FLAP2",
// Freed Cow // Seal
"S_COW1", "S_FLICKY_04_OUT",
"S_COW2", "S_FLICKY_04_AIM",
"S_COW3", "S_FLICKY_04_HOP",
"S_COW4", "S_FLICKY_04_UP",
"S_FLICKY_04_DOWN",
"S_FLICKY_04_SWIM1",
"S_FLICKY_04_SWIM2",
"S_FLICKY_04_SWIM3",
"S_FLICKY_04_SWIM4",
// Red Birdie in Bubble // Pig
"S_RBIRD1", "S_FLICKY_05_OUT",
"S_RBIRD2", "S_FLICKY_05_AIM",
"S_RBIRD3", "S_FLICKY_05_HOP",
"S_FLICKY_05_UP",
"S_FLICKY_05_DOWN",
// Chipmunk
"S_FLICKY_06_OUT",
"S_FLICKY_06_AIM",
"S_FLICKY_06_HOP",
"S_FLICKY_06_UP",
"S_FLICKY_06_DOWN",
// Penguin
"S_FLICKY_07_OUT",
"S_FLICKY_07_AIML",
"S_FLICKY_07_HOPL",
"S_FLICKY_07_UPL",
"S_FLICKY_07_DOWNL",
"S_FLICKY_07_AIMR",
"S_FLICKY_07_HOPR",
"S_FLICKY_07_UPR",
"S_FLICKY_07_DOWNR",
"S_FLICKY_07_SWIM1",
"S_FLICKY_07_SWIM2",
"S_FLICKY_07_SWIM3",
// Fish
"S_FLICKY_08_OUT",
"S_FLICKY_08_AIM",
"S_FLICKY_08_HOP",
"S_FLICKY_08_FLAP1",
"S_FLICKY_08_FLAP2",
"S_FLICKY_08_FLAP3",
"S_FLICKY_08_FLAP4",
"S_FLICKY_08_SWIM1",
"S_FLICKY_08_SWIM2",
"S_FLICKY_08_SWIM3",
"S_FLICKY_08_SWIM4",
// Ram
"S_FLICKY_09_OUT",
"S_FLICKY_09_AIM",
"S_FLICKY_09_HOP",
"S_FLICKY_09_UP",
"S_FLICKY_09_DOWN",
// Puffin
"S_FLICKY_10_OUT",
"S_FLICKY_10_FLAP1",
"S_FLICKY_10_FLAP2",
// Cow
"S_FLICKY_11_OUT",
"S_FLICKY_11_AIM",
"S_FLICKY_11_RUN1",
"S_FLICKY_11_RUN2",
"S_FLICKY_11_RUN3",
// Rat
"S_FLICKY_12_OUT",
"S_FLICKY_12_AIM",
"S_FLICKY_12_RUN1",
"S_FLICKY_12_RUN2",
"S_FLICKY_12_RUN3",
// Bear
"S_FLICKY_13_OUT",
"S_FLICKY_13_AIM",
"S_FLICKY_13_HOP",
"S_FLICKY_13_UP",
"S_FLICKY_13_DOWN",
// Dove
"S_FLICKY_14_OUT",
"S_FLICKY_14_FLAP1",
"S_FLICKY_14_FLAP2",
"S_FLICKY_14_FLAP3",
// Cat
"S_FLICKY_15_OUT",
"S_FLICKY_15_AIM",
"S_FLICKY_15_HOP",
"S_FLICKY_15_UP",
"S_FLICKY_15_DOWN",
// Canary
"S_FLICKY_16_OUT",
"S_FLICKY_16_FLAP1",
"S_FLICKY_16_FLAP2",
"S_FLICKY_16_FLAP3",
"S_YELLOWSPRING", "S_YELLOWSPRING",
"S_YELLOWSPRING2", "S_YELLOWSPRING2",
@ -5566,6 +5858,20 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_WATERZAP", "S_WATERZAP",
// Spindash dust
"S_SPINDUST1",
"S_SPINDUST2",
"S_SPINDUST3",
"S_SPINDUST4",
"S_SPINDUST_BUBBLE1",
"S_SPINDUST_BUBBLE2",
"S_SPINDUST_BUBBLE3",
"S_SPINDUST_BUBBLE4",
"S_SPINDUST_FIRE1",
"S_SPINDUST_FIRE2",
"S_SPINDUST_FIRE3",
"S_SPINDUST_FIRE4",
"S_FOG1", "S_FOG1",
"S_FOG2", "S_FOG2",
"S_FOG3", "S_FOG3",
@ -5796,20 +6102,19 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FIREBALLEXP2", "S_FIREBALLEXP2",
"S_FIREBALLEXP3", "S_FIREBALLEXP3",
"S_SHELL", "S_SHELL",
"S_SHELL1", "S_PUMA_START1",
"S_SHELL2", "S_PUMA_START2",
"S_SHELL3", "S_PUMA_UP1",
"S_SHELL4", "S_PUMA_UP2",
"S_PUMA1", "S_PUMA_UP3",
"S_PUMA2", "S_PUMA_DOWN1",
"S_PUMA3", "S_PUMA_DOWN2",
"S_PUMA4", "S_PUMA_DOWN3",
"S_PUMA5", "S_PUMATRAIL1",
"S_PUMA6", "S_PUMATRAIL2",
"S_HAMMER1", "S_PUMATRAIL3",
"S_HAMMER2", "S_PUMATRAIL4",
"S_HAMMER3", "S_HAMMER",
"S_HAMMER4",
"S_KOOPA1", "S_KOOPA1",
"S_KOOPA2", "S_KOOPA2",
"S_KOOPAFLAME1", "S_KOOPAFLAME1",
@ -5938,6 +6243,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_NIGHTOPIANHELPER6", "S_NIGHTOPIANHELPER6",
"S_NIGHTOPIANHELPER7", "S_NIGHTOPIANHELPER7",
"S_NIGHTOPIANHELPER8", "S_NIGHTOPIANHELPER8",
"S_NIGHTOPIANHELPER9",
"S_CRUMBLE1", "S_CRUMBLE1",
"S_CRUMBLE2", "S_CRUMBLE2",
@ -5961,10 +6267,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SPRK16", "S_SPRK16",
// Robot Explosion // Robot Explosion
"S_XPLD_FLICKY",
"S_XPLD1", "S_XPLD1",
"S_XPLD2", "S_XPLD2",
"S_XPLD3", "S_XPLD_EGGTRAP",
"S_XPLD4",
// Underwater Explosion // Underwater Explosion
"S_WPLD1", "S_WPLD1",
@ -6061,6 +6367,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_BOSSTANK2", "MT_BOSSTANK2",
"MT_BOSSSPIGOT", "MT_BOSSSPIGOT",
"MT_GOOP", "MT_GOOP",
"MT_GOOPTRAIL",
// Boss 3 // Boss 3
"MT_EGGMOBILE3", "MT_EGGMOBILE3",
@ -6164,6 +6471,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_RECYCLER_BOX", "MT_RECYCLER_BOX",
"MT_SCORE1K_BOX", "MT_SCORE1K_BOX",
"MT_SCORE10K_BOX", "MT_SCORE10K_BOX",
"MT_FLAMEAURA_BOX",
"MT_BUBBLEWRAP_BOX",
"MT_THUNDERCOIN_BOX",
// Monitor boxes -- repeating (big) boxes // Monitor boxes -- repeating (big) boxes
"MT_PITY_GOLDBOX", "MT_PITY_GOLDBOX",
@ -6176,6 +6486,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_INVULN_GOLDBOX", "MT_INVULN_GOLDBOX",
"MT_EGGMAN_GOLDBOX", "MT_EGGMAN_GOLDBOX",
"MT_GRAVITY_GOLDBOX", "MT_GRAVITY_GOLDBOX",
"MT_FLAMEAURA_GOLDBOX",
"MT_BUBBLEWRAP_GOLDBOX",
"MT_THUNDERCOIN_GOLDBOX",
// Monitor boxes -- special // Monitor boxes -- special
"MT_RING_REDBOX", "MT_RING_REDBOX",
@ -6198,6 +6511,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_RECYCLER_ICON", "MT_RECYCLER_ICON",
"MT_SCORE1K_ICON", "MT_SCORE1K_ICON",
"MT_SCORE10K_ICON", "MT_SCORE10K_ICON",
"MT_FLAMEAURA_ICON",
"MT_BUBBLEWRAP_ICON",
"MT_THUNDERCOIN_ICON",
// Projectiles // Projectiles
"MT_ROCKET", "MT_ROCKET",
@ -6221,7 +6537,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_BUSH", "MT_BUSH",
// Techno Hill Scenery // Techno Hill Scenery
"MT_THZPLANT", // THZ Plant "MT_THZFLOWER1",
"MT_THZFLOWER2",
"MT_ALARM", "MT_ALARM",
// Deep Sea Scenery // Deep Sea Scenery
@ -6237,6 +6554,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
// Castle Eggman Scenery // Castle Eggman Scenery
"MT_CHAIN", // CEZ Chain "MT_CHAIN", // CEZ Chain
"MT_FLAME", // Flame (has corona) "MT_FLAME", // Flame (has corona)
"MT_FLAMEPARTICLE",
"MT_EGGSTATUE", // Eggman Statue "MT_EGGSTATUE", // Eggman Statue
"MT_MACEPOINT", // Mace rotation point "MT_MACEPOINT", // Mace rotation point
"MT_SWINGMACEPOINT", // Mace swinging point "MT_SWINGMACEPOINT", // Mace swinging point
@ -6263,9 +6581,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_FLAMEJETFLAME", "MT_FLAMEJETFLAME",
"MT_FJSPINAXISA", // Counter-clockwise "MT_FJSPINAXISA", // Counter-clockwise
"MT_FJSPINHELPERA",
"MT_FJSPINAXISB", // Clockwise "MT_FJSPINAXISB", // Clockwise
"MT_FJSPINHELPERB",
"MT_FLAMEJETFLAMEB", // Blade's flame "MT_FLAMEJETFLAMEB", // Blade's flame
@ -6342,30 +6658,46 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_BSZVINE_ORANGE", "MT_BSZVINE_ORANGE",
"MT_BSZSHRUB", "MT_BSZSHRUB",
"MT_BSZCLOVER", "MT_BSZCLOVER",
"MT_BSZFISH", "MT_BIG_PALMTREE_TRUNK",
"MT_BSZSUNFLOWER", "MT_BIG_PALMTREE_TOP",
"MT_PALMTREE_TRUNK",
"MT_PALMTREE_TOP",
// Misc scenery // Misc scenery
"MT_DBALL", "MT_DBALL",
"MT_EGGSTATUE2", "MT_EGGSTATUE2",
// Powerup Indicators // Powerup Indicators
"MT_GREENORB", // Elemental shield mobj "MT_ELEMENTAL_ORB", // Elemental shield mobj
"MT_YELLOWORB", // Attract shield mobj "MT_ATTRACT_ORB", // Attract shield mobj
"MT_BLUEORB", // Force shield mobj "MT_FORCE_ORB", // Force shield mobj
"MT_BLACKORB", // Armageddon shield mobj "MT_ARMAGEDDON_ORB", // Armageddon shield mobj
"MT_WHITEORB", // Whirlwind shield mobj "MT_WHIRLWIND_ORB", // Whirlwind shield mobj
"MT_PITYORB", // Pity shield mobj "MT_PITY_ORB", // Pity shield mobj
"MT_IVSP", // invincibility sparkles "MT_FLAMEAURA_ORB", // Flame shield mobj
"MT_BUBBLEWRAP_ORB", // Bubble shield mobj
"MT_THUNDERCOIN_ORB", // Thunder shield mobj
"MT_THUNDERCOIN_SPARK", // Thunder spark
"MT_IVSP", // Invincibility sparkles
"MT_SUPERSPARK", // Super Sonic Spark "MT_SUPERSPARK", // Super Sonic Spark
// Freed Animals // Flickies
"MT_BIRD", // Birdie freed! "MT_FLICKY_01", // Bluebird
"MT_BUNNY", // Bunny freed! "MT_FLICKY_02", // Rabbit
"MT_MOUSE", // Mouse "MT_FLICKY_03", // Chicken
"MT_CHICKEN", // Chicken "MT_FLICKY_04", // Seal
"MT_COW", // Cow "MT_FLICKY_05", // Pig
"MT_REDBIRD", // Red Birdie in Bubble "MT_FLICKY_06", // Chipmunk
"MT_FLICKY_07", // Penguin
"MT_FLICKY_08", // Fish
"MT_FLICKY_09", // Ram
"MT_FLICKY_10", // Puffin
"MT_FLICKY_11", // Cow
"MT_FLICKY_12", // Rat
"MT_FLICKY_13", // Bear
"MT_FLICKY_14", // Dove
"MT_FLICKY_15", // Cat
"MT_FLICKY_16", // Canary
// Environmental Effects // Environmental Effects
"MT_RAIN", // Rain "MT_RAIN", // Rain
@ -6376,6 +6708,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_MEDIUMBUBBLE", // medium bubble "MT_MEDIUMBUBBLE", // medium bubble
"MT_EXTRALARGEBUBBLE", // extra large bubble "MT_EXTRALARGEBUBBLE", // extra large bubble
"MT_WATERZAP", "MT_WATERZAP",
"MT_SPINDUST", // Spindash dust
"MT_TFOG", "MT_TFOG",
"MT_SEED", "MT_SEED",
"MT_PARTICLE", "MT_PARTICLE",
@ -6434,6 +6767,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_FIREBALL", "MT_FIREBALL",
"MT_SHELL", "MT_SHELL",
"MT_PUMA", "MT_PUMA",
"MT_PUMATRAIL",
"MT_HAMMER", "MT_HAMMER",
"MT_KOOPA", "MT_KOOPA",
"MT_KOOPAFLAME", "MT_KOOPAFLAME",
@ -6544,35 +6878,36 @@ static const char *const MOBJFLAG_LIST[] = {
// \tMF2_(\S+).*// (.+) --> \t"\1", // \2 // \tMF2_(\S+).*// (.+) --> \t"\1", // \2
static const char *const MOBJFLAG2_LIST[] = { static const char *const MOBJFLAG2_LIST[] = {
"AXIS", // It's a NiGHTS axis! (For faster checking) "AXIS", // It's a NiGHTS axis! (For faster checking)
"TWOD", // Moves like it's in a 2D level "TWOD", // Moves like it's in a 2D level
"DONTRESPAWN", // Don't respawn this object! "DONTRESPAWN", // Don't respawn this object!
"DONTDRAW", // Don't generate a vissprite "DONTDRAW", // Don't generate a vissprite
"AUTOMATIC", // Thrown ring has automatic properties "AUTOMATIC", // Thrown ring has automatic properties
"RAILRING", // Thrown ring has rail properties "RAILRING", // Thrown ring has rail properties
"BOUNCERING", // Thrown ring has bounce properties "BOUNCERING", // Thrown ring has bounce properties
"EXPLOSION", // Thrown ring has explosive properties "EXPLOSION", // Thrown ring has explosive properties
"SCATTER", // Thrown ring has scatter properties "SCATTER", // Thrown ring has scatter properties
"BEYONDTHEGRAVE",// Source of this missile has died and has since respawned. "BEYONDTHEGRAVE", // Source of this missile has died and has since respawned.
"SLIDEPUSH", // MF_PUSHABLE that pushes continuously. "SLIDEPUSH", // MF_PUSHABLE that pushes continuously.
"CLASSICPUSH", // Drops straight down when object has negative Z. "CLASSICPUSH", // Drops straight down when object has negative Z.
"STANDONME", // While not pushable, stand on me anyway. "STANDONME", // While not pushable, stand on me anyway.
"INFLOAT", // Floating to a height for a move, don't auto float to target's height. "INFLOAT", // Floating to a height for a move, don't auto float to target's height.
"DEBRIS", // Splash ring from explosion ring "DEBRIS", // Splash ring from explosion ring
"NIGHTSPULL", // Attracted from a paraloop "NIGHTSPULL", // Attracted from a paraloop
"JUSTATTACKED", // can be pushed by other moving mobjs "JUSTATTACKED", // can be pushed by other moving mobjs
"FIRING", // turret fire "FIRING", // turret fire
"SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it. "SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
"SHADOW", // Fuzzy draw, makes targeting harder. "SHADOW", // Fuzzy draw, makes targeting harder.
"STRONGBOX", // Flag used for "strong" random monitors. "STRONGBOX", // Flag used for "strong" random monitors.
"OBJECTFLIP", // Flag for objects that always have flipped gravity. "OBJECTFLIP", // Flag for objects that always have flipped gravity.
"SKULLFLY", // Special handling: skull in flight. "SKULLFLY", // Special handling: skull in flight.
"FRET", // Flashing from a previous hit "FRET", // Flashing from a previous hit
"BOSSNOTRAP", // No Egg Trap after boss "BOSSNOTRAP", // No Egg Trap after boss
"BOSSFLEE", // Boss is fleeing! "BOSSFLEE", // Boss is fleeing!
"BOSSDEAD", // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.) "BOSSDEAD", // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
"AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH "AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH
"LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position) "LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
"SHIELD", // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
NULL NULL
}; };
@ -6654,6 +6989,9 @@ static const char *const PLAYERFLAG_LIST[] = {
/*** misc ***/ /*** misc ***/
"FORCESTRAFE", // Translate turn inputs into strafe inputs "FORCESTRAFE", // Translate turn inputs into strafe inputs
"ANALOGMODE", // Analog mode? "ANALOGMODE", // Analog mode?
"CANCARRY", // Can carry?
"SHIELDABILITY", // Thokked with shield ability
"FORCEJUMPDAMAGE", // Force jump damage
NULL // stop loop here. NULL // stop loop here.
}; };
@ -6998,20 +7336,27 @@ struct {
{"PRECIP_STORM_NOSTRIKES",PRECIP_STORM_NOSTRIKES}, {"PRECIP_STORM_NOSTRIKES",PRECIP_STORM_NOSTRIKES},
// Shields // Shields
// These ones use the lower 8 bits
{"SH_NONE",SH_NONE}, {"SH_NONE",SH_NONE},
{"SH_JUMP",SH_JUMP}, // Shield flags
{"SH_PROTECTFIRE",SH_PROTECTFIRE},
{"SH_PROTECTWATER",SH_PROTECTWATER},
{"SH_PROTECTELECTRIC",SH_PROTECTELECTRIC},
// Indivisible shields
{"SH_PITY",SH_PITY},
{"SH_WHIRLWIND",SH_WHIRLWIND},
{"SH_ARMAGEDDON",SH_ARMAGEDDON},
// normal shields that use flags
{"SH_ATTRACT",SH_ATTRACT}, {"SH_ATTRACT",SH_ATTRACT},
{"SH_ELEMENTAL",SH_ELEMENTAL}, {"SH_ELEMENTAL",SH_ELEMENTAL},
{"SH_BOMB",SH_BOMB}, // Sonic 3 shields
{"SH_FLAMEAURA",SH_FLAMEAURA},
{"SH_BUBBLEWRAP",SH_BUBBLEWRAP}, {"SH_BUBBLEWRAP",SH_BUBBLEWRAP},
{"SH_THUNDERCOIN",SH_THUNDERCOIN}, {"SH_THUNDERCOIN",SH_THUNDERCOIN},
{"SH_FLAMEAURA",SH_FLAMEAURA}, // The force shield uses the lower 8 bits to count how many extra hits are left.
{"SH_PITY",SH_PITY}, {"SH_FORCE",SH_FORCE},
// These ones are special and use the upper bits {"SH_FORCEHP",SH_FORCEHP}, // to be used as a bitmask only
{"SH_FIREFLOWER",SH_FIREFLOWER}, // Lower bits are a normal shield stacked on top of the fire flower // Mostly for use with Mario mode.
{"SH_FORCE",SH_FORCE}, // Lower bits are how many hits left, 0 is the last hit {"SH_FIREFLOWER", SH_FIREFLOWER},
// Stack masks
{"SH_STACK",SH_STACK}, {"SH_STACK",SH_STACK},
{"SH_NOSTACK",SH_NOSTACK}, {"SH_NOSTACK",SH_NOSTACK},
@ -7045,6 +7390,7 @@ struct {
{"SF_STOMPDAMAGE",SF_STOMPDAMAGE}, {"SF_STOMPDAMAGE",SF_STOMPDAMAGE},
{"SF_MARIODAMAGE",SF_MARIODAMAGE}, {"SF_MARIODAMAGE",SF_MARIODAMAGE},
{"SF_MACHINE",SF_MACHINE}, {"SF_MACHINE",SF_MACHINE},
{"SF_NOSPINDASHDUST",SF_NOSPINDASHDUST},
// Character abilities! // Character abilities!
// Primary // Primary
@ -7093,6 +7439,22 @@ struct {
{"PAL_MIXUP",PAL_MIXUP}, {"PAL_MIXUP",PAL_MIXUP},
{"PAL_RECYCLE",PAL_RECYCLE}, {"PAL_RECYCLE",PAL_RECYCLE},
{"PAL_NUKE",PAL_NUKE}, {"PAL_NUKE",PAL_NUKE},
// for P_DamageMobj
//// Damage types
{"DMG_WATER",DMG_WATER},
{"DMG_FIRE",DMG_FIRE},
{"DMG_ELECTRIC",DMG_ELECTRIC},
{"DMG_SPIKE",DMG_SPIKE},
{"DMG_NUKE",DMG_NUKE},
//// Death types
{"DMG_INSTAKILL",DMG_INSTAKILL},
{"DMG_DROWNED",DMG_DROWNED},
{"DMG_SPACEDROWN",DMG_SPACEDROWN},
{"DMG_DEATHPIT",DMG_DEATHPIT},
{"DMG_CRUSHED",DMG_CRUSHED},
{"DMG_SPECTATOR",DMG_SPECTATOR},
//// Masks
{"DMG_DEATHMASK",DMG_DEATHMASK},
// Gametypes, for use with global var "gametype" // Gametypes, for use with global var "gametype"
{"GT_COOP",GT_COOP}, {"GT_COOP",GT_COOP},
@ -7201,6 +7563,11 @@ struct {
{"FF_COLORMAPONLY",FF_COLORMAPONLY}, ///< Only copy the colormap, not the lightlevel {"FF_COLORMAPONLY",FF_COLORMAPONLY}, ///< Only copy the colormap, not the lightlevel
{"FF_GOOWATER",FF_GOOWATER}, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop. {"FF_GOOWATER",FF_GOOWATER}, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop.
#ifdef HAVE_LUA_SEGS
// Node flags
{"NF_SUBSECTOR",NF_SUBSECTOR}, // Indicate a leaf.
#endif
// Angles // Angles
{"ANG1",ANG1}, {"ANG1",ANG1},
{"ANG2",ANG2}, {"ANG2",ANG2},
@ -7346,7 +7713,7 @@ static mobjtype_t get_mobjtype(const char *word)
if (fastcmp(word, MOBJTYPE_LIST[i]+3)) if (fastcmp(word, MOBJTYPE_LIST[i]+3))
return i; return i;
deh_warning("Couldn't find mobjtype named 'MT_%s'",word); deh_warning("Couldn't find mobjtype named 'MT_%s'",word);
return MT_BLUECRAWLA; return MT_NULL;
} }
static statenum_t get_state(const char *word) static statenum_t get_state(const char *word)

View file

@ -1721,6 +1721,18 @@ INT32 I_PutEnv(char *variable)
return putenv(variable); return putenv(variable);
} }
INT32 I_ClipboardCopy(const char *data, size_t size)
{
(void)data;
(void)size;
return -1;
}
char *I_ClipboardPaste(void)
{
return NULL;
}
const CPUInfoFlags *I_CPUInfo(void) const CPUInfoFlags *I_CPUInfo(void)
{ {
static CPUInfoFlags DOS_CPUInfo; static CPUInfoFlags DOS_CPUInfo;

View file

@ -214,7 +214,7 @@ extern FILE *logstream;
// it's only for detection of the version the player is using so the MS can alert them of an update. // it's only for detection of the version the player is using so the MS can alert them of an update.
// Only set it higher, not lower, obviously. // Only set it higher, not lower, obviously.
// Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1". // Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
#define MODVERSION 21 #define MODVERSION 22
// ========================================================================= // =========================================================================
@ -407,6 +407,7 @@ void M_StartupLocale(void);
extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL; extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL;
char *va(const char *format, ...) FUNCPRINTF; char *va(const char *format, ...) FUNCPRINTF;
char *M_GetToken(const char *inputString); char *M_GetToken(const char *inputString);
void M_UnGetToken(void);
char *sizeu1(size_t num); char *sizeu1(size_t num);
char *sizeu2(size_t num); char *sizeu2(size_t num);
char *sizeu3(size_t num); char *sizeu3(size_t num);
@ -435,6 +436,9 @@ extern INT32 cv_debug;
// Misc stuff for later... // Misc stuff for later...
// ======================= // =======================
// Modifier key variables, accessible anywhere
extern UINT8 shiftdown, ctrldown, altdown;
// if we ever make our alloc stuff... // if we ever make our alloc stuff...
#define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL) #define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)

View file

@ -241,6 +241,10 @@ typedef struct
UINT8 levelflags; ///< LF_flags: merged eight booleans into one UINT8 for space, see below UINT8 levelflags; ///< LF_flags: merged eight booleans into one UINT8 for space, see below
UINT8 menuflags; ///< LF2_flags: options that affect record attack / nights mode menus UINT8 menuflags; ///< LF2_flags: options that affect record attack / nights mode menus
// Freed animals stuff.
UINT8 numFlickies; ///< Internal. For freed flicky support.
mobjtype_t *flickies; ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful.
// NiGHTS stuff. // NiGHTS stuff.
UINT8 numGradedMares; ///< Internal. For grade support. UINT8 numGradedMares; ///< Internal. For grade support.
nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful. nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful.

View file

@ -162,6 +162,18 @@ INT32 I_PutEnv(char *variable)
return -1; return -1;
} }
INT32 I_ClipboardCopy(const char *data, size_t size)
{
(void)data;
(void)size;
return -1;
}
char *I_ClipboardPaste(void)
{
return NULL;
}
void I_RegisterSysCommands(void) {} void I_RegisterSysCommands(void) {}
#include "../sdl/dosstr.c" #include "../sdl/dosstr.c"

View file

@ -974,7 +974,7 @@ static const char *credits[] = {
"Scott \"Graue\" Feeney", "Scott \"Graue\" Feeney",
"Nathan \"Jazz\" Giroux", "Nathan \"Jazz\" Giroux",
"Thomas \"Shadow Hog\" Igoe", "Thomas \"Shadow Hog\" Igoe",
"\"Monster\" Iestyn Jealous", "Iestyn \"Monster Iestyn\" Jealous",
"Ronald \"Furyhunter\" Kinard", // The SDL2 port "Ronald \"Furyhunter\" Kinard", // The SDL2 port
"John \"JTE\" Muniz", "John \"JTE\" Muniz",
"Ehab \"Wolfy\" Saeed", "Ehab \"Wolfy\" Saeed",
@ -986,6 +986,7 @@ static const char *credits[] = {
"\"chi.miru\"", // Red's secret weapon, the REAL reason slopes exist (also helped port drawing code from ZDoom) "\"chi.miru\"", // Red's secret weapon, the REAL reason slopes exist (also helped port drawing code from ZDoom)
"Andrew \"orospakr\" Clunis", "Andrew \"orospakr\" Clunis",
"Gregor \"Oogaland\" Dick", "Gregor \"Oogaland\" Dick",
"Louis-Antoine \"LJSonic\" de Moulins", // for fixing 2.1's netcode (de Rochefort doesn't quite fit on the screen sorry lol)
"Vivian \"toaster\" Grannell", "Vivian \"toaster\" Grannell",
"Julio \"Chaos Zero 64\" Guir", "Julio \"Chaos Zero 64\" Guir",
"\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog "\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog
@ -1021,7 +1022,7 @@ static const char *credits[] = {
"Paul \"Boinciel\" Clempson", "Paul \"Boinciel\" Clempson",
"Cyan Helkaraxe", "Cyan Helkaraxe",
"Kepa \"Nev3r\" Iceta", "Kepa \"Nev3r\" Iceta",
"\"Monster\" Iestyn Jealous", "Iestyn \"Monster Iestyn\" Jealous",
"Jarel \"Arrow\" Jones", "Jarel \"Arrow\" Jones",
"Stefan \"Stuf\" Rimalia", "Stefan \"Stuf\" Rimalia",
"Shane Mychal Sexton", "Shane Mychal Sexton",

View file

@ -88,6 +88,7 @@ UINT8 modeattacking = ATTACKING_NONE;
boolean disableSpeedAdjust = false; boolean disableSpeedAdjust = false;
boolean imcontinuing = false; boolean imcontinuing = false;
boolean runemeraldmanager = false; boolean runemeraldmanager = false;
UINT16 emeraldspawndelay = 60*TICRATE;
// menu demo things // menu demo things
UINT8 numDemos = 3; UINT8 numDemos = 3;
@ -2193,7 +2194,7 @@ void G_PlayerReborn(INT32 player)
p->pflags |= PF_JUMPDOWN; p->pflags |= PF_JUMPDOWN;
p->playerstate = PST_LIVE; p->playerstate = PST_LIVE;
p->health = 1; // 0 rings p->rings = 0; // 0 rings
p->panim = PA_IDLE; // standing animation p->panim = PA_IDLE; // standing animation
if ((netgame || multiplayer) && !p->spectator) if ((netgame || multiplayer) && !p->spectator)
@ -4727,7 +4728,7 @@ void G_BeginRecording(void)
// Don't do it. // Don't do it.
WRITEFIXED(demo_p, player->jumpfactor); WRITEFIXED(demo_p, player->jumpfactor);
// Save netvar data (SONICCD, etc) // Save netvar data
CV_SaveNetVars(&demo_p); CV_SaveNetVars(&demo_p);
memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldcmd,0,sizeof(oldcmd));

View file

@ -656,6 +656,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
{ {
FOutVector v[4]; FOutVector v[4];
FSurfaceInfo Surf; FSurfaceInfo Surf;
float sdupx, sdupy;
if (w < 0 || h < 0) if (w < 0 || h < 0)
return; // consistency w/ software return; // consistency w/ software
@ -664,10 +665,16 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
// | /| // | /|
// |/ | // |/ |
// 0--1 // 0--1
v[0].x = v[3].x = (x - 160.0f)/160.0f; sdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f;
v[2].x = v[1].x = ((x+w) - 160.0f)/160.0f; sdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f;
v[0].y = v[1].y = -(y - 100.0f)/100.0f;
v[2].y = v[3].y = -((y+h) - 100.0f)/100.0f; if (color & V_NOSCALESTART)
sdupx = sdupy = 2.0f;
v[0].x = v[3].x = (x*sdupx)/vid.width - 1;
v[2].x = v[1].x = (x*sdupx + w*sdupx)/vid.width - 1;
v[0].y = v[1].y = 1-(y*sdupy)/vid.height;
v[2].y = v[3].y = 1-(y*sdupy + h*sdupy)/vid.height;
//Hurdler: do we still use this argb color? if not, we should remove it //Hurdler: do we still use this argb color? if not, we should remove it
v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //; v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //;

View file

@ -271,6 +271,9 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_TVRC &lspr[NOLIGHT], // SPR_TVRC
&lspr[NOLIGHT], // SPR_TV1K &lspr[NOLIGHT], // SPR_TV1K
&lspr[NOLIGHT], // SPR_TVTK &lspr[NOLIGHT], // SPR_TVTK
&lspr[NOLIGHT], // SPR_TVFL
&lspr[NOLIGHT], // SPR_TVBB
&lspr[NOLIGHT], // SPR_TVZP
// Projectiles // Projectiles
&lspr[NOLIGHT], // SPR_MISL &lspr[NOLIGHT], // SPR_MISL
@ -293,6 +296,7 @@ light_t *t_lspr[NUMSPRITES] =
// Techno Hill Scenery // Techno Hill Scenery
&lspr[NOLIGHT], // SPR_THZP &lspr[NOLIGHT], // SPR_THZP
&lspr[NOLIGHT], // SPR_FWR5
&lspr[REDBALL_L], // SPR_ALRM &lspr[REDBALL_L], // SPR_ALRM
// Deep Sea Scenery // Deep Sea Scenery
@ -359,18 +363,32 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_ELEM &lspr[NOLIGHT], // SPR_ELEM
&lspr[NOLIGHT], // SPR_FORC &lspr[NOLIGHT], // SPR_FORC
&lspr[NOLIGHT], // SPR_PITY &lspr[NOLIGHT], // SPR_PITY
&lspr[NOLIGHT], // SPR_FIRS
&lspr[NOLIGHT], // SPR_BUBS
&lspr[NOLIGHT], // SPR_ZAPS
&lspr[INVINCIBLE_L], // SPR_IVSP &lspr[INVINCIBLE_L], // SPR_IVSP
&lspr[SUPERSPARK_L], // SPR_SSPK &lspr[SUPERSPARK_L], // SPR_SSPK
&lspr[NOLIGHT], // SPR_GOAL &lspr[NOLIGHT], // SPR_GOAL
// Freed Animals // Flickies
&lspr[NOLIGHT], // SPR_BIRD &lspr[NOLIGHT], // SPR_FBUB
&lspr[NOLIGHT], // SPR_BUNY &lspr[NOLIGHT], // SPR_FL01
&lspr[NOLIGHT], // SPR_MOUS &lspr[NOLIGHT], // SPR_FL02
&lspr[NOLIGHT], // SPR_CHIC &lspr[NOLIGHT], // SPR_FL03
&lspr[NOLIGHT], // SPR_COWZ &lspr[NOLIGHT], // SPR_FL04
&lspr[NOLIGHT], // SPR_RBRD &lspr[NOLIGHT], // SPR_FL05
&lspr[NOLIGHT], // SPR_FL06
&lspr[NOLIGHT], // SPR_FL07
&lspr[NOLIGHT], // SPR_FL08
&lspr[NOLIGHT], // SPR_FL09
&lspr[NOLIGHT], // SPR_FL10
&lspr[NOLIGHT], // SPR_FL11
&lspr[NOLIGHT], // SPR_FL12
&lspr[NOLIGHT], // SPR_FL13
&lspr[NOLIGHT], // SPR_FL14
&lspr[NOLIGHT], // SPR_FL15
&lspr[NOLIGHT], // SPR_FL16
// Springs // Springs
&lspr[NOLIGHT], // SPR_SPRY &lspr[NOLIGHT], // SPR_SPRY

View file

@ -1558,6 +1558,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (gr_backsector) if (gr_backsector)
{ {
INT32 gr_toptexture, gr_bottomtexture;
// two sided line // two sided line
if (gr_backsector->heightsec != -1) if (gr_backsector->heightsec != -1)
{ {
@ -1608,19 +1609,22 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif #endif
} }
gr_toptexture = R_GetTextureNum(gr_sidedef->toptexture);
gr_bottomtexture = R_GetTextureNum(gr_sidedef->bottomtexture);
// check TOP TEXTURE // check TOP TEXTURE
if (( if ((
#ifdef ESLOPE #ifdef ESLOPE
worldhighslope < worldtopslope || worldhighslope < worldtopslope ||
#endif #endif
worldhigh < worldtop worldhigh < worldtop
) && texturetranslation[gr_sidedef->toptexture]) ) && gr_toptexture)
{ {
if (drawtextured) if (drawtextured)
{ {
fixed_t texturevpegtop; // top fixed_t texturevpegtop; // top
grTex = HWR_GetTexture(texturetranslation[gr_sidedef->toptexture]); grTex = HWR_GetTexture(gr_toptexture);
// PEGGING // PEGGING
if (gr_linedef->flags & ML_DONTPEGTOP) if (gr_linedef->flags & ML_DONTPEGTOP)
@ -1638,7 +1642,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
texturevpegtop += gr_sidedef->rowoffset; texturevpegtop += gr_sidedef->rowoffset;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway // This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
texturevpegtop %= SHORT(textures[texturetranslation[gr_sidedef->toptexture]]->height)<<FRACBITS; texturevpegtop %= SHORT(textures[gr_toptexture]->height)<<FRACBITS;
wallVerts[3].t = wallVerts[2].t = texturevpegtop * grTex->scaleY; wallVerts[3].t = wallVerts[2].t = texturevpegtop * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpegtop + gr_frontsector->ceilingheight - gr_backsector->ceilingheight) * grTex->scaleY; wallVerts[0].t = wallVerts[1].t = (texturevpegtop + gr_frontsector->ceilingheight - gr_backsector->ceilingheight) * grTex->scaleY;
@ -1683,9 +1687,9 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif #endif
if (gr_frontsector->numlights) if (gr_frontsector->numlights)
HWR_SplitWall(gr_frontsector, wallVerts, texturetranslation[gr_sidedef->toptexture], &Surf, FF_CUTSOLIDS); HWR_SplitWall(gr_frontsector, wallVerts, gr_toptexture, &Surf, FF_CUTSOLIDS);
else if (grTex->mipmap.flags & TF_TRANSPARENT) else if (grTex->mipmap.flags & TF_TRANSPARENT)
HWR_AddTransparentWall(wallVerts, &Surf, texturetranslation[gr_sidedef->toptexture], PF_Environment, false, lightnum, colormap); HWR_AddTransparentWall(wallVerts, &Surf, gr_toptexture, PF_Environment, false, lightnum, colormap);
else else
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap); HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
} }
@ -1695,13 +1699,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#ifdef ESLOPE #ifdef ESLOPE
worldlowslope > worldbottomslope || worldlowslope > worldbottomslope ||
#endif #endif
worldlow > worldbottom) && texturetranslation[gr_sidedef->bottomtexture]) //only if VISIBLE!!! worldlow > worldbottom) && gr_bottomtexture) //only if VISIBLE!!!
{ {
if (drawtextured) if (drawtextured)
{ {
fixed_t texturevpegbottom = 0; // bottom fixed_t texturevpegbottom = 0; // bottom
grTex = HWR_GetTexture(texturetranslation[gr_sidedef->bottomtexture]); grTex = HWR_GetTexture(gr_bottomtexture);
// PEGGING // PEGGING
#ifdef ESLOPE #ifdef ESLOPE
@ -1721,7 +1725,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
texturevpegbottom += gr_sidedef->rowoffset; texturevpegbottom += gr_sidedef->rowoffset;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway // This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
texturevpegbottom %= SHORT(textures[texturetranslation[gr_sidedef->bottomtexture]]->height)<<FRACBITS; texturevpegbottom %= SHORT(textures[gr_bottomtexture]->height)<<FRACBITS;
wallVerts[3].t = wallVerts[2].t = texturevpegbottom * grTex->scaleY; wallVerts[3].t = wallVerts[2].t = texturevpegbottom * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpegbottom + gr_backsector->floorheight - gr_frontsector->floorheight) * grTex->scaleY; wallVerts[0].t = wallVerts[1].t = (texturevpegbottom + gr_backsector->floorheight - gr_frontsector->floorheight) * grTex->scaleY;
@ -1766,13 +1770,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif #endif
if (gr_frontsector->numlights) if (gr_frontsector->numlights)
HWR_SplitWall(gr_frontsector, wallVerts, texturetranslation[gr_sidedef->bottomtexture], &Surf, FF_CUTSOLIDS); HWR_SplitWall(gr_frontsector, wallVerts, gr_bottomtexture, &Surf, FF_CUTSOLIDS);
else if (grTex->mipmap.flags & TF_TRANSPARENT) else if (grTex->mipmap.flags & TF_TRANSPARENT)
HWR_AddTransparentWall(wallVerts, &Surf, texturetranslation[gr_sidedef->bottomtexture], PF_Environment, false, lightnum, colormap); HWR_AddTransparentWall(wallVerts, &Surf, gr_bottomtexture, PF_Environment, false, lightnum, colormap);
else else
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap); HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
} }
gr_midtexture = texturetranslation[gr_sidedef->midtexture]; gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture);
if (gr_midtexture) if (gr_midtexture)
{ {
FBITFIELD blendmode; FBITFIELD blendmode;
@ -2134,7 +2138,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
else else
{ {
// Single sided line... Deal only with the middletexture (if one exists) // Single sided line... Deal only with the middletexture (if one exists)
gr_midtexture = texturetranslation[gr_sidedef->midtexture]; gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture);
if (gr_midtexture) if (gr_midtexture)
{ {
if (drawtextured) if (drawtextured)
@ -2232,13 +2236,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (*rover->topheight < lowcut || *rover->bottomheight > highcut) if (*rover->topheight < lowcut || *rover->bottomheight > highcut)
continue; continue;
texnum = texturetranslation[sides[rover->master->sidenum[0]].midtexture]; texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE) if (rover->master->flags & ML_TFERLINE)
{ {
size_t linenum = gr_curline->linedef-gr_backsector->lines[0]; size_t linenum = gr_curline->linedef-gr_backsector->lines[0];
newline = rover->master->frontsector->lines[0] + linenum; newline = rover->master->frontsector->lines[0] + linenum;
texnum = texturetranslation[sides[newline->sidenum[0]].midtexture]; texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
} }
#ifdef ESLOPE #ifdef ESLOPE
@ -2366,13 +2370,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (*rover->topheight < lowcut || *rover->bottomheight > highcut) if (*rover->topheight < lowcut || *rover->bottomheight > highcut)
continue; continue;
texnum = texturetranslation[sides[rover->master->sidenum[0]].midtexture]; texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE) if (rover->master->flags & ML_TFERLINE)
{ {
size_t linenum = gr_curline->linedef-gr_backsector->lines[0]; size_t linenum = gr_curline->linedef-gr_backsector->lines[0];
newline = rover->master->frontsector->lines[0] + linenum; newline = rover->master->frontsector->lines[0] + linenum;
texnum = texturetranslation[sides[newline->sidenum[0]].midtexture]; texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
} }
#ifdef ESLOPE //backsides #ifdef ESLOPE //backsides
h = *rover->t_slope ? P_GetZAt(*rover->t_slope, v1x, v1y) : *rover->topheight; h = *rover->t_slope ? P_GetZAt(*rover->t_slope, v1x, v1y) : *rover->topheight;
@ -4519,8 +4523,8 @@ static void HWR_SortVisSprites(void)
gr_vissprite_t *ds, *dsprev, *dsnext, *dsfirst; gr_vissprite_t *ds, *dsprev, *dsnext, *dsfirst;
gr_vissprite_t *best = NULL; gr_vissprite_t *best = NULL;
gr_vissprite_t unsorted; gr_vissprite_t unsorted;
float bestdist; float bestdist = 0.0f;
INT32 bestdispoffset; INT32 bestdispoffset = 0;
if (!gr_visspritecount) if (!gr_visspritecount)
return; return;

View file

@ -308,6 +308,23 @@ static md2_model_t *md2_readModel(const char *filename)
model->header.numSkins = 1; model->header.numSkins = 1;
#define MD2LIMITCHECK(field, max, msgname) \
if (field > max) \
{ \
CONS_Alert(CONS_ERROR, "md2_readModel: %s has too many " msgname " (# found: %d, maximum: %d)\n", filename, field, max); \
md2_freeModel (model); \
return 0; \
}
// Uncomment if these are actually needed
// MD2LIMITCHECK(model->header.numSkins, MD2_MAX_SKINS, "skins")
// MD2LIMITCHECK(model->header.numTexCoords, MD2_MAX_TEXCOORDS, "texture coordinates")
MD2LIMITCHECK(model->header.numTriangles, MD2_MAX_TRIANGLES, "triangles")
MD2LIMITCHECK(model->header.numFrames, MD2_MAX_FRAMES, "frames")
MD2LIMITCHECK(model->header.numVertices, MD2_MAX_VERTICES, "vertices")
#undef MD2LIMITCHECK
// read skins // read skins
fseek(file, model->header.offsetSkins, SEEK_SET); fseek(file, model->header.offsetSkins, SEEK_SET);
if (model->header.numSkins > 0) if (model->header.numSkins > 0)
@ -319,8 +336,6 @@ static md2_model_t *md2_readModel(const char *filename)
md2_freeModel (model); md2_freeModel (model);
return 0; return 0;
} }
;
} }
// read texture coordinates // read texture coordinates
@ -334,8 +349,6 @@ static md2_model_t *md2_readModel(const char *filename)
md2_freeModel (model); md2_freeModel (model);
return 0; return 0;
} }
} }
// read triangles // read triangles
@ -769,6 +782,7 @@ void HWR_InitMD2(void)
md2_playermodels[s].grpatch = NULL; md2_playermodels[s].grpatch = NULL;
md2_playermodels[s].skin = -1; md2_playermodels[s].skin = -1;
md2_playermodels[s].notfound = true; md2_playermodels[s].notfound = true;
md2_playermodels[s].error = false;
} }
for (i = 0; i < NUMSPRITES; i++) for (i = 0; i < NUMSPRITES; i++)
{ {
@ -777,6 +791,7 @@ void HWR_InitMD2(void)
md2_models[i].grpatch = NULL; md2_models[i].grpatch = NULL;
md2_models[i].skin = -1; md2_models[i].skin = -1;
md2_models[i].notfound = true; md2_models[i].notfound = true;
md2_models[i].error = false;
} }
// read the md2.dat file // read the md2.dat file
@ -1378,6 +1393,8 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
else else
md2 = &md2_models[spr->mobj->sprite]; md2 = &md2_models[spr->mobj->sprite];
if (md2->error)
return; // we already failed loading this before :(
if (!md2->model) if (!md2->model)
{ {
//CONS_Debug(DBG_RENDER, "Loading MD2... (%s)", sprnames[spr->mobj->sprite]); //CONS_Debug(DBG_RENDER, "Loading MD2... (%s)", sprnames[spr->mobj->sprite]);
@ -1391,6 +1408,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
else else
{ {
//CONS_Debug(DBG_RENDER, " FAILED\n"); //CONS_Debug(DBG_RENDER, " FAILED\n");
md2->error = true; // prevent endless fail
return; return;
} }
} }

View file

@ -123,6 +123,7 @@ typedef struct
void *blendgrpatch; void *blendgrpatch;
boolean notfound; boolean notfound;
INT32 skin; INT32 skin;
boolean error;
} md2_t; } md2_t;
extern md2_t md2_models[NUMSPRITES]; extern md2_t md2_models[NUMSPRITES];

View file

@ -470,7 +470,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
boolean action = false; boolean action = false;
char *ptr; char *ptr;
CONS_Debug(DBG_NETPLAY,"Recieved SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]); CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]);
target = READSINT8(*p); target = READSINT8(*p);
flags = READUINT8(*p); flags = READUINT8(*p);
@ -757,15 +757,8 @@ void HU_clearChatChars(void)
// //
boolean HU_Responder(event_t *ev) boolean HU_Responder(event_t *ev)
{ {
static boolean shiftdown = false;
UINT8 c; UINT8 c;
if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
{
shiftdown = (ev->type == ev_keydown);
return chat_on;
}
if (ev->type != ev_keydown) if (ev->type != ev_keydown)
return false; return false;
@ -797,6 +790,14 @@ boolean HU_Responder(event_t *ev)
} }
else // if chat_on else // if chat_on
{ {
// Ignore modifier keys
// Note that we do this here so users can still set
// their chat keys to one of these, if they so desire.
if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT
|| ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL
|| ev->data1 == KEY_LALT || ev->data1 == KEY_RALT)
return true;
c = (UINT8)ev->data1; c = (UINT8)ev->data1;
// use console translations // use console translations
@ -1101,7 +1102,19 @@ void HU_Drawer(void)
// draw desynch text // draw desynch text
if (hu_resynching) if (hu_resynching)
V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_YELLOWMAP, "Resynching..."); {
static UINT32 resynch_ticker = 0;
char resynch_text[14];
UINT32 i;
// Animate the dots
resynch_ticker++;
strcpy(resynch_text, "Resynching");
for (i = 0; i < (resynch_ticker / 16) % 4; i++)
strcat(resynch_text, ".");
V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_YELLOWMAP | V_ALLOWLOWERCASE, resynch_text);
}
} }
//====================================================================== //======================================================================
@ -1198,7 +1211,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
V_DrawString(x + 20, y, V_DrawString(x + 20, y,
((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
| ((players[tab[i].num].health > 0) ? 0 : V_60TRANS) | ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS)
| V_ALLOWLOWERCASE, tab[i].name); | V_ALLOWLOWERCASE, tab[i].name);
// Draw emeralds // Draw emeralds
@ -1208,7 +1221,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
HU_DrawEmeralds(x-12,y+2,tab[i].emeralds); HU_DrawEmeralds(x-12,y+2,tab[i].emeralds);
} }
if (players[tab[i].num].health <= 0) if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentPatch (x, y-4, V_80TRANS, livesback); V_DrawSmallTranslucentPatch (x, y-4, V_80TRANS, livesback);
else else
V_DrawSmallScaledPatch (x, y-4, 0, livesback); V_DrawSmallScaledPatch (x, y-4, 0, livesback);
@ -1220,7 +1233,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
V_DrawSmallScaledPatch(x, y-4, 0, superprefix[players[tab[i].num].skin]); V_DrawSmallScaledPatch(x, y-4, 0, superprefix[players[tab[i].num].skin]);
else else
{ {
if (players[tab[i].num].health <= 0) if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentPatch(x, y-4, V_80TRANS, faceprefix[players[tab[i].num].skin]); V_DrawSmallTranslucentPatch(x, y-4, V_80TRANS, faceprefix[players[tab[i].num].skin]);
else else
V_DrawSmallScaledPatch(x, y-4, 0, faceprefix[players[tab[i].num].skin]); V_DrawSmallScaledPatch(x, y-4, 0, faceprefix[players[tab[i].num].skin]);
@ -1236,7 +1249,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
else else
{ {
colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE); colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
if (players[tab[i].num].health <= 0) if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentMappedPatch (x, y-4, V_80TRANS, faceprefix[players[tab[i].num].skin], colormap); V_DrawSmallTranslucentMappedPatch (x, y-4, V_80TRANS, faceprefix[players[tab[i].num].skin], colormap);
else else
V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap); V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
@ -1244,10 +1257,10 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
} }
if (G_GametypeUsesLives()) //show lives if (G_GametypeUsesLives()) //show lives
V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%dx", players[tab[i].num].lives)); V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%dx", players[tab[i].num].lives));
else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
{ {
if (players[tab[i].num].health <= 0) if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentPatch(x-32, y-4, V_60TRANS, tagico); V_DrawSmallTranslucentPatch(x-32, y-4, V_60TRANS, tagico);
else else
V_DrawSmallScaledPatch(x-32, y-4, 0, tagico); V_DrawSmallScaledPatch(x-32, y-4, 0, tagico);
@ -1260,13 +1273,13 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
if (players[tab[i].num].exiting) if (players[tab[i].num].exiting)
V_DrawRightAlignedString(x+240, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime))); V_DrawRightAlignedString(x+240, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
else else
V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count)); V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
} }
else else
V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count))); V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
} }
else else
V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count)); V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
y += 16; y += 16;
} }
@ -1311,7 +1324,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
strlcpy(name, tab[i].name, 9); strlcpy(name, tab[i].name, 9);
V_DrawString(x + 20, y, V_DrawString(x + 20, y,
((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
| ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT) | ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT)
| V_ALLOWLOWERCASE, name); | V_ALLOWLOWERCASE, name);
if (gametype == GT_CTF) if (gametype == GT_CTF)
@ -1337,12 +1350,12 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
else else
{ {
colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE); colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
if (players[tab[i].num].health <= 0) if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap); V_DrawSmallTranslucentMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
else else
V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap); V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
} }
V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count)); V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
} }
} }
@ -1367,7 +1380,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
strlcpy(name, tab[i].name, 9); strlcpy(name, tab[i].name, 9);
V_DrawString(x + 20, y, V_DrawString(x + 20, y,
((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
| ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT) | ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT)
| V_ALLOWLOWERCASE, name); | V_ALLOWLOWERCASE, name);
if (G_GametypeUsesLives()) //show lives if (G_GametypeUsesLives()) //show lives
@ -1390,7 +1403,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
V_DrawSmallScaledPatch (x, y-4, 0, superprefix[players[tab[i].num].skin]); V_DrawSmallScaledPatch (x, y-4, 0, superprefix[players[tab[i].num].skin]);
else else
{ {
if (players[tab[i].num].health <= 0) if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin]); V_DrawSmallTranslucentPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin]);
else else
V_DrawSmallScaledPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin]); V_DrawSmallScaledPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin]);
@ -1406,7 +1419,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
else else
{ {
colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE); colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
if (players[tab[i].num].health <= 0) if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap); V_DrawSmallTranslucentMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
else else
V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap); V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
@ -1421,13 +1434,13 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
if (players[tab[i].num].exiting) if (players[tab[i].num].exiting)
V_DrawRightAlignedThinString(x+156, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime))); V_DrawRightAlignedThinString(x+156, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
else else
V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count)); V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
} }
else else
V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count))); V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
} }
else else
V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count)); V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
y += 16; y += 16;
if (y > 160) if (y > 160)

View file

@ -85,7 +85,7 @@ extern doomcom_t *doomcom;
/** \brief return packet in doomcom struct /** \brief return packet in doomcom struct
*/ */
extern void (*I_NetGet)(void); extern boolean (*I_NetGet)(void);
/** \brief ask to driver if there is data waiting /** \brief ask to driver if there is data waiting
*/ */

View file

@ -296,6 +296,14 @@ char *I_GetEnv(const char *name);
INT32 I_PutEnv(char *variable); INT32 I_PutEnv(char *variable);
/** \brief Put data in system clipboard
*/
INT32 I_ClipboardCopy(const char *data, size_t size);
/** \brief Retrieve data from system clipboard
*/
const char *I_ClipboardPaste(void);
void I_RegisterSysCommands(void); void I_RegisterSysCommands(void);
#endif #endif

View file

@ -179,6 +179,7 @@ static UINT8 UPNP_support = TRUE;
#include "i_system.h" #include "i_system.h"
#include "i_net.h" #include "i_net.h"
#include "d_net.h" #include "d_net.h"
#include "d_netfil.h"
#include "i_tcp.h" #include "i_tcp.h"
#include "m_argv.h" #include "m_argv.h"
@ -482,21 +483,12 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
return false; return false;
} }
static SINT8 getfreenode(void)
{
SINT8 j;
for (j = 0; j < MAXNETNODES; j++)
if (!nodeconnected[j])
{
nodeconnected[j] = true;
return j;
}
return -1;
}
// This is a hack. For some reason, nodes aren't being freed properly. // This is a hack. For some reason, nodes aren't being freed properly.
// This goes through and cleans up what nodes were supposed to be freed. // This goes through and cleans up what nodes were supposed to be freed.
/** \warning This function causes the file downloading to stop if someone joins.
* How? Because it removes nodes that are connected but not in game,
* which is exactly what clients downloading a file are.
*/
static void cleanupnodes(void) static void cleanupnodes(void)
{ {
SINT8 j; SINT8 j;
@ -506,13 +498,81 @@ static void cleanupnodes(void)
// Why can't I start at zero? // Why can't I start at zero?
for (j = 1; j < MAXNETNODES; j++) for (j = 1; j < MAXNETNODES; j++)
//if (!(nodeingame[j] || SV_SendingFile(j)))
if (!nodeingame[j]) if (!nodeingame[j])
nodeconnected[j] = false; nodeconnected[j] = false;
} }
static SINT8 getfreenode(void)
{
SINT8 j;
cleanupnodes();
for (j = 0; j < MAXNETNODES; j++)
if (!nodeconnected[j])
{
nodeconnected[j] = true;
return j;
}
/** \warning No free node? Just in case a node might not have been freed properly,
* look if there are connected nodes that aren't in game, and forget them.
* It's dirty, and might result in a poor guy having to restart
* downloading a needed wad, but it's better than not letting anyone join...
*/
/*I_Error("No more free nodes!!1!11!11!!1111\n");
for (j = 1; j < MAXNETNODES; j++)
if (!nodeingame[j])
return j;*/
return -1;
}
#ifdef _DEBUG
void Command_Numnodes(void)
{
INT32 connected = 0;
INT32 ingame = 0;
INT32 i;
for (i = 1; i < MAXNETNODES; i++)
{
if (!(nodeconnected[i] || nodeingame[i]))
continue;
if (nodeconnected[i])
connected++;
if (nodeingame[i])
ingame++;
CONS_Printf("%2d - ", i);
if (nodetoplayer[i] != -1)
CONS_Printf("player %.2d", nodetoplayer[i]);
else
CONS_Printf(" ");
if (nodeconnected[i])
CONS_Printf(" - connected");
else
CONS_Printf(" - ");
if (nodeingame[i])
CONS_Printf(" - ingame");
else
CONS_Printf(" - ");
CONS_Printf(" - %s\n", I_GetNodeAddress(i));
}
CONS_Printf("\n"
"Connected: %d\n"
"Ingame: %d\n",
connected, ingame);
}
#endif
#endif #endif
#ifndef NONET #ifndef NONET
static void SOCK_Get(void) // Returns true if a packet was received from a new node, false in all other cases
static boolean SOCK_Get(void)
{ {
size_t i, n; size_t i, n;
int j; int j;
@ -535,13 +595,12 @@ static void SOCK_Get(void)
doomcom->remotenode = (INT16)j; // good packet from a game player doomcom->remotenode = (INT16)j; // good packet from a game player
doomcom->datalength = (INT16)c; doomcom->datalength = (INT16)c;
nodesocket[j] = mysockets[n]; nodesocket[j] = mysockets[n];
return; return false;
} }
} }
// not found // not found
// find a free slot // find a free slot
cleanupnodes();
j = getfreenode(); j = getfreenode();
if (j > 0) if (j > 0)
{ {
@ -564,14 +623,15 @@ static void SOCK_Get(void)
} }
if (i == numbans) if (i == numbans)
SOCK_bannednode[j] = false; SOCK_bannednode[j] = false;
return; return true;
} }
else else
DEBFILE("New node detected: No more free slots\n"); DEBFILE("New node detected: No more free slots\n");
} }
} }
doomcom->remotenode = -1; // no packet doomcom->remotenode = -1; // no packet
return false;
} }
#endif #endif
@ -1256,7 +1316,6 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
gaie = I_getaddrinfo(address, port, &hints, &ai); gaie = I_getaddrinfo(address, port, &hints, &ai);
if (gaie == 0) if (gaie == 0)
{ {
cleanupnodes();
newnode = getfreenode(); newnode = getfreenode();
} }
if (newnode == -1) if (newnode == -1)

1880
src/info.c

File diff suppressed because it is too large Load diff

View file

@ -56,6 +56,9 @@ void A_BombShield(); // Obtained Bomb Shield
void A_WaterShield(); // Obtained Water Shield void A_WaterShield(); // Obtained Water Shield
void A_ForceShield(); // Obtained Force Shield void A_ForceShield(); // Obtained Force Shield
void A_PityShield(); // Obtained Pity Shield. We're... sorry. void A_PityShield(); // Obtained Pity Shield. We're... sorry.
void A_FlameShield(); // Obtained Flame Shield
void A_BubbleShield(); // Obtained Bubble Shield
void A_ThunderShield(); // Obtained Thunder Shield
void A_GravityBox(); void A_GravityBox();
void A_ScoreRise(); // Rise the score logo void A_ScoreRise(); // Rise the score logo
void A_ParticleSpawn(); void A_ParticleSpawn();
@ -211,6 +214,17 @@ void A_BrakFireShot();
void A_BrakLobShot(); void A_BrakLobShot();
void A_NapalmScatter(); void A_NapalmScatter();
void A_SpawnFreshCopy(); void A_SpawnFreshCopy();
void A_FlickySpawn();
void A_FlickyAim();
void A_FlickyFly();
void A_FlickySoar();
void A_FlickyCoast();
void A_FlickyHop();
void A_FlickyFlounder();
void A_FlickyCheck();
void A_FlickyHeightCheck();
void A_FlickyFlutter();
void A_FlameParticle();
// ratio of states to sprites to mobj types is roughly 6 : 1 : 1 // ratio of states to sprites to mobj types is roughly 6 : 1 : 1
#define NUMMOBJFREESLOTS 256 #define NUMMOBJFREESLOTS 256
@ -351,6 +365,9 @@ typedef enum sprite
SPR_TVRC, // ReCycler SPR_TVRC, // ReCycler
SPR_TV1K, // 1,000 points (1 K) SPR_TV1K, // 1,000 points (1 K)
SPR_TVTK, // 10,000 points (Ten K) SPR_TVTK, // 10,000 points (Ten K)
SPR_TVFL, // FLame shield
SPR_TVBB, // BuBble shield
SPR_TVZP, // Thunder shield (ZaP)
// Projectiles // Projectiles
SPR_MISL, SPR_MISL,
@ -372,7 +389,8 @@ typedef enum sprite
SPR_BUS2, // GFZ Bush w/o berries SPR_BUS2, // GFZ Bush w/o berries
// Techno Hill Scenery // Techno Hill Scenery
SPR_THZP, // Techno Hill Zone Plant SPR_THZP, // THZ1 Flower
SPR_FWR5, // Another flower
SPR_ALRM, // THZ2 Alarm SPR_ALRM, // THZ2 Alarm
// Deep Sea Scenery // Deep Sea Scenery
@ -435,18 +453,32 @@ typedef enum sprite
SPR_ELEM, // Elemental Shield Orb and Fire SPR_ELEM, // Elemental Shield Orb and Fire
SPR_FORC, // Force Shield Orb SPR_FORC, // Force Shield Orb
SPR_PITY, // Pity Shield Orb SPR_PITY, // Pity Shield Orb
SPR_FIRS, // Flame Shield Orb
SPR_BUBS, // Bubble Shield Orb
SPR_ZAPS, // Thunder Shield Orb
SPR_IVSP, // invincibility sparkles SPR_IVSP, // invincibility sparkles
SPR_SSPK, // Super Sonic Spark SPR_SSPK, // Super Sonic Spark
SPR_GOAL, // Special Stage goal (here because lol NiGHTS) SPR_GOAL, // Special Stage goal (here because lol NiGHTS)
// Freed Animals // Flickies
SPR_BIRD, // Birdie freed! SPR_FBUB, // Flicky-sized bubble
SPR_BUNY, // Bunny freed! SPR_FL01, // Bluebird
SPR_MOUS, // Mouse SPR_FL02, // Rabbit
SPR_CHIC, // Chicken SPR_FL03, // Chicken
SPR_COWZ, // Cow SPR_FL04, // Seal
SPR_RBRD, // Red Birdie in Bubble SPR_FL05, // Pig
SPR_FL06, // Chipmunk
SPR_FL07, // Penguin
SPR_FL08, // Fish
SPR_FL09, // Ram
SPR_FL10, // Puffin
SPR_FL11, // Cow
SPR_FL12, // Rat
SPR_FL13, // Bear
SPR_FL14, // Dove
SPR_FL15, // Cat
SPR_FL16, // Canary
// Springs // Springs
SPR_SPRY, // yellow spring SPR_SPRY, // yellow spring
@ -466,6 +498,8 @@ typedef enum sprite
SPR_SMOK, SPR_SMOK,
SPR_BUBL, // Bubble SPR_BUBL, // Bubble
SPR_WZAP, SPR_WZAP,
SPR_DUST, // Spindash dust
SPR_FPRT, // Spindash dust (flame)
SPR_TFOG, // Teleport Fog SPR_TFOG, // Teleport Fog
SPR_SEED, // Sonic CD flower seed SPR_SEED, // Sonic CD flower seed
SPR_PRTL, // Particle (for fans, etc.) SPR_PRTL, // Particle (for fans, etc.)
@ -1168,6 +1202,7 @@ typedef enum state
S_GOOP1, S_GOOP1,
S_GOOP2, S_GOOP2,
S_GOOP3, S_GOOP3,
S_GOOPTRAIL,
// Boss 3 // Boss 3
S_EGGMOBILE3_STND, S_EGGMOBILE3_STND,
@ -1723,7 +1758,7 @@ typedef enum state
S_SPIKEBALL7, S_SPIKEBALL7,
S_SPIKEBALL8, S_SPIKEBALL8,
// Fire Shield's Spawn // Elemental Shield's Spawn
S_SPINFIRE1, S_SPINFIRE1,
S_SPINFIRE2, S_SPINFIRE2,
S_SPINFIRE3, S_SPINFIRE3,
@ -1744,7 +1779,9 @@ typedef enum state
// Starpost // Starpost
S_STARPOST_IDLE, S_STARPOST_IDLE,
S_STARPOST_FLASH, S_STARPOST_FLASH,
S_STARPOST_STARTSPIN,
S_STARPOST_SPIN, S_STARPOST_SPIN,
S_STARPOST_ENDSPIN,
// Big floating mine // Big floating mine
S_BIGMINE1, S_BIGMINE1,
@ -1797,6 +1834,9 @@ typedef enum state
S_RECYCLER_BOX, S_RECYCLER_BOX,
S_SCORE1K_BOX, S_SCORE1K_BOX,
S_SCORE10K_BOX, S_SCORE10K_BOX,
S_FLAMEAURA_BOX,
S_BUBBLEWRAP_BOX,
S_THUNDERCOIN_BOX,
// Gold Repeat Monitor States (one per box) // Gold Repeat Monitor States (one per box)
S_PITY_GOLDBOX, S_PITY_GOLDBOX,
@ -1809,6 +1849,9 @@ typedef enum state
S_INVULN_GOLDBOX, S_INVULN_GOLDBOX,
S_EGGMAN_GOLDBOX, S_EGGMAN_GOLDBOX,
S_GRAVITY_GOLDBOX, S_GRAVITY_GOLDBOX,
S_FLAMEAURA_GOLDBOX,
S_BUBBLEWRAP_GOLDBOX,
S_THUNDERCOIN_GOLDBOX,
// Team Ring Boxes (these are special) // Team Ring Boxes (these are special)
S_RING_REDBOX1, S_RING_REDBOX1,
@ -1870,6 +1913,15 @@ typedef enum state
S_SCORE10K_ICON1, S_SCORE10K_ICON1,
S_SCORE10K_ICON2, S_SCORE10K_ICON2,
S_FLAMEAURA_ICON1,
S_FLAMEAURA_ICON2,
S_BUBBLEWRAP_ICON1,
S_BUBBLEWRAP_ICON2,
S_THUNDERCOIN_ICON1,
S_THUNDERCOIN_ICON2,
// --- // ---
S_ROCKET, S_ROCKET,
@ -1913,21 +1965,15 @@ typedef enum state
S_DEMONFIRE6, S_DEMONFIRE6,
S_GFZFLOWERA, S_GFZFLOWERA,
S_GFZFLOWERA2, S_GFZFLOWERB,
S_GFZFLOWERC,
S_GFZFLOWERB1,
S_GFZFLOWERB2,
S_GFZFLOWERC1,
S_BERRYBUSH, S_BERRYBUSH,
S_BUSH, S_BUSH,
// THZ Plant // THZ Plant
S_THZPLANT1, S_THZFLOWERA,
S_THZPLANT2, S_THZFLOWERB,
S_THZPLANT3,
S_THZPLANT4,
// THZ Alarm // THZ Alarm
S_ALARM1, S_ALARM1,
@ -1972,6 +2018,11 @@ typedef enum state
S_FLAME2, S_FLAME2,
S_FLAME3, S_FLAME3,
S_FLAME4, S_FLAME4,
S_FLAME5,
S_FLAME6,
S_FLAMEPARTICLE,
S_FLAMEREST,
// Eggman Statue // Eggman Statue
S_EGGSTATUE1, S_EGGSTATUE1,
@ -2033,36 +2084,13 @@ typedef enum state
// Spinning flame jets // Spinning flame jets
S_FJSPINAXISA1, // Counter-clockwise S_FJSPINAXISA1, // Counter-clockwise
S_FJSPINAXISA2, S_FJSPINAXISA2,
S_FJSPINAXISA3,
S_FJSPINAXISA4,
S_FJSPINAXISA5,
S_FJSPINAXISA6,
S_FJSPINAXISA7,
S_FJSPINAXISA8,
S_FJSPINAXISA9,
S_FJSPINHELPERA1,
S_FJSPINHELPERA2,
S_FJSPINHELPERA3,
S_FJSPINAXISB1, // Clockwise S_FJSPINAXISB1, // Clockwise
S_FJSPINAXISB2, S_FJSPINAXISB2,
S_FJSPINAXISB3,
S_FJSPINAXISB4,
S_FJSPINAXISB5,
S_FJSPINAXISB6,
S_FJSPINAXISB7,
S_FJSPINAXISB8,
S_FJSPINAXISB9,
S_FJSPINHELPERB1,
S_FJSPINHELPERB2,
S_FJSPINHELPERB3,
// Blade's flame // Blade's flame
S_FLAMEJETFLAMEB1, S_FLAMEJETFLAMEB1,
S_FLAMEJETFLAMEB2, S_FLAMEJETFLAMEB2,
S_FLAMEJETFLAMEB3, S_FLAMEJETFLAMEB3,
S_FLAMEJETFLAMEB4,
S_FLAMEJETFLAMEB5,
S_FLAMEJETFLAMEB6,
// Trapgoyles // Trapgoyles
S_TRAPGOYLE, S_TRAPGOYLE,
@ -2157,8 +2185,10 @@ typedef enum state
S_BSZVINE_ORANGE, S_BSZVINE_ORANGE,
S_BSZSHRUB, S_BSZSHRUB,
S_BSZCLOVER, S_BSZCLOVER,
S_BSZFISH, S_BIG_PALMTREE_TRUNK,
S_BSZSUNFLOWER, S_BIG_PALMTREE_TOP,
S_PALMTREE_TRUNK,
S_PALMTREE_TOP,
S_DBALL1, S_DBALL1,
S_DBALL2, S_DBALL2,
@ -2241,6 +2271,7 @@ typedef enum state
S_MAGN10, S_MAGN10,
S_MAGN11, S_MAGN11,
S_MAGN12, S_MAGN12,
S_MAGN13,
S_FORC1, S_FORC1,
S_FORC2, S_FORC2,
@ -2264,6 +2295,8 @@ typedef enum state
S_FORC19, S_FORC19,
S_FORC20, S_FORC20,
S_FORC21,
S_ELEM1, S_ELEM1,
S_ELEM2, S_ELEM2,
S_ELEM3, S_ELEM3,
@ -2277,6 +2310,9 @@ typedef enum state
S_ELEM11, S_ELEM11,
S_ELEM12, S_ELEM12,
S_ELEM13,
S_ELEM14,
S_ELEMF1, S_ELEMF1,
S_ELEMF2, S_ELEMF2,
S_ELEMF3, S_ELEMF3,
@ -2285,6 +2321,8 @@ typedef enum state
S_ELEMF6, S_ELEMF6,
S_ELEMF7, S_ELEMF7,
S_ELEMF8, S_ELEMF8,
S_ELEMF9,
S_ELEMF10,
S_PITY1, S_PITY1,
S_PITY2, S_PITY2,
@ -2297,6 +2335,84 @@ typedef enum state
S_PITY9, S_PITY9,
S_PITY10, S_PITY10,
S_FIRS1,
S_FIRS2,
S_FIRS3,
S_FIRS4,
S_FIRS5,
S_FIRS6,
S_FIRS7,
S_FIRS8,
S_FIRS9,
S_FIRS10,
S_FIRS11,
S_FIRSB1,
S_FIRSB2,
S_FIRSB3,
S_FIRSB4,
S_FIRSB5,
S_FIRSB6,
S_FIRSB7,
S_FIRSB8,
S_FIRSB9,
S_FIRSB10,
S_BUBS1,
S_BUBS2,
S_BUBS3,
S_BUBS4,
S_BUBS5,
S_BUBS6,
S_BUBS7,
S_BUBS8,
S_BUBS9,
S_BUBS10,
S_BUBS11,
S_BUBSB1,
S_BUBSB2,
S_BUBSB3,
S_BUBSB4,
S_BUBSB5,
S_BUBSB6,
S_ZAPS1,
S_ZAPS2,
S_ZAPS3,
S_ZAPS4,
S_ZAPS5,
S_ZAPS6,
S_ZAPS7,
S_ZAPS8,
S_ZAPS9,
S_ZAPS10,
S_ZAPS11,
S_ZAPS12,
S_ZAPS13, // blank frame
S_ZAPS14,
S_ZAPS15,
S_ZAPS16,
S_ZAPSB1, // blank frame
S_ZAPSB2,
S_ZAPSB3,
S_ZAPSB4,
S_ZAPSB5,
S_ZAPSB6,
S_ZAPSB7,
S_ZAPSB8,
S_ZAPSB9,
S_ZAPSB10,
S_ZAPSB11, // blank frame
//Thunder spark
S_THUNDERCOIN_SPARK,
// Invincibility Sparkles // Invincibility Sparkles
S_IVSP, S_IVSP,
@ -2307,43 +2423,133 @@ typedef enum state
S_SSPK4, S_SSPK4,
S_SSPK5, S_SSPK5,
// Freed Birdie // Flicky-sized bubble
S_BIRD1, S_FLICKY_BUBBLE,
S_BIRD2,
S_BIRD3,
// Freed Bunny // Bluebird
S_BUNNY1, S_FLICKY_01_OUT,
S_BUNNY2, S_FLICKY_01_FLAP1,
S_BUNNY3, S_FLICKY_01_FLAP2,
S_BUNNY4, S_FLICKY_01_FLAP3,
S_BUNNY5,
S_BUNNY6,
S_BUNNY7,
S_BUNNY8,
S_BUNNY9,
S_BUNNY10,
// Freed Mouse // Rabbit
S_MOUSE1, S_FLICKY_02_OUT,
S_MOUSE2, S_FLICKY_02_AIM,
S_FLICKY_02_HOP,
S_FLICKY_02_UP,
S_FLICKY_02_DOWN,
// Freed Chicken // Chicken
S_CHICKEN1, S_FLICKY_03_OUT,
S_CHICKENHOP, S_FLICKY_03_AIM,
S_CHICKENFLY1, S_FLICKY_03_HOP,
S_CHICKENFLY2, S_FLICKY_03_UP,
S_FLICKY_03_FLAP1,
S_FLICKY_03_FLAP2,
// Freed Cow // Seal
S_COW1, S_FLICKY_04_OUT,
S_COW2, S_FLICKY_04_AIM,
S_COW3, S_FLICKY_04_HOP,
S_COW4, S_FLICKY_04_UP,
S_FLICKY_04_DOWN,
S_FLICKY_04_SWIM1,
S_FLICKY_04_SWIM2,
S_FLICKY_04_SWIM3,
S_FLICKY_04_SWIM4,
// Red Birdie in Bubble // Pig
S_RBIRD1, S_FLICKY_05_OUT,
S_RBIRD2, S_FLICKY_05_AIM,
S_RBIRD3, S_FLICKY_05_HOP,
S_FLICKY_05_UP,
S_FLICKY_05_DOWN,
// Chipmunk
S_FLICKY_06_OUT,
S_FLICKY_06_AIM,
S_FLICKY_06_HOP,
S_FLICKY_06_UP,
S_FLICKY_06_DOWN,
// Penguin
S_FLICKY_07_OUT,
S_FLICKY_07_AIML,
S_FLICKY_07_HOPL,
S_FLICKY_07_UPL,
S_FLICKY_07_DOWNL,
S_FLICKY_07_AIMR,
S_FLICKY_07_HOPR,
S_FLICKY_07_UPR,
S_FLICKY_07_DOWNR,
S_FLICKY_07_SWIM1,
S_FLICKY_07_SWIM2,
S_FLICKY_07_SWIM3,
// Fish
S_FLICKY_08_OUT,
S_FLICKY_08_AIM,
S_FLICKY_08_HOP,
S_FLICKY_08_FLAP1,
S_FLICKY_08_FLAP2,
S_FLICKY_08_FLAP3,
S_FLICKY_08_FLAP4,
S_FLICKY_08_SWIM1,
S_FLICKY_08_SWIM2,
S_FLICKY_08_SWIM3,
S_FLICKY_08_SWIM4,
// Ram
S_FLICKY_09_OUT,
S_FLICKY_09_AIM,
S_FLICKY_09_HOP,
S_FLICKY_09_UP,
S_FLICKY_09_DOWN,
// Puffin
S_FLICKY_10_OUT,
S_FLICKY_10_FLAP1,
S_FLICKY_10_FLAP2,
// Cow
S_FLICKY_11_OUT,
S_FLICKY_11_AIM,
S_FLICKY_11_RUN1,
S_FLICKY_11_RUN2,
S_FLICKY_11_RUN3,
// Rat
S_FLICKY_12_OUT,
S_FLICKY_12_AIM,
S_FLICKY_12_RUN1,
S_FLICKY_12_RUN2,
S_FLICKY_12_RUN3,
// Bear
S_FLICKY_13_OUT,
S_FLICKY_13_AIM,
S_FLICKY_13_HOP,
S_FLICKY_13_UP,
S_FLICKY_13_DOWN,
// Dove
S_FLICKY_14_OUT,
S_FLICKY_14_FLAP1,
S_FLICKY_14_FLAP2,
S_FLICKY_14_FLAP3,
// Cat
S_FLICKY_15_OUT,
S_FLICKY_15_AIM,
S_FLICKY_15_HOP,
S_FLICKY_15_UP,
S_FLICKY_15_DOWN,
// Canary
S_FLICKY_16_OUT,
S_FLICKY_16_FLAP1,
S_FLICKY_16_FLAP2,
S_FLICKY_16_FLAP3,
S_YELLOWSPRING, S_YELLOWSPRING,
S_YELLOWSPRING2, S_YELLOWSPRING2,
@ -2457,6 +2663,20 @@ typedef enum state
S_WATERZAP, S_WATERZAP,
// Spindash dust
S_SPINDUST1,
S_SPINDUST2,
S_SPINDUST3,
S_SPINDUST4,
S_SPINDUST_BUBBLE1,
S_SPINDUST_BUBBLE2,
S_SPINDUST_BUBBLE3,
S_SPINDUST_BUBBLE4,
S_SPINDUST_FIRE1,
S_SPINDUST_FIRE2,
S_SPINDUST_FIRE3,
S_SPINDUST_FIRE4,
S_FOG1, S_FOG1,
S_FOG2, S_FOG2,
S_FOG3, S_FOG3,
@ -2687,20 +2907,19 @@ typedef enum state
S_FIREBALLEXP2, S_FIREBALLEXP2,
S_FIREBALLEXP3, S_FIREBALLEXP3,
S_SHELL, S_SHELL,
S_SHELL1, S_PUMA_START1,
S_SHELL2, S_PUMA_START2,
S_SHELL3, S_PUMA_UP1,
S_SHELL4, S_PUMA_UP2,
S_PUMA1, S_PUMA_UP3,
S_PUMA2, S_PUMA_DOWN1,
S_PUMA3, S_PUMA_DOWN2,
S_PUMA4, S_PUMA_DOWN3,
S_PUMA5, S_PUMATRAIL1,
S_PUMA6, S_PUMATRAIL2,
S_HAMMER1, S_PUMATRAIL3,
S_HAMMER2, S_PUMATRAIL4,
S_HAMMER3, S_HAMMER,
S_HAMMER4,
S_KOOPA1, S_KOOPA1,
S_KOOPA2, S_KOOPA2,
S_KOOPAFLAME1, S_KOOPAFLAME1,
@ -2829,6 +3048,7 @@ typedef enum state
S_NIGHTOPIANHELPER6, S_NIGHTOPIANHELPER6,
S_NIGHTOPIANHELPER7, S_NIGHTOPIANHELPER7,
S_NIGHTOPIANHELPER8, S_NIGHTOPIANHELPER8,
S_NIGHTOPIANHELPER9,
S_CRUMBLE1, S_CRUMBLE1,
S_CRUMBLE2, S_CRUMBLE2,
@ -2852,10 +3072,10 @@ typedef enum state
S_SPRK16, S_SPRK16,
// Robot Explosion // Robot Explosion
S_XPLD_FLICKY,
S_XPLD1, S_XPLD1,
S_XPLD2, S_XPLD2,
S_XPLD3, S_XPLD_EGGTRAP,
S_XPLD4,
// Underwater Explosion // Underwater Explosion
S_WPLD1, S_WPLD1,
@ -2971,6 +3191,7 @@ typedef enum mobj_type
MT_BOSSTANK2, MT_BOSSTANK2,
MT_BOSSSPIGOT, MT_BOSSSPIGOT,
MT_GOOP, MT_GOOP,
MT_GOOPTRAIL,
// Boss 3 // Boss 3
MT_EGGMOBILE3, MT_EGGMOBILE3,
@ -3074,6 +3295,9 @@ typedef enum mobj_type
MT_RECYCLER_BOX, MT_RECYCLER_BOX,
MT_SCORE1K_BOX, MT_SCORE1K_BOX,
MT_SCORE10K_BOX, MT_SCORE10K_BOX,
MT_FLAMEAURA_BOX,
MT_BUBBLEWRAP_BOX,
MT_THUNDERCOIN_BOX,
// Monitor boxes -- repeating (big) boxes // Monitor boxes -- repeating (big) boxes
MT_PITY_GOLDBOX, MT_PITY_GOLDBOX,
@ -3086,6 +3310,9 @@ typedef enum mobj_type
MT_INVULN_GOLDBOX, MT_INVULN_GOLDBOX,
MT_EGGMAN_GOLDBOX, MT_EGGMAN_GOLDBOX,
MT_GRAVITY_GOLDBOX, MT_GRAVITY_GOLDBOX,
MT_FLAMEAURA_GOLDBOX,
MT_BUBBLEWRAP_GOLDBOX,
MT_THUNDERCOIN_GOLDBOX,
// Monitor boxes -- special // Monitor boxes -- special
MT_RING_REDBOX, MT_RING_REDBOX,
@ -3108,6 +3335,9 @@ typedef enum mobj_type
MT_RECYCLER_ICON, MT_RECYCLER_ICON,
MT_SCORE1K_ICON, MT_SCORE1K_ICON,
MT_SCORE10K_ICON, MT_SCORE10K_ICON,
MT_FLAMEAURA_ICON,
MT_BUBBLEWRAP_ICON,
MT_THUNDERCOIN_ICON,
// Projectiles // Projectiles
MT_ROCKET, MT_ROCKET,
@ -3131,7 +3361,8 @@ typedef enum mobj_type
MT_BUSH, MT_BUSH,
// Techno Hill Scenery // Techno Hill Scenery
MT_THZPLANT, // THZ Plant MT_THZFLOWER1,
MT_THZFLOWER2,
MT_ALARM, MT_ALARM,
// Deep Sea Scenery // Deep Sea Scenery
@ -3147,6 +3378,7 @@ typedef enum mobj_type
// Castle Eggman Scenery // Castle Eggman Scenery
MT_CHAIN, // CEZ Chain MT_CHAIN, // CEZ Chain
MT_FLAME, // Flame (has corona) MT_FLAME, // Flame (has corona)
MT_FLAMEPARTICLE,
MT_EGGSTATUE, // Eggman Statue MT_EGGSTATUE, // Eggman Statue
MT_MACEPOINT, // Mace rotation point MT_MACEPOINT, // Mace rotation point
MT_SWINGMACEPOINT, // Mace swinging point MT_SWINGMACEPOINT, // Mace swinging point
@ -3173,9 +3405,7 @@ typedef enum mobj_type
MT_FLAMEJETFLAME, MT_FLAMEJETFLAME,
MT_FJSPINAXISA, // Counter-clockwise MT_FJSPINAXISA, // Counter-clockwise
MT_FJSPINHELPERA,
MT_FJSPINAXISB, // Clockwise MT_FJSPINAXISB, // Clockwise
MT_FJSPINHELPERB,
MT_FLAMEJETFLAMEB, // Blade's flame MT_FLAMEJETFLAMEB, // Blade's flame
@ -3252,30 +3482,46 @@ typedef enum mobj_type
MT_BSZVINE_ORANGE, MT_BSZVINE_ORANGE,
MT_BSZSHRUB, MT_BSZSHRUB,
MT_BSZCLOVER, MT_BSZCLOVER,
MT_BSZFISH, MT_BIG_PALMTREE_TRUNK,
MT_BSZSUNFLOWER, MT_BIG_PALMTREE_TOP,
MT_PALMTREE_TRUNK,
MT_PALMTREE_TOP,
// Misc scenery // Misc scenery
MT_DBALL, MT_DBALL,
MT_EGGSTATUE2, MT_EGGSTATUE2,
// Powerup Indicators // Powerup Indicators
MT_GREENORB, // Elemental shield mobj MT_ELEMENTAL_ORB, // Elemental shield mobj
MT_YELLOWORB, // Attract shield mobj MT_ATTRACT_ORB, // Attract shield mobj
MT_BLUEORB, // Force shield mobj MT_FORCE_ORB, // Force shield mobj
MT_BLACKORB, // Armageddon shield mobj MT_ARMAGEDDON_ORB, // Armageddon shield mobj
MT_WHITEORB, // Whirlwind shield mobj MT_WHIRLWIND_ORB, // Whirlwind shield mobj
MT_PITYORB, // Pity shield mobj MT_PITY_ORB, // Pity shield mobj
MT_IVSP, // invincibility sparkles MT_FLAMEAURA_ORB, // Flame shield mobj
MT_BUBBLEWRAP_ORB, // Bubble shield mobj
MT_THUNDERCOIN_ORB, // Thunder shield mobj
MT_THUNDERCOIN_SPARK, // Thunder spark
MT_IVSP, // Invincibility sparkles
MT_SUPERSPARK, // Super Sonic Spark MT_SUPERSPARK, // Super Sonic Spark
// Freed Animals // Flickies
MT_BIRD, // Birdie freed! MT_FLICKY_01, // Bluebird
MT_BUNNY, // Bunny freed! MT_FLICKY_02, // Rabbit
MT_MOUSE, // Mouse MT_FLICKY_03, // Chicken
MT_CHICKEN, // Chicken MT_FLICKY_04, // Seal
MT_COW, // Cow MT_FLICKY_05, // Pig
MT_REDBIRD, // Red Birdie in Bubble MT_FLICKY_06, // Chipmunk
MT_FLICKY_07, // Penguin
MT_FLICKY_08, // Fish
MT_FLICKY_09, // Ram
MT_FLICKY_10, // Puffin
MT_FLICKY_11, // Cow
MT_FLICKY_12, // Rat
MT_FLICKY_13, // Bear
MT_FLICKY_14, // Dove
MT_FLICKY_15, // Cat
MT_FLICKY_16, // Canary
// Environmental Effects // Environmental Effects
MT_RAIN, // Rain MT_RAIN, // Rain
@ -3286,6 +3532,7 @@ typedef enum mobj_type
MT_MEDIUMBUBBLE, // medium bubble MT_MEDIUMBUBBLE, // medium bubble
MT_EXTRALARGEBUBBLE, // extra large bubble MT_EXTRALARGEBUBBLE, // extra large bubble
MT_WATERZAP, MT_WATERZAP,
MT_SPINDUST, // Spindash dust
MT_TFOG, MT_TFOG,
MT_SEED, MT_SEED,
MT_PARTICLE, MT_PARTICLE,
@ -3344,6 +3591,7 @@ typedef enum mobj_type
MT_FIREBALL, MT_FIREBALL,
MT_SHELL, MT_SHELL,
MT_PUMA, MT_PUMA,
MT_PUMATRAIL,
MT_HAMMER, MT_HAMMER,
MT_KOOPA, MT_KOOPA,
MT_KOOPAFLAME, MT_KOOPAFLAME,

View file

@ -27,7 +27,6 @@
#define NOHUD if (hud_running) return luaL_error(L, "HUD rendering code should not call this function!"); #define NOHUD if (hud_running) return luaL_error(L, "HUD rendering code should not call this function!");
boolean luaL_checkboolean(lua_State *L, int narg) { boolean luaL_checkboolean(lua_State *L, int narg) {
luaL_checktype(L, narg, LUA_TBOOLEAN); luaL_checktype(L, narg, LUA_TBOOLEAN);
return lua_toboolean(L, narg); return lua_toboolean(L, narg);
@ -205,6 +204,41 @@ static int lib_pClosestPointOnLine(lua_State *L)
return 2; return 2;
} }
static int lib_pPointOnLineSide(lua_State *L)
{
int n = lua_gettop(L);
fixed_t x = luaL_checkfixed(L, 1);
fixed_t y = luaL_checkfixed(L, 2);
//HUDSAFE
if (lua_isuserdata(L, 3)) // use a real linedef to get our points
{
line_t *line = *((line_t **)luaL_checkudata(L, 3, META_LINE));
if (!line)
return LUA_ErrInvalid(L, "line_t");
lua_pushinteger(L, P_PointOnLineSide(x, y, line));
}
else // use custom coordinates of our own!
{
vertex_t v1, v2; // fake vertexes
line_t junk; // fake linedef
if (n < 6)
return luaL_error(L, "arguments 3 to 6 not all given (expected 4 fixed-point integers)");
v1.x = luaL_checkfixed(L, 3);
v1.y = luaL_checkfixed(L, 4);
v2.x = luaL_checkfixed(L, 5);
v2.y = luaL_checkfixed(L, 6);
junk.v1 = &v1;
junk.v2 = &v2;
junk.dx = v2.x - v1.x;
junk.dy = v2.y - v1.y;
lua_pushinteger(L, P_PointOnLineSide(x, y, &junk));
}
return 1;
}
// P_ENEMY // P_ENEMY
///////////// /////////////
@ -309,6 +343,19 @@ static int lib_pRemoveMobj(lua_State *L)
return 0; return 0;
} }
// P_IsValidSprite2 technically doesn't exist, and probably never should... but too much would need to be exposed to allow this to be checked by other methods.
static int lib_pIsValidSprite2(lua_State *L)
{
mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
UINT8 spr2 = (UINT8)luaL_checkinteger(L, 2);
//HUDSAFE
if (!mobj)
return LUA_ErrInvalid(L, "mobj_t");
lua_pushboolean(L, (mobj->skin && (((skin_t *)mobj->skin)->sprites[spr2].numframes > 0)));
return 1;
}
static int lib_pSpawnMissile(lua_State *L) static int lib_pSpawnMissile(lua_State *L)
{ {
mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@ -618,6 +665,17 @@ static int lib_pAddPlayerScore(lua_State *L)
return 0; return 0;
} }
static int lib_pStealPlayerScore(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
UINT32 amount = (UINT32)luaL_checkinteger(L, 2);
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
P_StealPlayerScore(player, amount);
return 0;
}
static int lib_pPlayerInPain(lua_State *L) static int lib_pPlayerInPain(lua_State *L)
{ {
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@ -777,6 +835,16 @@ static int lib_pDoJumpShield(lua_State *L)
return 0; return 0;
} }
static int lib_pDoBubbleBounce(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
P_DoBubbleBounce(player);
return 0;
}
static int lib_pBlackOw(lua_State *L) static int lib_pBlackOw(lua_State *L)
{ {
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@ -787,13 +855,14 @@ static int lib_pBlackOw(lua_State *L)
return 0; return 0;
} }
static int lib_pElementalFireTrail(lua_State *L) static int lib_pElementalFire(lua_State *L)
{ {
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
boolean cropcircle = lua_optboolean(L, 2);
NOHUD NOHUD
if (!player) if (!player)
return LUA_ErrInvalid(L, "player_t"); return LUA_ErrInvalid(L, "player_t");
P_ElementalFireTrail(player); P_ElementalFire(player, cropcircle);
return 0; return 0;
} }
@ -848,10 +917,11 @@ static int lib_pReturnThrustY(lua_State *L)
static int lib_pLookForEnemies(lua_State *L) static int lib_pLookForEnemies(lua_State *L)
{ {
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
boolean nonenemies = lua_opttrueboolean(L, 2);
NOHUD NOHUD
if (!player) if (!player)
return LUA_ErrInvalid(L, "player_t"); return LUA_ErrInvalid(L, "player_t");
lua_pushboolean(L, P_LookForEnemies(player)); lua_pushboolean(L, P_LookForEnemies(player, nonenemies));
return 1; return 1;
} }
@ -1132,7 +1202,7 @@ static int lib_pPlayerRingBurst(lua_State *L)
if (!player) if (!player)
return LUA_ErrInvalid(L, "player_t"); return LUA_ErrInvalid(L, "player_t");
if (num_rings == -1) if (num_rings == -1)
num_rings = player->health - 1; num_rings = player->rings;
P_PlayerRingBurst(player, num_rings); P_PlayerRingBurst(player, num_rings);
return 0; return 0;
} }
@ -1157,6 +1227,16 @@ static int lib_pPlayerWeaponAmmoBurst(lua_State *L)
return 0; return 0;
} }
static int lib_pPlayerWeaponPanelOrAmmoBurst(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
P_PlayerWeaponPanelOrAmmoBurst(player);
return 0;
}
static int lib_pPlayerEmeraldBurst(lua_State *L) static int lib_pPlayerEmeraldBurst(lua_State *L)
{ {
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@ -1264,6 +1344,16 @@ static int lib_pDoNightsScore(lua_State *L)
return 0; return 0;
} }
static int lib_pDoMatchSuper(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
P_DoMatchSuper(player);
return 0;
}
// P_SPEC // P_SPEC
//////////// ////////////
@ -2018,6 +2108,7 @@ static luaL_Reg lib[] = {
// p_maputil // p_maputil
{"P_AproxDistance",lib_pAproxDistance}, {"P_AproxDistance",lib_pAproxDistance},
{"P_ClosestPointOnLine",lib_pClosestPointOnLine}, {"P_ClosestPointOnLine",lib_pClosestPointOnLine},
{"P_PointOnLineSide",lib_pPointOnLineSide},
// p_enemy // p_enemy
{"P_CheckMeleeRange", lib_pCheckMeleeRange}, {"P_CheckMeleeRange", lib_pCheckMeleeRange},
@ -2032,6 +2123,7 @@ static luaL_Reg lib[] = {
// don't add P_SetMobjState or P_SetPlayerMobjState, use "mobj.state = S_NEWSTATE" instead. // don't add P_SetMobjState or P_SetPlayerMobjState, use "mobj.state = S_NEWSTATE" instead.
{"P_SpawnMobj",lib_pSpawnMobj}, {"P_SpawnMobj",lib_pSpawnMobj},
{"P_RemoveMobj",lib_pRemoveMobj}, {"P_RemoveMobj",lib_pRemoveMobj},
{"P_IsValidSprite2", lib_pIsValidSprite2},
{"P_SpawnMissile",lib_pSpawnMissile}, {"P_SpawnMissile",lib_pSpawnMissile},
{"P_SpawnXYZMissile",lib_pSpawnXYZMissile}, {"P_SpawnXYZMissile",lib_pSpawnXYZMissile},
{"P_SpawnPointMissile",lib_pSpawnPointMissile}, {"P_SpawnPointMissile",lib_pSpawnPointMissile},
@ -2058,6 +2150,7 @@ static luaL_Reg lib[] = {
{"P_GetPlayerSpinHeight",lib_pGetPlayerSpinHeight}, {"P_GetPlayerSpinHeight",lib_pGetPlayerSpinHeight},
{"P_GetPlayerControlDirection",lib_pGetPlayerControlDirection}, {"P_GetPlayerControlDirection",lib_pGetPlayerControlDirection},
{"P_AddPlayerScore",lib_pAddPlayerScore}, {"P_AddPlayerScore",lib_pAddPlayerScore},
{"P_StealPlayerScore",lib_pStealPlayerScore},
{"P_PlayerInPain",lib_pPlayerInPain}, {"P_PlayerInPain",lib_pPlayerInPain},
{"P_DoPlayerPain",lib_pDoPlayerPain}, {"P_DoPlayerPain",lib_pDoPlayerPain},
{"P_ResetPlayer",lib_pResetPlayer}, {"P_ResetPlayer",lib_pResetPlayer},
@ -2073,8 +2166,9 @@ static luaL_Reg lib[] = {
{"P_GivePlayerLives",lib_pGivePlayerLives}, {"P_GivePlayerLives",lib_pGivePlayerLives},
{"P_ResetScore",lib_pResetScore}, {"P_ResetScore",lib_pResetScore},
{"P_DoJumpShield",lib_pDoJumpShield}, {"P_DoJumpShield",lib_pDoJumpShield},
{"P_DoBubbleBounce",lib_pDoBubbleBounce},
{"P_BlackOw",lib_pBlackOw}, {"P_BlackOw",lib_pBlackOw},
{"P_ElementalFireTrail",lib_pElementalFireTrail}, {"P_ElementalFire",lib_pElementalFire},
{"P_DoPlayerExit",lib_pDoPlayerExit}, {"P_DoPlayerExit",lib_pDoPlayerExit},
{"P_InstaThrust",lib_pInstaThrust}, {"P_InstaThrust",lib_pInstaThrust},
{"P_ReturnThrustX",lib_pReturnThrustX}, {"P_ReturnThrustX",lib_pReturnThrustX},
@ -2108,6 +2202,7 @@ static luaL_Reg lib[] = {
{"P_PlayerRingBurst",lib_pPlayerRingBurst}, {"P_PlayerRingBurst",lib_pPlayerRingBurst},
{"P_PlayerWeaponPanelBurst",lib_pPlayerWeaponPanelBurst}, {"P_PlayerWeaponPanelBurst",lib_pPlayerWeaponPanelBurst},
{"P_PlayerWeaponAmmoBurst",lib_pPlayerWeaponAmmoBurst}, {"P_PlayerWeaponAmmoBurst",lib_pPlayerWeaponAmmoBurst},
{"P_PlayerWeaponPanelOrAmmoBurst", lib_pPlayerWeaponPanelOrAmmoBurst},
{"P_PlayerEmeraldBurst",lib_pPlayerEmeraldBurst}, {"P_PlayerEmeraldBurst",lib_pPlayerEmeraldBurst},
{"P_PlayerFlagBurst",lib_pPlayerFlagBurst}, {"P_PlayerFlagBurst",lib_pPlayerFlagBurst},
{"P_PlayRinglossSound",lib_pPlayRinglossSound}, {"P_PlayRinglossSound",lib_pPlayRinglossSound},
@ -2116,6 +2211,7 @@ static luaL_Reg lib[] = {
{"P_PlayLivesJingle",lib_pPlayLivesJingle}, {"P_PlayLivesJingle",lib_pPlayLivesJingle},
{"P_CanPickupItem",lib_pCanPickupItem}, {"P_CanPickupItem",lib_pCanPickupItem},
{"P_DoNightsScore",lib_pDoNightsScore}, {"P_DoNightsScore",lib_pDoNightsScore},
{"P_DoMatchSuper",lib_pDoMatchSuper},
// p_spec // p_spec
{"P_Thrust",lib_pThrust}, {"P_Thrust",lib_pThrust},

266
src/lua_blockmaplib.c Normal file
View file

@ -0,0 +1,266 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2016 by Iestyn "Monster Iestyn" Jealous.
// Copyright (C) 2016 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 lua_blockmaplib.c
/// \brief blockmap library for Lua scripting
#include "doomdef.h"
#ifdef HAVE_BLUA
#include "p_local.h"
#include "r_main.h" // validcount
#include "lua_script.h"
#include "lua_libs.h"
//#include "lua_hud.h" // hud_running errors
static const char *const search_opt[] = {
"objects",
"lines",
NULL};
// a quickly-made function pointer typedef used by lib_searchBlockmap...
// return values:
// 0 - normal, no interruptions
// 1 - stop search through current block
// 2 - stop search completely
typedef UINT8 (*blockmap_func)(lua_State *, INT32, INT32, mobj_t *);
static boolean blockfuncerror = false; // errors should only print once per search blockmap call
// Helper function for "objects" search
static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *thing)
{
mobj_t *mobj, *bnext = NULL;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
return 0;
// Check interaction with the objects in the blockmap.
for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext)
{
P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
if (mobj == thing)
continue; // our thing just found itself, so move on
lua_pushvalue(L, 1); // push function
LUA_PushUserdata(L, thing, META_MOBJ);
LUA_PushUserdata(L, mobj, META_MOBJ);
if (lua_pcall(gL, 2, 1, 0)) {
if (!blockfuncerror || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
blockfuncerror = true;
return 0; // *shrugs*
}
if (!lua_isnil(gL, -1))
{ // if nil, continue
if (lua_toboolean(gL, -1))
return 2; // stop whole search
else
return 1; // stop block search
}
lua_pop(gL, 1);
if (P_MobjWasRemoved(thing) // func just popped our thing, cannot continue.
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
{
P_SetTarget(&bnext, NULL);
return (P_MobjWasRemoved(thing)) ? 2 : 1;
}
}
return 0;
}
// Helper function for "lines" search
static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *thing)
{
INT32 offset;
const INT32 *list; // Big blockmap
#ifdef POLYOBJECTS
polymaplink_t *plink; // haleyjd 02/22/06
#endif
line_t *ld;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
return 0;
offset = y*bmapwidth + x;
#ifdef POLYOBJECTS
// haleyjd 02/22/06: consider polyobject lines
plink = polyblocklinks[offset];
while (plink)
{
polyobj_t *po = plink->po;
if (po->validcount != validcount) // if polyobj hasn't been checked
{
size_t i;
po->validcount = validcount;
for (i = 0; i < po->numLines; ++i)
{
if (po->lines[i]->validcount == validcount) // line has been checked
continue;
po->lines[i]->validcount = validcount;
lua_pushvalue(L, 1);
LUA_PushUserdata(L, thing, META_MOBJ);
LUA_PushUserdata(L, po->lines[i], META_LINE);
if (lua_pcall(gL, 2, 1, 0)) {
if (!blockfuncerror || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
blockfuncerror = true;
return 0; // *shrugs*
}
if (!lua_isnil(gL, -1))
{ // if nil, continue
if (lua_toboolean(gL, -1))
return 2; // stop whole search
else
return 1; // stop block search
}
lua_pop(gL, 1);
if (P_MobjWasRemoved(thing))
return 2;
}
}
plink = (polymaplink_t *)(plink->link.next);
}
#endif
offset = *(blockmap + offset); // offset = blockmap[y*bmapwidth+x];
// First index is really empty, so +1 it.
for (list = blockmaplump + offset + 1; *list != -1; list++)
{
ld = &lines[*list];
if (ld->validcount == validcount)
continue; // Line has already been checked.
ld->validcount = validcount;
lua_pushvalue(L, 1);
LUA_PushUserdata(L, thing, META_MOBJ);
LUA_PushUserdata(L, ld, META_LINE);
if (lua_pcall(gL, 2, 1, 0)) {
if (!blockfuncerror || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
blockfuncerror = true;
return 0; // *shrugs*
}
if (!lua_isnil(gL, -1))
{ // if nil, continue
if (lua_toboolean(gL, -1))
return 2; // stop whole search
else
return 1; // stop block search
}
lua_pop(gL, 1);
if (P_MobjWasRemoved(thing))
return 2;
}
return 0; // Everything was checked.
}
// The searchBlockmap function
// arguments: searchBlockmap(searchtype, function, mobj, [x1, x2, y1, y2])
// return value:
// true = search completely uninteruppted,
// false = searching of at least one block stopped mid-way (including if the whole search was stopped)
static int lib_searchBlockmap(lua_State *L)
{
int searchtype = luaL_checkoption(L, 1, "objects", search_opt);
int n;
mobj_t *mobj;
INT32 xl, xh, yl, yh, bx, by;
fixed_t x1, x2, y1, y2;
boolean retval = true;
UINT8 funcret = 0;
blockmap_func searchFunc;
lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2]
luaL_checktype(L, 1, LUA_TFUNCTION);
switch (searchtype)
{
case 0: // "objects"
default:
searchFunc = lib_searchBlockmap_Objects;
break;
case 1: // "lines"
searchFunc = lib_searchBlockmap_Lines;
break;
}
// the mobj we are searching around, the "calling" mobj we could say
mobj = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
if (!mobj)
return LUA_ErrInvalid(L, "mobj_t");
n = lua_gettop(L);
if (n > 2) // specific x/y ranges have been supplied
{
if (n < 6)
return luaL_error(L, "arguments 4 to 6 not all given (expected 4 fixed-point integers)");
x1 = luaL_checkfixed(L, 3);
x2 = luaL_checkfixed(L, 4);
y1 = luaL_checkfixed(L, 5);
y2 = luaL_checkfixed(L, 6);
}
else // mobj and function only - search around mobj's radius by default
{
fixed_t radius = mobj->radius + MAXRADIUS;
x1 = mobj->x - radius;
x2 = mobj->x + radius;
y1 = mobj->y - radius;
y2 = mobj->y + radius;
}
lua_settop(L, 2); // pop everything except function, mobj
xl = (unsigned)(x1 - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(x2 - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(y1 - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(y2 - bmaporgy)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh);
blockfuncerror = false; // reset
validcount++;
for (bx = xl; bx <= xh; bx++)
for (by = yl; by <= yh; by++)
{
funcret = searchFunc(L, bx, by, mobj);
// return value of searchFunc determines searchFunc's return value and/or when to stop
if (funcret == 2){ // stop whole search
lua_pushboolean(L, false); // return false
return 1;
}
else if (funcret == 1) // search was interrupted for this block
retval = false; // this changes the return value, but doesn't stop the whole search
// else don't do anything, continue as normal
if (P_MobjWasRemoved(mobj)){ // ...unless the original object was removed
lua_pushboolean(L, false); // in which case we have to stop now regardless
return 1;
}
}
lua_pushboolean(L, retval);
return 1;
}
int LUA_BlockmapLib(lua_State *L)
{
lua_register(L, "searchBlockmap", lib_searchBlockmap);
return 0;
}
#endif

View file

@ -43,6 +43,8 @@ enum hook {
hook_PlayerMsg, hook_PlayerMsg,
hook_HurtMsg, hook_HurtMsg,
hook_PlayerSpawn, hook_PlayerSpawn,
hook_ShieldSpawn,
hook_ShieldSpecial,
hook_MAX // last hook hook_MAX // last hook
}; };
@ -60,11 +62,11 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which);
#define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type #define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type
boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type
#define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type #define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type
#define LUAh_MobjThinker(mo) LUAh_MobjHook(mo, hook_MobjThinker) // Hook for P_MobjThinker or P_SceneryThinker by mobj type boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type
#define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type #define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type
UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Should mobj take damage?) UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source); // Hook for P_KillMobj by mobj type boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for P_KillMobj by mobj type
#define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type #define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type
#define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type #define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type
#define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping) #define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping)
@ -75,7 +77,9 @@ boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd); // Hook for B_BuildTiccmd
boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name
boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors
boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg); // Hook for chat messages boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg); // Hook for chat messages
boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source); // Hook for hurt messages boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for hurt messages
#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer #define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer
#define LUAh_ShieldSpawn(player) LUAh_PlayerHook(player, hook_ShieldSpawn) // Hook for P_SpawnShieldOrb
#define LUAh_ShieldSpecial(player) LUAh_PlayerHook(player, hook_ShieldSpecial) // Hook for shield abilities
#endif #endif

View file

@ -54,6 +54,8 @@ const char *const hookNames[hook_MAX+1] = {
"PlayerMsg", "PlayerMsg",
"HurtMsg", "HurtMsg",
"PlayerSpawn", "PlayerSpawn",
"ShieldSpawn",
"ShieldSpecial",
NULL NULL
}; };
@ -74,12 +76,30 @@ typedef struct hook_s* hook_p;
#define FMT_HOOKID "hook_%d" #define FMT_HOOKID "hook_%d"
// For each mobj type, a linked list to its thinker and collision hooks.
// That way, we don't have to iterate through all the hooks.
// We could do that with all other mobj hooks, but it would probably just be
// a waste of memory since they are only called occasionally. Probably...
static hook_p mobjthinkerhooks[NUMMOBJTYPES];
static hook_p mobjcollidehooks[NUMMOBJTYPES];
// For each mobj type, a linked list for other mobj hooks
static hook_p mobjhooks[NUMMOBJTYPES];
// A linked list for player hooks
static hook_p playerhooks;
// A linked list for linedef executor hooks
static hook_p linedefexecutorhooks;
// For other hooks, a unique linked list
hook_p roothook; hook_p roothook;
// Takes hook, function, and additional arguments (mobj type to act on, etc.) // Takes hook, function, and additional arguments (mobj type to act on, etc.)
static int lib_addHook(lua_State *L) static int lib_addHook(lua_State *L)
{ {
static struct hook_s hook = {NULL, 0, 0, {0}, false}; static struct hook_s hook = {NULL, 0, 0, {0}, false};
static UINT32 nextid;
hook_p hookp, *lastp; hook_p hookp, *lastp;
hook.type = luaL_checkoption(L, 1, NULL, hookNames); hook.type = luaL_checkoption(L, 1, NULL, hookNames);
@ -109,6 +129,7 @@ static int lib_addHook(lua_State *L)
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);
luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t");
break; break;
case hook_BotAI: case hook_BotAI:
hook.s.skinname = NULL; hook.s.skinname = NULL;
@ -141,18 +162,49 @@ static int lib_addHook(lua_State *L)
hooksAvailable[hook.type/8] |= 1<<(hook.type%8); hooksAvailable[hook.type/8] |= 1<<(hook.type%8);
// iterate the hook metadata structs
// set hook.id to the highest id + 1 // set hook.id to the highest id + 1
// set lastp to the last hook struct's "next" pointer. hook.id = nextid++;
lastp = &roothook;
hook.id = 0; // Special cases for some hook types (see the comments above mobjthinkerhooks declaration)
for (hookp = roothook; hookp; hookp = hookp->next) switch(hook.type)
{ {
if (hookp->id >= hook.id) case hook_MobjThinker:
hook.id = hookp->id+1; lastp = &mobjthinkerhooks[hook.s.mt];
lastp = &hookp->next; break;
case hook_MobjCollide:
case hook_MobjMoveCollide:
lastp = &mobjcollidehooks[hook.s.mt];
break;
case hook_MobjSpawn:
case hook_TouchSpecial:
case hook_MobjFuse:
case hook_BossThinker:
case hook_ShouldDamage:
case hook_MobjDamage:
case hook_MobjDeath:
case hook_BossDeath:
case hook_MobjRemoved:
lastp = &mobjhooks[hook.s.mt];
break;
case hook_JumpSpecial:
case hook_AbilitySpecial:
case hook_SpinSpecial:
case hook_JumpSpinSpecial:
case hook_PlayerSpawn:
lastp = &playerhooks;
break;
case hook_LinedefExecute:
lastp = &linedefexecutorhooks;
break;
default:
lastp = &roothook;
break;
} }
// iterate the hook metadata structs
// set lastp to the last hook struct's "next" pointer.
for (hookp = *lastp; hookp; hookp = hookp->next)
lastp = &hookp->next;
// allocate a permanent memory struct to stuff hook. // allocate a permanent memory struct to stuff hook.
hookp = ZZ_Alloc(sizeof(struct hook_s)); hookp = ZZ_Alloc(sizeof(struct hook_s));
memcpy(hookp, &hook, sizeof(struct hook_s)); memcpy(hookp, &hook, sizeof(struct hook_s));
@ -183,9 +235,29 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
lua_settop(gL, 0); lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next) // Look for all generic mobj hooks
if (hookp->type == which for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
&& (hookp->s.mt == MT_NULL || hookp->s.mt == mo->type)) if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
LUA_PushUserdata(gL, mo, META_MOBJ);
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -2);
if (lua_pcall(gL, 1, 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[mo->type]; hookp; hookp = hookp->next)
if (hookp->type == which)
{ {
if (lua_gettop(gL) == 0) if (lua_gettop(gL) == 0)
LUA_PushUserdata(gL, mo, META_MOBJ); LUA_PushUserdata(gL, mo, META_MOBJ);
@ -217,7 +289,7 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which)
lua_settop(gL, 0); lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next) for (hookp = playerhooks; hookp; hookp = hookp->next)
if (hookp->type == which) if (hookp->type == which)
{ {
if (lua_gettop(gL) == 0) if (lua_gettop(gL) == 0)
@ -338,9 +410,38 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
lua_settop(gL, 0); lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next) // Look for all generic mobj collision hooks
if (hookp->type == which for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
&& (hookp->s.mt == MT_NULL || hookp->s.mt == thing1->type)) if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, thing1, META_MOBJ);
LUA_PushUserdata(gL, thing2, 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_isnil(gL, -1))
{ // if nil, leave shouldCollide = 0.
if (lua_toboolean(gL, -1))
shouldCollide = 1; // Force yes
else
shouldCollide = 2; // Force no
}
lua_pop(gL, 1);
}
for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next)
if (hookp->type == which)
{ {
if (lua_gettop(gL) == 0) if (lua_gettop(gL) == 0)
{ {
@ -372,6 +473,59 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
return shouldCollide; return shouldCollide;
} }
// Hook for mobj thinkers
boolean LUAh_MobjThinker(mobj_t *mo)
{
hook_p hookp;
boolean hooked = false;
if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8))))
return false;
lua_settop(gL, 0);
// Look for all generic mobj thinker hooks
for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
{
if (lua_gettop(gL) == 0)
LUA_PushUserdata(gL, mo, META_MOBJ);
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -2);
if (lua_pcall(gL, 1, 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 = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next)
{
if (lua_gettop(gL) == 0)
LUA_PushUserdata(gL, mo, META_MOBJ);
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -2);
if (lua_pcall(gL, 1, 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);
}
lua_settop(gL, 0);
return hooked;
}
// Hook for P_TouchSpecialThing by mobj type // Hook for P_TouchSpecialThing by mobj type
boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
{ {
@ -382,9 +536,33 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
lua_settop(gL, 0); lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next) // Look for all generic touch special hooks
if (hookp->type == hook_TouchSpecial for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
&& (hookp->s.mt == MT_NULL || hookp->s.mt == special->type)) if (hookp->type == hook_TouchSpecial)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, special, META_MOBJ);
LUA_PushUserdata(gL, toucher, 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[special->type]; hookp; hookp = hookp->next)
if (hookp->type == hook_TouchSpecial)
{ {
if (lua_gettop(gL) == 0) if (lua_gettop(gL) == 0)
{ {
@ -412,7 +590,7 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
} }
// Hook for P_DamageMobj by mobj type (Should mobj take damage?) // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{ {
hook_p hookp; hook_p hookp;
UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no. UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no.
@ -421,9 +599,9 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_settop(gL, 0); lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next) // Look for all generic should damage hooks
if (hookp->type == hook_ShouldDamage for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type)) if (hookp->type == hook_ShouldDamage)
{ {
if (lua_gettop(gL) == 0) if (lua_gettop(gL) == 0)
{ {
@ -455,12 +633,47 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_pop(gL, 1); lua_pop(gL, 1);
} }
for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
if (hookp->type == hook_ShouldDamage)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, target, META_MOBJ);
LUA_PushUserdata(gL, inflictor, META_MOBJ);
LUA_PushUserdata(gL, source, META_MOBJ);
lua_pushinteger(gL, damage);
lua_pushinteger(gL, damagetype);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -6);
lua_pushvalue(gL, -6);
lua_pushvalue(gL, -6);
lua_pushvalue(gL, -6);
lua_pushvalue(gL, -6);
if (lua_pcall(gL, 5, 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_isnil(gL, -1))
{
if (lua_toboolean(gL, -1))
shouldDamage = 1; // Force yes
else
shouldDamage = 2; // Force no
}
lua_pop(gL, 1);
}
lua_settop(gL, 0); lua_settop(gL, 0);
return shouldDamage; return shouldDamage;
} }
// Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{ {
hook_p hookp; hook_p hookp;
boolean hooked = false; boolean hooked = false;
@ -469,9 +682,9 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_settop(gL, 0); lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next) // Look for all generic mobj damage hooks
if (hookp->type == hook_MobjDamage for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type)) if (hookp->type == hook_MobjDamage)
{ {
if (lua_gettop(gL) == 0) if (lua_gettop(gL) == 0)
{ {
@ -498,12 +711,42 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_pop(gL, 1); lua_pop(gL, 1);
} }
for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDamage)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, target, META_MOBJ);
LUA_PushUserdata(gL, inflictor, META_MOBJ);
LUA_PushUserdata(gL, source, META_MOBJ);
lua_pushinteger(gL, damage);
lua_pushinteger(gL, damagetype);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -6);
lua_pushvalue(gL, -6);
lua_pushvalue(gL, -6);
lua_pushvalue(gL, -6);
lua_pushvalue(gL, -6);
if (lua_pcall(gL, 5, 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);
}
lua_settop(gL, 0); lua_settop(gL, 0);
return hooked; return hooked;
} }
// Hook for P_KillMobj by mobj type // Hook for P_KillMobj by mobj type
boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source) boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{ {
hook_p hookp; hook_p hookp;
boolean hooked = false; boolean hooked = false;
@ -512,9 +755,9 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
lua_settop(gL, 0); lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next) // Look for all generic mobj death hooks
if (hookp->type == hook_MobjDeath for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type)) if (hookp->type == hook_MobjDeath)
{ {
if (lua_gettop(gL) == 0) if (lua_gettop(gL) == 0)
{ {
@ -539,6 +782,34 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
lua_pop(gL, 1); lua_pop(gL, 1);
} }
for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDeath)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, target, META_MOBJ);
LUA_PushUserdata(gL, inflictor, META_MOBJ);
LUA_PushUserdata(gL, source, META_MOBJ);
lua_pushinteger(gL, damagetype);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -5);
lua_pushvalue(gL, -5);
lua_pushvalue(gL, -5);
lua_pushvalue(gL, -5);
if (lua_pcall(gL, 4, 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);
}
lua_settop(gL, 0); lua_settop(gL, 0);
return hooked; return hooked;
} }
@ -652,9 +923,8 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
lua_settop(gL, 0); lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next) for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next)
if (hookp->type == hook_LinedefExecute if (!strcmp(hookp->s.funcname, line->text))
&& !strcmp(hookp->s.funcname, line->text))
{ {
if (lua_gettop(gL) == 0) if (lua_gettop(gL) == 0)
{ {
@ -729,7 +999,7 @@ boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
} }
// Hook for hurt messages // Hook for hurt messages
boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source) boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{ {
hook_p hookp; hook_p hookp;
boolean hooked = false; boolean hooked = false;
@ -747,13 +1017,15 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source)
LUA_PushUserdata(gL, player, META_PLAYER); LUA_PushUserdata(gL, player, META_PLAYER);
LUA_PushUserdata(gL, inflictor, META_MOBJ); LUA_PushUserdata(gL, inflictor, META_MOBJ);
LUA_PushUserdata(gL, source, META_MOBJ); LUA_PushUserdata(gL, source, META_MOBJ);
lua_pushinteger(gL, damagetype);
} }
lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX); lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -4); lua_pushvalue(gL, -5);
lua_pushvalue(gL, -4); lua_pushvalue(gL, -5);
lua_pushvalue(gL, -4); lua_pushvalue(gL, -5);
if (lua_pcall(gL, 3, 1, 0)) { lua_pushvalue(gL, -5);
if (lua_pcall(gL, 4, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA) if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1); lua_pop(gL, 1);

View file

@ -226,7 +226,12 @@ static int hudinfo_num(lua_State *L)
static int colormap_get(lua_State *L) static int colormap_get(lua_State *L)
{ {
return luaL_error(L, "colormap is not a struct."); const UINT8 *colormap = *((UINT8 **)luaL_checkudata(L, 1, META_COLORMAP));
UINT32 i = luaL_checkinteger(L, 2);
if (i >= 256)
return luaL_error(L, "colormap index %d out of range (0 - %d)", i, 255);
lua_pushinteger(L, colormap[i]);
return 1;
} }
static int patch_get(lua_State *L) static int patch_get(lua_State *L)

View file

@ -38,12 +38,22 @@ extern lua_State *gL;
#define META_SUBSECTOR "SUBSECTOR_T*" #define META_SUBSECTOR "SUBSECTOR_T*"
#define META_SECTOR "SECTOR_T*" #define META_SECTOR "SECTOR_T*"
#define META_FFLOOR "FFLOOR_T*" #define META_FFLOOR "FFLOOR_T*"
#ifdef HAVE_LUA_SEGS
#define META_SEG "SEG_T*"
#define META_NODE "NODE_T*"
#endif
#define META_MAPHEADER "MAPHEADER_T*" #define META_MAPHEADER "MAPHEADER_T*"
#define META_CVAR "CONSVAR_T*" #define META_CVAR "CONSVAR_T*"
#define META_SECTORLINES "SECTOR_T*LINES" #define META_SECTORLINES "SECTOR_T*LINES"
#define META_SIDENUM "LINE_T*SIDENUM" #define META_SIDENUM "LINE_T*SIDENUM"
#ifdef HAVE_LUA_SEGS
#define META_NODEBBOX "NODE_T*BBOX"
#define META_NODECHILDREN "NODE_T*CHILDREN"
#endif
#define META_BBOX "BOUNDING_BOX"
#define META_HUDINFO "HUDINFO_T*" #define META_HUDINFO "HUDINFO_T*"
#define META_PATCH "PATCH_T*" #define META_PATCH "PATCH_T*"
@ -64,6 +74,7 @@ int LUA_PlayerLib(lua_State *L);
int LUA_SkinLib(lua_State *L); int LUA_SkinLib(lua_State *L);
int LUA_ThinkerLib(lua_State *L); int LUA_ThinkerLib(lua_State *L);
int LUA_MapLib(lua_State *L); int LUA_MapLib(lua_State *L);
int LUA_BlockmapLib(lua_State *L);
int LUA_HudLib(lua_State *L); int LUA_HudLib(lua_State *L);
#endif #endif

View file

@ -185,6 +185,82 @@ static const char *const ffloor_opt[] = {
"alpha", "alpha",
NULL}; NULL};
#ifdef HAVE_LUA_SEGS
enum seg_e {
seg_valid = 0,
seg_v1,
seg_v2,
seg_side,
seg_offset,
seg_angle,
seg_sidedef,
seg_linedef,
seg_frontsector,
seg_backsector,
};
static const char *const seg_opt[] = {
"valid",
"v1",
"v2",
"side",
"offset",
"angle",
"sidedef",
"linedef",
"frontsector",
"backsector",
NULL};
enum node_e {
node_valid = 0,
node_x,
node_y,
node_dx,
node_dy,
node_bbox,
node_children,
};
static const char *const node_opt[] = {
"valid",
"x",
"y",
"dx",
"dy",
"bbox",
"children",
NULL};
enum nodechild_e {
nodechild_valid = 0,
nodechild_right,
nodechild_left,
};
static const char *const nodechild_opt[] = {
"valid",
"right",
"left",
NULL};
#endif
enum bbox_e {
bbox_valid = 0,
bbox_top,
bbox_bottom,
bbox_left,
bbox_right,
};
static const char *const bbox_opt[] = {
"valid",
"top",
"bottom",
"left",
"right",
NULL};
static const char *const array_opt[] ={"iterate",NULL}; static const char *const array_opt[] ={"iterate",NULL};
static const char *const valid_opt[] ={"valid",NULL}; static const char *const valid_opt[] ={"valid",NULL};
@ -348,22 +424,12 @@ static int sector_get(lua_State *L)
case sector_ceilingheight: case sector_ceilingheight:
lua_pushfixed(L, sector->ceilingheight); lua_pushfixed(L, sector->ceilingheight);
return 1; return 1;
case sector_floorpic: { // floorpic case sector_floorpic: // floorpic
levelflat_t *levelflat; lua_pushlstring(L, levelflats[sector->floorpic].name, 8);
INT16 i;
for (i = 0, levelflat = levelflats; i != sector->floorpic; i++, levelflat++)
;
lua_pushlstring(L, levelflat->name, 8);
return 1; return 1;
} case sector_ceilingpic: // ceilingpic
case sector_ceilingpic: { // ceilingpic lua_pushlstring(L, levelflats[sector->ceilingpic].name, 8);
levelflat_t *levelflat;
INT16 i;
for (i = 0, levelflat = levelflats; i != sector->ceilingpic; i++, levelflat++)
;
lua_pushlstring(L, levelflat->name, 8);
return 1; return 1;
}
case sector_lightlevel: case sector_lightlevel:
lua_pushinteger(L, sector->lightlevel); lua_pushinteger(L, sector->lightlevel);
return 1; return 1;
@ -400,46 +466,6 @@ static int sector_get(lua_State *L)
return 0; return 0;
} }
// help function for P_LoadSectors, find a flat in the active wad files,
// allocate an id for it, and set the levelflat (to speedup search)
//
static INT32 P_AddLevelFlatRuntime(const char *flatname)
{
size_t i;
levelflat_t *levelflat = levelflats;
//
// first scan through the already found flats
//
for (i = 0; i < numlevelflats; i++, levelflat++)
if (strnicmp(levelflat->name,flatname,8)==0)
break;
// that flat was already found in the level, return the id
if (i == numlevelflats)
{
// allocate new flat memory
levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL);
levelflat = levelflats+i;
// store the name
strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
strupr(levelflat->name);
// store the flat lump number
levelflat->lumpnum = R_GetFlatNumForName(flatname);
#ifndef ZDEBUG
CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
#endif
numlevelflats++;
}
// level flat id
return (INT32)i;
}
static int sector_set(lua_State *L) static int sector_set(lua_State *L)
{ {
sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR)); sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
@ -818,6 +844,262 @@ static int vertex_num(lua_State *L)
return 1; return 1;
} }
#ifdef HAVE_LUA_SEGS
static int seg_get(lua_State *L)
{
seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG));
enum seg_e field = luaL_checkoption(L, 2, seg_opt[0], seg_opt);
if (!seg)
{
if (field == seg_valid) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed seg_t doesn't exist anymore.");
}
switch(field)
{
case seg_valid: // valid
lua_pushboolean(L, 1);
return 1;
case seg_v1:
LUA_PushUserdata(L, seg->v1, META_VERTEX);
return 1;
case seg_v2:
LUA_PushUserdata(L, seg->v2, META_VERTEX);
return 1;
case seg_side:
lua_pushinteger(L, seg->side);
return 1;
case seg_offset:
lua_pushfixed(L, seg->offset);
return 1;
case seg_angle:
lua_pushangle(L, seg->angle);
return 1;
case seg_sidedef:
LUA_PushUserdata(L, seg->sidedef, META_SIDE);
return 1;
case seg_linedef:
LUA_PushUserdata(L, seg->linedef, META_LINE);
return 1;
case seg_frontsector:
LUA_PushUserdata(L, seg->frontsector, META_SECTOR);
return 1;
case seg_backsector:
LUA_PushUserdata(L, seg->backsector, META_SECTOR);
return 1;
}
return 0;
}
static int seg_num(lua_State *L)
{
seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG));
lua_pushinteger(L, seg-segs);
return 1;
}
static int node_get(lua_State *L)
{
node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE));
enum node_e field = luaL_checkoption(L, 2, node_opt[0], node_opt);
if (!node)
{
if (field == node_valid) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed node_t doesn't exist anymore.");
}
switch(field)
{
case node_valid: // valid
lua_pushboolean(L, 1);
return 1;
case node_x:
lua_pushfixed(L, node->x);
return 1;
case node_y:
lua_pushfixed(L, node->y);
return 1;
case node_dx:
lua_pushfixed(L, node->x);
return 1;
case node_dy:
lua_pushfixed(L, node->x);
return 1;
case node_bbox:
LUA_PushUserdata(L, node->bbox, META_NODEBBOX);
return 1;
case node_children:
LUA_PushUserdata(L, node->children, META_NODECHILDREN);
return 1;
}
return 0;
}
static int node_num(lua_State *L)
{
node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE));
lua_pushinteger(L, node-nodes);
return 1;
}
/*
// node.bbox[i][j]: i = 0 or 1, j = 0 1 2 or 3
// NOTE: 2D arrays are NOT double pointers,
// the second bbox will be directly after the first in memory (hence the way the bbox is pushed here)
// this function handles the [i] part, bbox_get handles the [j] part
static int nodebbox_get(lua_State *L)
{
fixed_t *bbox = *((fixed_t **)luaL_checkudata(L, 1, META_NODEBBOX));
int i;
lua_settop(L, 2);
if (!lua_isnumber(L, 2))
{
int field = luaL_checkoption(L, 2, NULL, valid_opt);
if (!bbox)
{
if (field == 0) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed node_t doesn't exist anymore.");
} else if (field == 0) {
lua_pushboolean(L, 1);
return 1;
}
}
i = lua_tointeger(L, 2);
if (i < 0 || i > 1)
return 0;
LUA_PushUserdata(L, bbox + i*4*sizeof(fixed_t), META_BBOX);
return 1;
}
*/
static int nodebbox_call(lua_State *L)
{
fixed_t *bbox = *((fixed_t **)luaL_checkudata(L, 1, META_NODEBBOX));
int i, j;
int n = lua_gettop(L);
if (!bbox)
return luaL_error(L, "accessed node bbox doesn't exist anymore.");
if (n < 3)
return luaL_error(L, "arguments 2 and/or 3 not given (expected node.bbox(child, coord))");
// get child
if (!lua_isnumber(L, 2)) {
enum nodechild_e field = luaL_checkoption(L, 2, nodechild_opt[0], nodechild_opt);
switch (field) {
case nodechild_right: i = 0; break;
case nodechild_left: i = 1; break;
default:
return luaL_error(L, "invalid node child \"%s\".", lua_tostring(L, 2));
}
}
else {
i = lua_tointeger(L, 2);
if (i < 0 || i > 1)
return 0;
}
// get bbox coord
if (!lua_isnumber(L, 3)) {
enum bbox_e field = luaL_checkoption(L, 3, bbox_opt[0], bbox_opt);
switch (field) {
case bbox_top: j = BOXTOP; break;
case bbox_bottom: j = BOXBOTTOM; break;
case bbox_left: j = BOXLEFT; break;
case bbox_right: j = BOXRIGHT; break;
default:
return luaL_error(L, "invalid bbox coordinate \"%s\".", lua_tostring(L, 3));
}
}
else {
j = lua_tointeger(L, 3);
if (j < 0 || j > 3)
return 0;
}
lua_pushinteger(L, bbox[i*4 + j]);
return 1;
}
// node.children[i]: i = 0 or 1
static int nodechildren_get(lua_State *L)
{
UINT16 *children = *((UINT16 **)luaL_checkudata(L, 1, META_NODECHILDREN));
int i;
lua_settop(L, 2);
if (!lua_isnumber(L, 2))
{
enum nodechild_e field = luaL_checkoption(L, 2, nodechild_opt[0], nodechild_opt);
if (!children)
{
if (field == nodechild_valid) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed node_t doesn't exist anymore.");
} else if (field == nodechild_valid) {
lua_pushboolean(L, 1);
return 1;
} else switch (field) {
case nodechild_right: i = 0; break;
case nodechild_left: i = 1; break;
default: return 0;
}
}
else {
i = lua_tointeger(L, 2);
if (i < 0 || i > 1)
return 0;
}
lua_pushinteger(L, children[i]);
return 1;
}
#endif
// bounding box (aka fixed_t array with four elements)
// NOTE: may be useful for polyobjects or other things later
static int bbox_get(lua_State *L)
{
fixed_t *bbox = *((fixed_t **)luaL_checkudata(L, 1, META_BBOX));
int i;
lua_settop(L, 2);
if (!lua_isnumber(L, 2))
{
enum bbox_e field = luaL_checkoption(L, 2, bbox_opt[0], bbox_opt);
if (!bbox)
{
if (field == bbox_valid) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed bbox doesn't exist anymore.");
} else if (field == bbox_valid) {
lua_pushboolean(L, 1);
return 1;
} else switch (field) {
case bbox_top: i = BOXTOP; break;
case bbox_bottom: i = BOXBOTTOM; break;
case bbox_left: i = BOXLEFT; break;
case bbox_right: i = BOXRIGHT; break;
default: return 0;
}
}
else {
i = lua_tointeger(L, 2);
if (i < 0 || i > 3)
return 0;
}
lua_pushinteger(L, bbox[i]);
return 1;
}
static int lib_iterateSectors(lua_State *L) static int lib_iterateSectors(lua_State *L)
{ {
size_t i = 0; size_t i = 0;
@ -1048,6 +1330,100 @@ static int lib_numvertexes(lua_State *L)
return 1; return 1;
} }
#ifdef HAVE_LUA_SEGS
static int lib_iterateSegs(lua_State *L)
{
size_t i = 0;
if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call segs.iterate() directly, use it as 'for seg in segs.iterate do <block> end'.");
lua_settop(L, 2);
lua_remove(L, 1); // state is unused.
if (!lua_isnil(L, 1))
i = (size_t)(*((seg_t **)luaL_checkudata(L, 1, META_SEG)) - segs)+1;
if (i < numsegs)
{
LUA_PushUserdata(L, &segs[i], META_SEG);
return 1;
}
return 0;
}
static int lib_getSeg(lua_State *L)
{
int field;
lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1))
{
size_t i = lua_tointeger(L, 1);
if (i >= numsegs)
return 0;
LUA_PushUserdata(L, &segs[i], META_SEG);
return 1;
}
field = luaL_checkoption(L, 1, NULL, array_opt);
switch(field)
{
case 0: // iterate
lua_pushcfunction(L, lib_iterateSegs);
return 1;
}
return 0;
}
static int lib_numsegs(lua_State *L)
{
lua_pushinteger(L, numsegs);
return 1;
}
static int lib_iterateNodes(lua_State *L)
{
size_t i = 0;
if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call nodes.iterate() directly, use it as 'for node in nodes.iterate do <block> end'.");
lua_settop(L, 2);
lua_remove(L, 1); // state is unused.
if (!lua_isnil(L, 1))
i = (size_t)(*((node_t **)luaL_checkudata(L, 1, META_NODE)) - nodes)+1;
if (i < numsegs)
{
LUA_PushUserdata(L, &nodes[i], META_NODE);
return 1;
}
return 0;
}
static int lib_getNode(lua_State *L)
{
int field;
lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1))
{
size_t i = lua_tointeger(L, 1);
if (i >= numnodes)
return 0;
LUA_PushUserdata(L, &nodes[i], META_NODE);
return 1;
}
field = luaL_checkoption(L, 1, NULL, array_opt);
switch(field)
{
case 0: // iterate
lua_pushcfunction(L, lib_iterateNodes);
return 1;
}
return 0;
}
static int lib_numnodes(lua_State *L)
{
lua_pushinteger(L, numnodes);
return 1;
}
#endif
static int ffloor_get(lua_State *L) static int ffloor_get(lua_State *L)
{ {
ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR)); ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
@ -1367,6 +1743,41 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__newindex"); lua_setfield(L, -2, "__newindex");
lua_pop(L, 1); lua_pop(L, 1);
#ifdef HAVE_LUA_SEGS
luaL_newmetatable(L, META_SEG);
lua_pushcfunction(L, seg_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, seg_num);
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
luaL_newmetatable(L, META_NODE);
lua_pushcfunction(L, node_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, node_num);
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
luaL_newmetatable(L, META_NODEBBOX);
//lua_pushcfunction(L, nodebbox_get);
//lua_setfield(L, -2, "__index");
lua_pushcfunction(L, nodebbox_call);
lua_setfield(L, -2, "__call");
lua_pop(L, 1);
luaL_newmetatable(L, META_NODECHILDREN);
lua_pushcfunction(L, nodechildren_get);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
#endif
luaL_newmetatable(L, META_BBOX);
lua_pushcfunction(L, bbox_get);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
luaL_newmetatable(L, META_MAPHEADER); luaL_newmetatable(L, META_MAPHEADER);
lua_pushcfunction(L, mapheaderinfo_get); lua_pushcfunction(L, mapheaderinfo_get);
lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "__index");
@ -1425,6 +1836,28 @@ int LUA_MapLib(lua_State *L)
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
lua_setglobal(L, "vertexes"); lua_setglobal(L, "vertexes");
#ifdef HAVE_LUA_SEGS
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getSeg);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_numsegs);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "segs");
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getNode);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_numnodes);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "nodes");
#endif
lua_newuserdata(L, 0); lua_newuserdata(L, 0);
lua_createtable(L, 0, 2); lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getMapheaderinfo); lua_pushcfunction(L, lib_getMapheaderinfo);

View file

@ -34,6 +34,7 @@ enum mobj_e {
mobj_angle, mobj_angle,
mobj_sprite, mobj_sprite,
mobj_frame, mobj_frame,
mobj_sprite2,
mobj_anim_duration, mobj_anim_duration,
mobj_touching_sectorlist, mobj_touching_sectorlist,
mobj_subsector, mobj_subsector,
@ -93,6 +94,7 @@ static const char *const mobj_opt[] = {
"angle", "angle",
"sprite", "sprite",
"frame", "frame",
"sprite2",
"anim_duration", "anim_duration",
"touching_sectorlist", "touching_sectorlist",
"subsector", "subsector",
@ -189,6 +191,9 @@ static int mobj_get(lua_State *L)
case mobj_frame: case mobj_frame:
lua_pushinteger(L, mo->frame); lua_pushinteger(L, mo->frame);
break; break;
case mobj_sprite2:
lua_pushinteger(L, mo->sprite2);
break;
case mobj_anim_duration: case mobj_anim_duration:
lua_pushinteger(L, mo->anim_duration); lua_pushinteger(L, mo->anim_duration);
break; break;
@ -411,6 +416,9 @@ static int mobj_set(lua_State *L)
case mobj_frame: case mobj_frame:
mo->frame = (UINT32)luaL_checkinteger(L, 3); mo->frame = (UINT32)luaL_checkinteger(L, 3);
break; break;
case mobj_sprite2:
mo->sprite2 = P_GetMobjSprite2(mo, (UINT8)luaL_checkinteger(L, 3));
break;
case mobj_anim_duration: case mobj_anim_duration:
mo->anim_duration = (UINT16)luaL_checkinteger(L, 3); mo->anim_duration = (UINT16)luaL_checkinteger(L, 3);
break; break;

View file

@ -122,8 +122,8 @@ static int player_get(lua_State *L)
lua_pushfixed(L, plr->bob); lua_pushfixed(L, plr->bob);
else if (fastcmp(field,"aiming")) else if (fastcmp(field,"aiming"))
lua_pushangle(L, plr->aiming); lua_pushangle(L, plr->aiming);
else if (fastcmp(field,"health")) else if (fastcmp(field,"rings"))
lua_pushinteger(L, plr->health); lua_pushinteger(L, plr->rings);
else if (fastcmp(field,"pity")) else if (fastcmp(field,"pity"))
lua_pushinteger(L, plr->pity); lua_pushinteger(L, plr->pity);
else if (fastcmp(field,"currentweapon")) else if (fastcmp(field,"currentweapon"))
@ -382,8 +382,8 @@ static int player_set(lua_State *L)
else if (plr == &players[secondarydisplayplayer]) else if (plr == &players[secondarydisplayplayer])
localaiming2 = plr->aiming; localaiming2 = plr->aiming;
} }
else if (fastcmp(field,"health")) else if (fastcmp(field,"rings"))
plr->health = (INT32)luaL_checkinteger(L, 3); plr->rings = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"pity")) else if (fastcmp(field,"pity"))
plr->pity = (SINT8)luaL_checkinteger(L, 3); plr->pity = (SINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"currentweapon")) else if (fastcmp(field,"currentweapon"))

View file

@ -48,6 +48,7 @@ static lua_CFunction liblist[] = {
LUA_SkinLib, // skin_t, skins[] LUA_SkinLib, // skin_t, skins[]
LUA_ThinkerLib, // thinker_t LUA_ThinkerLib, // thinker_t
LUA_MapLib, // line_t, side_t, sector_t, subsector_t LUA_MapLib, // line_t, side_t, sector_t, subsector_t
LUA_BlockmapLib, // blockmap stuff
LUA_HudLib, // HUD stuff LUA_HudLib, // HUD stuff
NULL NULL
}; };
@ -395,6 +396,7 @@ void LUA_InvalidateLevel(void)
{ {
thinker_t *th; thinker_t *th;
size_t i; size_t i;
ffloor_t *rover = NULL;
if (!gL) if (!gL)
return; return;
@ -406,7 +408,15 @@ void LUA_InvalidateLevel(void)
for (i = 0; i < numsubsectors; i++) for (i = 0; i < numsubsectors; i++)
LUA_InvalidateUserdata(&subsectors[i]); LUA_InvalidateUserdata(&subsectors[i]);
for (i = 0; i < numsectors; i++) for (i = 0; i < numsectors; i++)
{
LUA_InvalidateUserdata(&sectors[i]); LUA_InvalidateUserdata(&sectors[i]);
LUA_InvalidateUserdata(sectors[i].lines);
if (sectors[i].ffloors)
{
for (rover = sectors[i].ffloors; rover; rover = rover->next)
LUA_InvalidateUserdata(rover);
}
}
for (i = 0; i < numlines; i++) for (i = 0; i < numlines; i++)
{ {
LUA_InvalidateUserdata(&lines[i]); LUA_InvalidateUserdata(&lines[i]);
@ -416,6 +426,16 @@ void LUA_InvalidateLevel(void)
LUA_InvalidateUserdata(&sides[i]); LUA_InvalidateUserdata(&sides[i]);
for (i = 0; i < numvertexes; i++) for (i = 0; i < numvertexes; i++)
LUA_InvalidateUserdata(&vertexes[i]); LUA_InvalidateUserdata(&vertexes[i]);
#ifdef HAVE_LUA_SEGS
for (i = 0; i < numsegs; i++)
LUA_InvalidateUserdata(&segs[i]);
for (i = 0; i < numnodes; i++)
{
LUA_InvalidateUserdata(&nodes[i]);
LUA_InvalidateUserdata(nodes[i].bbox);
LUA_InvalidateUserdata(nodes[i].children);
}
#endif
} }
void LUA_InvalidateMapthings(void) void LUA_InvalidateMapthings(void)
@ -455,6 +475,11 @@ enum
ARCH_SIDE, ARCH_SIDE,
ARCH_SUBSECTOR, ARCH_SUBSECTOR,
ARCH_SECTOR, ARCH_SECTOR,
#ifdef HAVE_LUA_SEGS
ARCH_SEG,
ARCH_NODE,
#endif
ARCH_FFLOOR,
ARCH_MAPHEADER, ARCH_MAPHEADER,
ARCH_TEND=0xFF, ARCH_TEND=0xFF,
@ -474,6 +499,11 @@ static const struct {
{META_SIDE, ARCH_SIDE}, {META_SIDE, ARCH_SIDE},
{META_SUBSECTOR,ARCH_SUBSECTOR}, {META_SUBSECTOR,ARCH_SUBSECTOR},
{META_SECTOR, ARCH_SECTOR}, {META_SECTOR, ARCH_SECTOR},
#ifdef HAVE_LUA_SEGS
{META_SEG, ARCH_SEG},
{META_NODE, ARCH_NODE},
#endif
{META_FFLOOR, ARCH_FFLOOR},
{META_MAPHEADER, ARCH_MAPHEADER}, {META_MAPHEADER, ARCH_MAPHEADER},
{NULL, ARCH_NULL} {NULL, ARCH_NULL}
}; };
@ -664,6 +694,56 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
} }
break; break;
} }
#ifdef HAVE_LUA_SEGS
case ARCH_SEG:
{
seg_t *seg = *((seg_t **)lua_touserdata(gL, myindex));
if (!seg)
WRITEUINT8(save_p, ARCH_NULL);
else {
WRITEUINT8(save_p, ARCH_SEG);
WRITEUINT16(save_p, seg - segs);
}
break;
}
case ARCH_NODE:
{
node_t *node = *((node_t **)lua_touserdata(gL, myindex));
if (!node)
WRITEUINT8(save_p, ARCH_NULL);
else {
WRITEUINT8(save_p, ARCH_NODE);
WRITEUINT16(save_p, node - nodes);
}
break;
}
#endif
case ARCH_FFLOOR:
{
ffloor_t *rover = *((ffloor_t **)lua_touserdata(gL, myindex));
if (!rover)
WRITEUINT8(save_p, ARCH_NULL);
else {
ffloor_t *r2;
UINT16 i = 0;
// search for id
for (r2 = rover->target->ffloors; r2; r2 = r2->next)
{
if (r2 == rover)
break;
i++;
}
if (!r2)
WRITEUINT8(save_p, ARCH_NULL);
else
{
WRITEUINT8(save_p, ARCH_FFLOOR);
WRITEUINT16(save_p, rover->target - sectors);
WRITEUINT16(save_p, i);
}
}
break;
}
case ARCH_MAPHEADER: case ARCH_MAPHEADER:
{ {
mapheader_t *header = *((mapheader_t **)lua_touserdata(gL, myindex)); mapheader_t *header = *((mapheader_t **)lua_touserdata(gL, myindex));
@ -842,6 +922,23 @@ static UINT8 UnArchiveValue(int TABLESINDEX)
case ARCH_SECTOR: case ARCH_SECTOR:
LUA_PushUserdata(gL, &sectors[READUINT16(save_p)], META_SECTOR); LUA_PushUserdata(gL, &sectors[READUINT16(save_p)], META_SECTOR);
break; break;
#ifdef HAVE_LUA_SEGS
case ARCH_SEG:
LUA_PushUserdata(gL, &segs[READUINT16(save_p)], META_SEG);
break;
case ARCH_NODE:
LUA_PushUserdata(gL, &nodes[READUINT16(save_p)], META_NODE);
break;
#endif
case ARCH_FFLOOR:
{
sector_t *sector = &sectors[READUINT16(save_p)];
UINT16 id = READUINT16(save_p);
ffloor_t *rover = P_GetFFloorByID(sector, id);
if (rover)
LUA_PushUserdata(gL, rover, META_FFLOOR);
break;
}
case ARCH_MAPHEADER: case ARCH_MAPHEADER:
LUA_PushUserdata(gL, &sectors[READUINT16(save_p)], META_MAPHEADER); LUA_PushUserdata(gL, &sectors[READUINT16(save_p)], META_MAPHEADER);
break; break;

View file

@ -92,4 +92,7 @@ void COM_Lua_f(void);
}\ }\
} }
// uncomment if you want seg_t/node_t in Lua
// #define HAVE_LUA_SEGS
#endif #endif

View file

@ -28,4 +28,4 @@ void M_AATreeSet(aatree_t *aatree, INT32 key, void* value);
void *M_AATreeGet(aatree_t *aatree, INT32 key); void *M_AATreeGet(aatree_t *aatree, INT32 key);
void M_AATreeIterate(aatree_t *aatree, aatree_iter_t callback); void M_AATreeIterate(aatree_t *aatree, aatree_iter_t callback);
#endif #endif

View file

@ -606,7 +606,7 @@ void Command_CauseCfail_f(void)
players[consoleplayer].mo->y = 123311; //cfail cansuled kthxbye players[consoleplayer].mo->y = 123311; //cfail cansuled kthxbye
players[consoleplayer].mo->z = 123311; players[consoleplayer].mo->z = 123311;
players[consoleplayer].score = 1337; players[consoleplayer].score = 1337;
players[consoleplayer].health = 1337; players[consoleplayer].rings = 1337;
players[consoleplayer].mo->destscale = 25; players[consoleplayer].mo->destscale = 25;
P_SetThingPosition(players[consoleplayer].mo); P_SetThingPosition(players[consoleplayer].mo);
@ -720,7 +720,7 @@ void Command_Setrings_f(void)
if (COM_Argc() > 1) if (COM_Argc() > 1)
{ {
// P_GivePlayerRings does value clamping // P_GivePlayerRings does value clamping
players[consoleplayer].health = players[consoleplayer].mo->health = 1; players[consoleplayer].rings = 0;
P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1))); P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1)));
if (!G_IsSpecialStage(gamemap) || !useNightsSS) if (!G_IsSpecialStage(gamemap) || !useNightsSS)
players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings
@ -1298,7 +1298,7 @@ void Command_ObjectPlace_f(void)
// Like the classics, recover from death by entering objectplace // Like the classics, recover from death by entering objectplace
if (players[0].mo->health <= 0) if (players[0].mo->health <= 0)
{ {
players[0].mo->health = players[0].health = 1; players[0].mo->health = 1;
players[0].deadtimer = 0; players[0].deadtimer = 0;
op_oldflags1 = mobjinfo[MT_PLAYER].flags; op_oldflags1 = mobjinfo[MT_PLAYER].flags;
++players[0].lives; ++players[0].lives;

View file

@ -46,41 +46,6 @@ typedef INT32 fixed_t;
#define FLOAT_TO_FIXED(f) (fixed_t)((f) * ((float)FRACUNIT)) #define FLOAT_TO_FIXED(f) (fixed_t)((f) * ((float)FRACUNIT))
/** \brief The TMulScale16 function
\param a a parameter of type fixed_t
\param b a parameter of type fixed_t
\param c a parameter of type fixed_t
\param d a parameter of type fixed_t
\param e a parameter of type fixed_t
\param f a parameter of type fixed_t
\return fixed_t
*/
FUNCMATH FUNCINLINE static ATTRINLINE fixed_t TMulScale16(fixed_t a, fixed_t b, fixed_t c, fixed_t d, fixed_t e, fixed_t f) \
{ \
return (fixed_t)((((INT64)a * (INT64)b) + ((INT64)c * (INT64)d) \
+ ((INT64)e * (INT64)f)) >> 16); \
}
/** \brief The DMulScale16 function
\param a a parameter of type fixed_t
\param b a parameter of type fixed_t
\param c a parameter of type fixed_t
\param d a parameter of type fixed_t
\return fixed_t
*/
FUNCMATH FUNCINLINE static ATTRINLINE fixed_t DMulScale16(fixed_t a, fixed_t b, fixed_t c, fixed_t d) \
{ \
return (fixed_t)((((INT64)a * (INT64)b) + ((INT64)c * (INT64)d)) >> 16); \
}
#if defined (__WATCOMC__) && FRACBITS == 16 #if defined (__WATCOMC__) && FRACBITS == 16
#pragma aux FixedMul = \ #pragma aux FixedMul = \
"imul ebx", \ "imul ebx", \
@ -283,9 +248,16 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedFloor(fixed_t x)
{ {
const fixed_t a = abs(x); //absolute of x const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part
const fixed_t f = i-a; // cut out the integral part const fixed_t f = a-i; // cut out the integral part
if (f == 0)
return x;
if (x != INT32_MIN) if (x != INT32_MIN)
return x-f; // return largest integral value not greater than argument { // return rounded down to nearest whole number
if (x > 0)
return x-f;
else
return x-(FRACUNIT-f);
}
return INT32_MIN; return INT32_MIN;
} }
@ -301,7 +273,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedTrunc(fixed_t x)
{ {
const fixed_t a = abs(x); //absolute of x const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part
const fixed_t f = i-a; // cut out the integral part const fixed_t f = a-i; // cut out the integral part
if (x != INT32_MIN) if (x != INT32_MIN)
{ // return rounded to nearest whole number, towards zero { // return rounded to nearest whole number, towards zero
if (x > 0) if (x > 0)
@ -324,11 +296,18 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedCeil(fixed_t x)
{ {
const fixed_t a = abs(x); //absolute of x const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part
const fixed_t f = i-a; // cut out the integral part const fixed_t f = a-i; // cut out the integral part
if (f == 0)
return x;
if (x == INT32_MIN) if (x == INT32_MIN)
return INT32_MIN; return INT32_MIN;
else if (x < FixedFloor(INT32_MAX)) else if (x < FixedFloor(INT32_MAX))
return x+(FRACUNIT-f); // return smallest integral value not less than argument { // return rounded up to nearest whole number
if (x > 0)
return x+(FRACUNIT-f);
else
return x+f;
}
return INT32_MAX; return INT32_MAX;
} }
@ -344,7 +323,9 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedRound(fixed_t x)
{ {
const fixed_t a = abs(x); //absolute of x const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part
const fixed_t f = i-a; // cut out the integral part const fixed_t f = a-i; // cut out the integral part
if (f == 0)
return x;
if (x == INT32_MIN) if (x == INT32_MIN)
return INT32_MIN; return INT32_MIN;
else if (x < FixedFloor(INT32_MAX)) else if (x < FixedFloor(INT32_MAX))

View file

@ -183,9 +183,6 @@ static INT32 vidm_selected = 0;
static INT32 vidm_nummodes; static INT32 vidm_nummodes;
static INT32 vidm_column_size; static INT32 vidm_column_size;
// what a headache.
static boolean shiftdown = false;
// //
// PROTOTYPES // PROTOTYPES
// //
@ -2083,11 +2080,6 @@ boolean M_Responder(event_t *ev)
|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION) || gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
return false; return false;
if (ev->type == ev_keyup && (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT))
{
shiftdown = false;
return false;
}
if (noFurtherInput) if (noFurtherInput)
{ {
// Ignore input after enter/escape/other buttons // Ignore input after enter/escape/other buttons
@ -2101,10 +2093,6 @@ boolean M_Responder(event_t *ev)
// added 5-2-98 remap virtual keys (mouse & joystick buttons) // added 5-2-98 remap virtual keys (mouse & joystick buttons)
switch (ch) switch (ch)
{ {
case KEY_LSHIFT:
case KEY_RSHIFT:
shiftdown = true;
break; //return false;
case KEY_MOUSE1: case KEY_MOUSE1:
case KEY_JOY1: case KEY_JOY1:
case KEY_JOY1 + 2: case KEY_JOY1 + 2:
@ -3831,7 +3819,7 @@ static void M_HandleImageDef(INT32 choice)
static void M_PandorasBox(INT32 choice) static void M_PandorasBox(INT32 choice)
{ {
(void)choice; (void)choice;
CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].health - 1, 0)); CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].rings, 0));
CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives); CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives);
CV_StealthSetValue(&cv_dummycontinues, players[consoleplayer].continues); CV_StealthSetValue(&cv_dummycontinues, players[consoleplayer].continues);
M_SetupNextMenu(&SR_PandoraDef); M_SetupNextMenu(&SR_PandoraDef);
@ -3839,7 +3827,7 @@ static void M_PandorasBox(INT32 choice)
static boolean M_ExitPandorasBox(void) static boolean M_ExitPandorasBox(void)
{ {
if (cv_dummyrings.value != max(players[consoleplayer].health - 1, 0)) if (cv_dummyrings.value != max(players[consoleplayer].rings, 0))
COM_ImmedExecute(va("setrings %d", cv_dummyrings.value)); COM_ImmedExecute(va("setrings %d", cv_dummyrings.value));
if (cv_dummylives.value != players[consoleplayer].lives) if (cv_dummylives.value != players[consoleplayer].lives)
COM_ImmedExecute(va("setlives %d", cv_dummylives.value)); COM_ImmedExecute(va("setlives %d", cv_dummylives.value));

View file

@ -1617,6 +1617,11 @@ INT32 axtoi(const char *hexStg)
return intValue; return intValue;
} }
// Token parser variables
static UINT32 oldendPos = 0; // old value of endPos, used by M_UnGetToken
static UINT32 endPos = 0; // now external to M_GetToken, but still static
/** Token parser for TEXTURES, ANIMDEFS, and potentially other lumps later down the line. /** Token parser for TEXTURES, ANIMDEFS, and potentially other lumps later down the line.
* Was originally R_GetTexturesToken when I was coding up the TEXTURES parser, until I realized I needed it for ANIMDEFS too. * Was originally R_GetTexturesToken when I was coding up the TEXTURES parser, until I realized I needed it for ANIMDEFS too.
* Parses up to the next whitespace character or comma. When finding the start of the next token, whitespace is skipped. * Parses up to the next whitespace character or comma. When finding the start of the next token, whitespace is skipped.
@ -1631,7 +1636,7 @@ char *M_GetToken(const char *inputString)
{ {
static const char *stringToUse = NULL; // Populated if inputString != NULL; used otherwise static const char *stringToUse = NULL; // Populated if inputString != NULL; used otherwise
static UINT32 startPos = 0; static UINT32 startPos = 0;
static UINT32 endPos = 0; // static UINT32 endPos = 0;
static UINT32 stringLength = 0; static UINT32 stringLength = 0;
static UINT8 inComment = 0; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */ static UINT8 inComment = 0; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */
char *texturesToken = NULL; char *texturesToken = NULL;
@ -1641,12 +1646,12 @@ char *M_GetToken(const char *inputString)
{ {
stringToUse = inputString; stringToUse = inputString;
startPos = 0; startPos = 0;
endPos = 0; oldendPos = endPos = 0;
stringLength = strlen(inputString); stringLength = strlen(inputString);
} }
else else
{ {
startPos = endPos; startPos = oldendPos = endPos;
} }
if (stringToUse == NULL) if (stringToUse == NULL)
return NULL; return NULL;
@ -1777,6 +1782,16 @@ char *M_GetToken(const char *inputString)
return texturesToken; return texturesToken;
} }
/** Undoes the last M_GetToken call
* The current position along the string being parsed is reset to the last saved position.
* This exists mostly because of R_ParseTexture/R_ParsePatch honestly, but could be useful elsewhere?
* -Monster Iestyn (22/10/16)
*/
void M_UnGetToken(void)
{
endPos = oldendPos;
}
/** Count bits in a number. /** Count bits in a number.
*/ */
UINT8 M_CountBits(UINT32 num, UINT8 size) UINT8 M_CountBits(UINT32 num, UINT8 size)

View file

@ -269,6 +269,18 @@ INT32 I_PutEnv(char *variable)
return -1; return -1;
} }
INT32 I_ClipboardCopy(const char *data, size_t size)
{
(void)data;
(void)size;
return -1;
}
char *I_ClipboardPaste(void)
{
return NULL;
}
void I_RegisterSysCommands(void) {} void I_RegisterSysCommands(void) {}
#include "../sdl/dosstr.c" #include "../sdl/dosstr.c"

View file

@ -104,6 +104,9 @@ void A_BombShield(mobj_t *actor);
void A_WaterShield(mobj_t *actor); void A_WaterShield(mobj_t *actor);
void A_ForceShield(mobj_t *actor); void A_ForceShield(mobj_t *actor);
void A_PityShield(mobj_t *actor); void A_PityShield(mobj_t *actor);
void A_FlameShield(mobj_t *actor);
void A_BubbleShield(mobj_t *actor);
void A_ThunderShield(mobj_t *actor);
void A_GravityBox(mobj_t *actor); void A_GravityBox(mobj_t *actor);
void A_ScoreRise(mobj_t *actor); void A_ScoreRise(mobj_t *actor);
void A_ParticleSpawn(mobj_t *actor); void A_ParticleSpawn(mobj_t *actor);
@ -239,6 +242,17 @@ void A_BrakFireShot(mobj_t *actor);
void A_BrakLobShot(mobj_t *actor); void A_BrakLobShot(mobj_t *actor);
void A_NapalmScatter(mobj_t *actor); void A_NapalmScatter(mobj_t *actor);
void A_SpawnFreshCopy(mobj_t *actor); void A_SpawnFreshCopy(mobj_t *actor);
void A_FlickySpawn(mobj_t *actor);
void A_FlickyAim(mobj_t *actor);
void A_FlickyFly(mobj_t *actor);
void A_FlickySoar(mobj_t *actor);
void A_FlickyCoast(mobj_t *actor);
void A_FlickyHop(mobj_t *actor);
void A_FlickyFlounder(mobj_t *actor);
void A_FlickyCheck(mobj_t *actor);
void A_FlickyHeightCheck(mobj_t *actor);
void A_FlickyFlutter(mobj_t *actor);
void A_FlameParticle(mobj_t *actor);
// //
// ENEMY THINKING // ENEMY THINKING
@ -658,15 +672,15 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed
if ((netgame || multiplayer) && player->spectator) if ((netgame || multiplayer) && player->spectator)
continue; continue;
if (player->health <= 0)
continue; // dead
if (player->pflags & PF_INVIS) if (player->pflags & PF_INVIS)
continue; // ignore notarget continue; // ignore notarget
if (!player->mo || P_MobjWasRemoved(player->mo)) if (!player->mo || P_MobjWasRemoved(player->mo))
continue; continue;
if (player->mo->health <= 0)
continue; // dead
if (dist > 0 if (dist > 0
&& P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist) && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist)
continue; // Too far away continue; // Too far away
@ -730,7 +744,7 @@ static boolean P_LookForShield(mobj_t *actor)
player = &players[actor->lastlook]; player = &players[actor->lastlook];
if (player->health <= 0 || !player->mo) if (!player->mo || player->mo->health <= 0)
continue; // dead continue; // dead
//When in CTF, don't pull rings that you cannot pick up. //When in CTF, don't pull rings that you cannot pick up.
@ -738,7 +752,7 @@ static boolean P_LookForShield(mobj_t *actor)
(actor->type == MT_BLUETEAMRING && player->ctfteam != 2)) (actor->type == MT_BLUETEAMRING && player->ctfteam != 2))
continue; continue;
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale))) && (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
{ {
P_SetTarget(&actor->tracer, player->mo); P_SetTarget(&actor->tracer, player->mo);
@ -2121,13 +2135,15 @@ void A_Boss1Laser(mobj_t *actor)
if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
{ {
point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET); point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET);
point->angle = actor->angle;
point->fuse = actor->tics+1; point->fuse = actor->tics+1;
P_SetTarget(&point->target, actor->target); P_SetTarget(&point->target, actor->target);
P_SetTarget(&actor->target, point); P_SetTarget(&actor->target, point);
} }
} }
/* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path
else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y); actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/
if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH) if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)
angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT); angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT);
@ -2177,11 +2193,16 @@ void A_Boss1Laser(mobj_t *actor)
// var1: // var1:
// 0 - accelerative focus with friction // 0 - accelerative focus with friction
// 1 - steady focus with fixed movement speed // 1 - steady focus with fixed movement speed
// var2 = unused // anything else - don't move
// var2:
// 0 - don't trace target, just move forwards
// & 1 - change horizontal angle
// & 2 - change vertical angle
// //
void A_FocusTarget(mobj_t *actor) void A_FocusTarget(mobj_t *actor)
{ {
INT32 locvar1 = var1; INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUA_CallAction("A_FocusTarget", actor)) if (LUA_CallAction("A_FocusTarget", actor))
return; return;
@ -2190,9 +2211,9 @@ void A_FocusTarget(mobj_t *actor)
if (actor->target) if (actor->target)
{ {
fixed_t speed = FixedMul(actor->info->speed, actor->scale); fixed_t speed = FixedMul(actor->info->speed, actor->scale);
fixed_t dist = R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y); fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1);
angle_t vangle = R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist); angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle);
angle_t hangle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90);
switch(locvar1) switch(locvar1)
{ {
case 0: case 0:
@ -2553,6 +2574,7 @@ void A_1upThinker(mobj_t *actor)
{ {
P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY)); P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY));
P_SetTarget(&actor->tracer->target, actor); P_SetTarget(&actor->tracer->target, actor);
actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame
P_SetMobjState(actor->tracer, actor->info->seestate); P_SetMobjState(actor->tracer, actor->info->seestate);
// The overlay is going to be one tic early turning off and on // The overlay is going to be one tic early turning off and on
@ -2813,7 +2835,7 @@ void A_BossDeath(mobj_t *mo)
// make sure there is a player alive for victory // make sure there is a player alive for victory
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && (players[i].health > 0 if (playeringame[i] && ((players[i].mo && players[i].mo->health > 0)
|| ((netgame || multiplayer) && (players[i].lives > 0 || players[i].continues > 0)))) || ((netgame || multiplayer) && (players[i].lives > 0 || players[i].continues > 0))))
break; break;
@ -3058,11 +3080,7 @@ void A_JumpShield(mobj_t *actor)
player = actor->target->player; player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_JUMP) P_SwitchShield(player, SH_WHIRLWIND);
{
player->powers[pw_shield] = SH_JUMP|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
S_StartSound(player->mo, actor->info->seesound); S_StartSound(player->mo, actor->info->seesound);
} }
@ -3090,11 +3108,7 @@ void A_RingShield(mobj_t *actor)
player = actor->target->player; player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_ATTRACT) P_SwitchShield(player, SH_ATTRACT);
{
player->powers[pw_shield] = SH_ATTRACT|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
S_StartSound(player->mo, actor->info->seesound); S_StartSound(player->mo, actor->info->seesound);
} }
@ -3291,11 +3305,12 @@ void A_BombShield(mobj_t *actor)
player = actor->target->player; player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_BOMB) // If you already have a bomb shield, use it!
{ if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON)
player->powers[pw_shield] = SH_BOMB|(player->powers[pw_shield] & SH_STACK); P_BlackOw(player);
P_SpawnShieldOrb(player);
} // Now we know for certain that we don't have a bomb shield, so add one. :3
P_SwitchShield(player, SH_ARMAGEDDON);
S_StartSound(player->mo, actor->info->seesound); S_StartSound(player->mo, actor->info->seesound);
} }
@ -3323,22 +3338,8 @@ void A_WaterShield(mobj_t *actor)
player = actor->target->player; player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_ELEMENTAL) P_SwitchShield(player, SH_ELEMENTAL);
{
player->powers[pw_shield] = SH_ELEMENTAL|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)
P_RestoreMusic(player);
player->powers[pw_underwater] = 0;
if (player->powers[pw_spacetime] > 1)
{
player->powers[pw_spacetime] = 0;
P_RestoreMusic(player);
}
S_StartSound(player->mo, actor->info->seesound); S_StartSound(player->mo, actor->info->seesound);
} }
@ -3346,12 +3347,13 @@ void A_WaterShield(mobj_t *actor)
// //
// Description: Awards the player a force shield. // Description: Awards the player a force shield.
// //
// var1 = unused // var1 = Number of additional hitpoints to give
// var2 = unused // var2 = unused
// //
void A_ForceShield(mobj_t *actor) void A_ForceShield(mobj_t *actor)
{ {
player_t *player; player_t *player;
INT32 locvar1 = var1;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUA_CallAction("A_ForceShield", actor)) if (LUA_CallAction("A_ForceShield", actor))
@ -3363,15 +3365,15 @@ void A_ForceShield(mobj_t *actor)
return; return;
} }
if (locvar1 & ~SH_FORCEHP)
{
CONS_Debug(DBG_GAMELOGIC, "Invalid number of additional hitpoints.\n");
return;
}
player = actor->target->player; player = actor->target->player;
if (!(player->powers[pw_shield] & SH_FORCE)) P_SwitchShield(player, SH_FORCE|locvar1);
{
player->powers[pw_shield] = SH_FORCE|(player->powers[pw_shield] & SH_STACK)|0x01;
P_SpawnShieldOrb(player);
}
else
player->powers[pw_shield] = SH_FORCE|(player->powers[pw_shield] & SH_STACK)|0x01;
S_StartSound(player->mo, actor->info->seesound); S_StartSound(player->mo, actor->info->seesound);
} }
@ -3403,12 +3405,92 @@ void A_PityShield(mobj_t *actor)
player = actor->target->player; player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_PITY) P_SwitchShield(player, SH_PITY);
S_StartSound(player->mo, actor->info->seesound);
}
// Function: A_FlameShield
//
// Description: Awards the player a flame shield.
//
// var1 = unused
// var2 = unused
//
void A_FlameShield(mobj_t *actor)
{
player_t *player;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlameShield", actor))
return;
#endif
if (!actor->target || !actor->target->player)
{ {
player->powers[pw_shield] = SH_PITY+(player->powers[pw_shield] & SH_STACK); CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
P_SpawnShieldOrb(player); return;
} }
player = actor->target->player;
P_SwitchShield(player, SH_FLAMEAURA);
S_StartSound(player->mo, actor->info->seesound);
}
// Function: A_BubbleShield
//
// Description: Awards the player a bubble shield.
//
// var1 = unused
// var2 = unused
//
void A_BubbleShield(mobj_t *actor)
{
player_t *player;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_BubbleShield", actor))
return;
#endif
if (!actor->target || !actor->target->player)
{
CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
return;
}
player = actor->target->player;
P_SwitchShield(player, SH_BUBBLEWRAP);
S_StartSound(player->mo, actor->info->seesound);
}
// Function: A_ThunderShield
//
// Description: Awards the player a thunder shield.
//
// var1 = unused
// var2 = unused
//
void A_ThunderShield(mobj_t *actor)
{
player_t *player;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_ThunderShield", actor))
return;
#endif
if (!actor->target || !actor->target->player)
{
CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
return;
}
player = actor->target->player;
P_SwitchShield(player, SH_THUNDERCOIN);
S_StartSound(player->mo, actor->info->seesound); S_StartSound(player->mo, actor->info->seesound);
} }
@ -3435,9 +3517,10 @@ void A_GravityBox(mobj_t *actor)
} }
player = actor->target->player; player = actor->target->player;
player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
S_StartSound(player, actor->info->activesound); S_StartSound(player, actor->info->activesound);
player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
} }
// Function: A_ScoreRise // Function: A_ScoreRise
@ -3459,41 +3542,49 @@ void A_ScoreRise(mobj_t *actor)
// Function: A_ParticleSpawn // Function: A_ParticleSpawn
// //
// Description: Spawns a particle at a specified interval // Description: Hyper-specialised function for spawning a particle for MT_PARTICLEGEN.
// //
// var1 = type (if 0, defaults to MT_PARTICLE) // var1 = unused
// var2 = unused // var2 = unused
// //
void A_ParticleSpawn(mobj_t *actor) void A_ParticleSpawn(mobj_t *actor)
{ {
INT32 locvar1 = var1; INT32 i = 0;
fixed_t speed;
mobjtype_t type;
mobj_t *spawn; mobj_t *spawn;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUA_CallAction("A_ParticleSpawn", actor)) if (LUA_CallAction("A_ParticleSpawn", actor))
return; return;
#endif #endif
if (!actor->spawnpoint) if (!actor->health)
{
P_RemoveMobj(actor);
return; return;
if (!actor->lastlook)
return;
if (!actor->threshold)
return;
for (i = 0; i < actor->lastlook; i++)
{
spawn = P_SpawnMobj(
actor->x + FixedMul(FixedMul(actor->friction, actor->scale), FINECOSINE(actor->angle>>ANGLETOFINESHIFT)),
actor->y + FixedMul(FixedMul(actor->friction, actor->scale), FINESINE(actor->angle>>ANGLETOFINESHIFT)),
actor->z,
(mobjtype_t)actor->threshold);
P_SetScale(spawn, actor->scale);
spawn->momz = FixedMul(actor->movefactor, spawn->scale);
spawn->destscale = spawn->scale/100;
spawn->scalespeed = spawn->scale/actor->health;
spawn->tics = (tic_t)actor->health;
spawn->flags2 |= (actor->flags2 & MF2_OBJECTFLIP);
spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones
if (spawn->frame & FF_ANIMATE)
spawn->frame += P_RandomKey(spawn->state->var1);
actor->angle += actor->movedir;
} }
actor->angle += (angle_t)actor->movecount;
if (locvar1)
type = (mobjtype_t)locvar1;
else
type = MT_PARTICLE;
speed = FixedMul((actor->spawnpoint->angle >> 12)<<FRACBITS, actor->scale);
spawn = P_SpawnMobj(actor->x, actor->y, actor->z, type);
P_SetScale(spawn, actor->scale);
spawn->momz = speed;
spawn->destscale = FixedDiv(spawn->scale<<FRACBITS, 100<<FRACBITS);
spawn->scalespeed = FixedDiv(((actor->spawnpoint->angle >> 8) & 63) << FRACBITS, 100<<FRACBITS);
actor->tics = actor->spawnpoint->extrainfo + 1;
} }
// Function: A_BunnyHop // Function: A_BunnyHop
@ -3703,7 +3794,7 @@ void A_AttractChase(mobj_t *actor)
// Turn flingrings back into regular rings if attracted. // Turn flingrings back into regular rings if attracted.
if (actor->tracer && actor->tracer->player if (actor->tracer && actor->tracer->player
&& (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) != SH_ATTRACT && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime) && !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
{ {
mobj_t *newring; mobj_t *newring;
newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime); newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime);
@ -3807,11 +3898,18 @@ void A_DropMine(mobj_t *actor)
void A_FishJump(mobj_t *actor) void A_FishJump(mobj_t *actor)
{ {
INT32 locvar1 = var1; INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUA_CallAction("A_FishJump", actor)) if (LUA_CallAction("A_FishJump", actor))
return; return;
#endif #endif
if (locvar2)
{
fixed_t rad = actor->radius>>FRACBITS;
P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<<FRACBITS, P_RandomRange(rad, -rad)<<FRACBITS, 0, (mobjtype_t)locvar2);
}
if ((actor->z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale))) if ((actor->z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale)))
{ {
fixed_t jumpval; fixed_t jumpval;
@ -3908,7 +4006,7 @@ void A_ThrownRing(mobj_t *actor)
// A non-homing ring getting attracted by a // A non-homing ring getting attracted by a
// magnetic player. If he gets too far away, make // magnetic player. If he gets too far away, make
// sure to stop the attraction! // sure to stop the attraction!
if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x, && P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x,
actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale))) actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale)))
{ {
@ -3916,7 +4014,7 @@ void A_ThrownRing(mobj_t *actor)
} }
if (actor->tracer && (actor->tracer->health) if (actor->tracer && (actor->tracer->health)
&& (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT)// Already found someone to follow. && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow.
{ {
const INT32 temp = actor->threshold; const INT32 temp = actor->threshold;
actor->threshold = 32000; actor->threshold = 32000;
@ -3984,7 +4082,7 @@ void A_ThrownRing(mobj_t *actor)
if (!P_CheckSight(actor, player->mo)) if (!P_CheckSight(actor, player->mo))
continue; // out of sight continue; // out of sight
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& dist < FixedMul(RING_DIST/4, player->mo->scale)) && dist < FixedMul(RING_DIST/4, player->mo->scale))
P_SetTarget(&actor->tracer, player->mo); P_SetTarget(&actor->tracer, player->mo);
return; return;
@ -7763,7 +7861,7 @@ void A_SetObjectFlags(mobj_t *actor)
else if (locvar2 == 1) else if (locvar2 == 1)
locvar1 = actor->flags & ~locvar1; locvar1 = actor->flags & ~locvar1;
if ((locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
unlinkthings = true; unlinkthings = true;
if (unlinkthings) { if (unlinkthings) {
@ -8379,7 +8477,7 @@ void A_RingDrain(mobj_t *actor)
} }
player = actor->target->player; player = actor->target->player;
P_GivePlayerRings(player, -min(locvar1, player->mo->health-1)); P_GivePlayerRings(player, -min(locvar1, player->rings));
} }
// Function: A_SplitShot // Function: A_SplitShot
@ -8687,7 +8785,7 @@ void A_CheckTargetRings(mobj_t *actor)
if (!(actor->target) || !(actor->target->player)) if (!(actor->target) || !(actor->target->player))
return; return;
if (actor->target->player->health >= locvar1) if (actor->target->player->rings >= locvar1)
P_SetMobjState(actor, locvar2); P_SetMobjState(actor, locvar2);
} }
@ -8709,7 +8807,7 @@ void A_CheckRings(mobj_t *actor)
#endif #endif
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
cntr += players[i].health-1; cntr += players[i].rings;
if (cntr >= locvar1) if (cntr >= locvar1)
P_SetMobjState(actor, locvar2); P_SetMobjState(actor, locvar2);
@ -9281,7 +9379,7 @@ void A_ForceWin(mobj_t *actor)
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && (players[i].health > 0 if (playeringame[i] && ((players[i].mo && players[i].mo->health > 0)
|| ((netgame || multiplayer) && (players[i].lives > 0 || players[i].continues > 0)))) || ((netgame || multiplayer) && (players[i].lives > 0 || players[i].continues > 0))))
break; break;
} }
@ -9548,14 +9646,19 @@ void A_HomingChase(mobj_t *actor)
// lower 16 bits = object # to fire // lower 16 bits = object # to fire
// upper 16 bits = front offset // upper 16 bits = front offset
// var2: // var2:
// lower 16 bits = vertical angle // lower 15 bits = vertical angle variable
// 16th bit:
// - 0: use vertical angle variable as vertical angle in degrees
// - 1: mimic P_SpawnXYZMissile
// use z of actor minus z of missile as vertical distance to cover during momz calculation
// use vertical angle variable as horizontal distance to cover during momz calculation
// upper 16 bits = height offset // upper 16 bits = height offset
// //
void A_TrapShot(mobj_t *actor) void A_TrapShot(mobj_t *actor)
{ {
INT32 locvar1 = var1; INT32 locvar1 = var1;
INT32 locvar2 = var2; INT32 locvar2 = var2;
angle_t vertang = FixedAngle(((INT16)(locvar2 & 65535))*FRACUNIT); boolean oldstyle = (locvar2 & 32768) ? true : false;
mobjtype_t type = (mobjtype_t)(locvar1 & 65535); mobjtype_t type = (mobjtype_t)(locvar1 & 65535);
mobj_t *missile; mobj_t *missile;
INT16 frontoff = (INT16)(locvar1 >> 16); INT16 frontoff = (INT16)(locvar1 >> 16);
@ -9571,10 +9674,7 @@ void A_TrapShot(mobj_t *actor)
y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale)); y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
if (actor->eflags & MFE_VERTICALFLIP) if (actor->eflags & MFE_VERTICALFLIP)
{
z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale); z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale);
vertang = InvAngle(vertang); // flip firing angle
}
else else
z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale); z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale);
@ -9585,20 +9685,35 @@ void A_TrapShot(mobj_t *actor)
if (actor->eflags & MFE_VERTICALFLIP) if (actor->eflags & MFE_VERTICALFLIP)
missile->flags2 |= MF2_OBJECTFLIP; missile->flags2 |= MF2_OBJECTFLIP;
missile->destscale = actor->destscale;
P_SetScale(missile, missile->destscale); missile->destscale = actor->scale;
P_SetScale(missile, actor->scale);
if (missile->info->seesound) if (missile->info->seesound)
S_StartSound(actor, missile->info->seesound); S_StartSound(missile, missile->info->seesound);
P_SetTarget(&missile->target, actor); P_SetTarget(&missile->target, actor);
missile->angle = actor->angle; missile->angle = actor->angle;
speed = FixedMul(missile->info->speed, missile->scale); speed = FixedMul(missile->info->speed, missile->scale);
missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed)); if (oldstyle)
missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed)); {
missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed); missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed);
missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed);
// The below line basically mimics P_SpawnXYZMissile's momz calculation.
missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed);
P_CheckMissileSpawn(missile);
}
else
{
angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT);
if (actor->eflags & MFE_VERTICALFLIP)
vertang = InvAngle(vertang); // flip firing angle
missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed));
missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed));
missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed);
}
} }
// Function: A_VileTarget // Function: A_VileTarget
@ -10271,3 +10386,421 @@ void A_SpawnFreshCopy(mobj_t *actor)
if (newObject->info->seesound) if (newObject->info->seesound)
S_StartSound(newObject, newObject->info->seesound); S_StartSound(newObject, newObject->info->seesound);
} }
// Internal Flicky spawning function.
mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers)
{
mobj_t *flicky;
if (!flickytype)
{
if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
return NULL;
else
{
INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies);
flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
}
}
flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype);
flicky->angle = actor->angle;
if (flickytype == MT_SEED)
flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2;
if (actor->eflags & MFE_UNDERWATER)
momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
P_SetObjectMomZ(flicky, momz, false);
flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1);
flicky->fuse = P_RandomRange(595, 700); // originally 300, 350
flicky->threshold = 0;
if (lookforplayers)
P_LookForPlayers(flicky, true, false, 0);
return flicky;
}
// Function: A_FlickySpawn
//
// Description: Flicky spawning function.
//
// var1:
// lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
// upper 16 bits: if 0, no sound is played. Else, A_Scream is called.
// var2 = upwards thrust for spawned flicky. If zero, default value is provided.
//
void A_FlickySpawn(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickySpawn", actor))
return;
#endif
if (locvar1 >> 16) {
A_Scream(actor); // A shortcut for the truly lazy.
locvar1 &= 65535;
}
P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true);
}
// Internal Flicky bubbling function.
void P_InternalFlickyBubble(mobj_t *actor)
{
if (actor->eflags & MFE_UNDERWATER)
{
mobj_t *overlay;
if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer)
return;
overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate);
P_SetTarget(&actor->tracer, overlay);
P_SetTarget(&overlay->target, actor);
return;
}
if (!actor->tracer || P_MobjWasRemoved(actor->tracer))
return;
P_RemoveMobj(actor->tracer);
P_SetTarget(&actor->tracer, NULL);
}
// Function: A_FlickyAim
//
// Description: Flicky aiming function.
//
// var1 = how far around the target (in angle constants) the flicky should look
// var2 = distance from target to aim for
//
void A_FlickyAim(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
boolean flickyhitwall = false;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyAim", actor))
return;
#endif
if (actor->momx == actor->momy && actor->momy == 0)
flickyhitwall = true;
P_InternalFlickyBubble(actor);
P_InstaThrust(actor, 0, 0);
if (!actor->target)
{
P_LookForPlayers(actor, true, false, 0);
actor->angle = P_RandomKey(36)*ANG10;
return;
}
if (actor->fuse > 2*TICRATE)
{
angle_t posvar;
fixed_t chasevar, chasex, chasey;
if (flickyhitwall)
actor->movedir *= -1;
posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK;
chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2;
chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar);
chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar);
if (P_AproxDistance(chasex - actor->x, chasey - actor->y))
actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey);
}
else if (flickyhitwall)
{
actor->angle += ANGLE_180;
actor->threshold = 0;
}
}
//Internal Flicky flying function. Also usuable as an underwater swim thrust.
void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez)
{
angle_t vertangle;
flyspeed = FixedMul(flyspeed, actor->scale);
actor->flags |= MF_NOGRAVITY;
var1 = ANG30;
var2 = 32*FRACUNIT;
A_FlickyAim(actor);
chasez *= 8;
if (!actor->target || !(actor->fuse > 2*TICRATE))
chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT);
else
{
fixed_t add = actor->target->z + (actor->target->height - actor->height)/2;
if (add > (actor->ceilingz - 24*actor->scale - actor->height))
add = actor->ceilingz - 24*actor->scale - actor->height;
else if (add < (actor->floorz + 24*actor->scale))
add = actor->floorz + 24*actor->scale;
chasez += add;
}
if (!targetdist)
targetdist = 16*FRACUNIT; //Default!
if (actor->target && abs(chasez - actor->z) > targetdist)
targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK;
P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed));
actor->momz = FixedMul(FINESINE(vertangle), flyspeed);
}
// Function: A_FlickyFly
//
// Description: Flicky flying function.
//
// var1 = how fast to fly
// var2 = how far ahead the target should be considered
//
void A_FlickyFly(mobj_t *actor)
{
// We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead.
//INT32 locvar1 = var1;
//INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyFly", actor))
return;
#endif
P_InternalFlickyFly(actor, var1, var2,
FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK)
);
}
// Function: A_FlickySoar
//
// Description: Flicky soaring function - specific to puffin.
//
// var1 = how fast to fly
// var2 = how far ahead the target should be considered
//
void A_FlickySoar(mobj_t *actor)
{
// We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead.
//INT32 locvar1 = var1;
//INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickySoar", actor))
return;
#endif
P_InternalFlickyFly(actor, var1, var2,
2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK)))
);
if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10)
actor->frame = 3;
}
//Function: A_FlickyCoast
//
// Description: Flicky swim-coasting function.
//
// var1 = speed to change state upon reaching
// var2 = state to change to upon slowing down
// the spawnstate of the mobj = state to change to when above water
//
void A_FlickyCoast(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyCoast", actor))
return;
#endif
if (actor->eflags & MFE_UNDERWATER)
{
actor->momx = (11*actor->momx)/12;
actor->momy = (11*actor->momy)/12;
actor->momz = (11*actor->momz)/12;
if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1)
P_SetMobjState(actor, locvar2);
return;
}
actor->flags &= ~MF_NOGRAVITY;
P_SetMobjState(actor, mobjinfo[actor->type].spawnstate);
}
// Internal Flicky hopping function.
void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle)
{
if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
{
if (momz)
{
if (actor->eflags & MFE_UNDERWATER)
momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
P_SetObjectMomZ(actor, momz, false);
}
P_InstaThrust(actor, angle, FixedMul(momh, actor->scale));
}
}
// Function: A_FlickyHop
//
// Description: Flicky hopping function.
//
// var1 = vertical thrust
// var2 = horizontal thrust
//
void A_FlickyHop(mobj_t *actor)
{
// We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyHop instead.
//INT32 locvar1 = var1;
//INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyHop", actor))
return;
#endif
P_InternalFlickyHop(actor, var1, var2, actor->angle);
}
// Function: A_FlickyFlounder
//
// Description: Flicky floundering function.
//
// var1 = intended vertical thrust
// var2 = intended horizontal thrust
//
void A_FlickyFlounder(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
angle_t hopangle;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyFlounder", actor))
return;
#endif
locvar1 *= (P_RandomKey(2) + 1);
locvar2 *= (P_RandomKey(2) + 1);
hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2);
P_InternalFlickyHop(actor, locvar1, locvar2, hopangle);
}
// Function: A_FlickyCheck
//
// Description: Flicky airtime check function.
//
// var1 = state to change to upon touching the floor
// var2 = state to change to upon falling
// the meleestate of the mobj = state to change to when underwater
//
void A_FlickyCheck(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyCheck", actor))
return;
#endif
if (locvar2 && P_MobjFlip(actor)*actor->momz < 1)
P_SetMobjState(actor, locvar2);
else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
P_SetMobjState(actor, locvar1);
else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER))
P_SetMobjState(actor, mobjinfo[actor->type].meleestate);
P_InternalFlickyBubble(actor);
}
// Function: A_FlickyHeightCheck
//
// Description: Flicky height check function.
//
// var1 = state to change to when falling below height relative to target
// var2 = height relative to target to change state at
//
void A_FlickyHeightCheck(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyHeightCheck", actor))
return;
#endif
if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1
&& ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2)
|| (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz)))
P_SetMobjState(actor, locvar1);
P_InternalFlickyBubble(actor);
}
// Function: A_FlickyFlutter
//
// Description: Flicky fluttering function - specific to chicken.
//
// var1 = state to change to upon touching the floor
// var2 = state to change to upon falling
// the meleestate of the mobj = state to change to when underwater
//
void A_FlickyFlutter(mobj_t *actor)
{
// We're not setting up locvars here - it passes var1 and var2 through to A_FlickyCheck instead.
//INT32 locvar1 = var1;
//INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyFlutter", actor))
return;
#endif
A_FlickyCheck(actor);
var1 = ANG30;
var2 = 32*FRACUNIT;
A_FlickyAim(actor);
P_InstaThrust(actor, actor->angle, 2*actor->scale);
if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2)
actor->momz = -P_MobjFlip(actor)*actor->scale/2;
}
#undef FLICKYHITWALL
// Function: A_FlameParticle
//
// Description: Creates the mobj's painchance at a random position around the object's radius.
//
// var1 = momz of particle.
//
void A_FlameParticle(mobj_t *actor)
{
mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance);
INT32 locvar1 = var1;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlameParticle", actor))
return;
#endif
if (type)
{
fixed_t rad = 2*actor->radius>>FRACBITS;
fixed_t hei = actor->height>>FRACBITS;
mobj_t *particle = P_SpawnMobjFromMobj(actor,
P_RandomRange(rad, -rad)<<FRACBITS,
P_RandomRange(rad, -rad)<<FRACBITS,
P_RandomRange(hei/2, hei)<<FRACBITS,
type);
P_SetObjectMomZ(particle, locvar1<<FRACBITS, false);
}
}

View file

@ -13,7 +13,11 @@
#include "doomdef.h" #include "doomdef.h"
#include "doomstat.h" #include "doomstat.h"
#include "m_random.h"
#include "p_local.h" #include "p_local.h"
#ifdef ESLOPE
#include "p_slopes.h"
#endif
#include "r_state.h" #include "r_state.h"
#include "s_sound.h" #include "s_sound.h"
#include "z_zone.h" #include "z_zone.h"
@ -1141,6 +1145,7 @@ void T_MarioBlock(levelspecthink_t *block)
block->sector->ceilingdata = NULL; block->sector->ceilingdata = NULL;
block->sector->floorspeed = 0; block->sector->floorspeed = 0;
block->sector->ceilspeed = 0; block->sector->ceilspeed = 0;
block->direction = 0;
} }
for (i = -1; (i = P_FindSectorFromTag((INT16)block->vars[0], i)) >= 0 ;) for (i = -1; (i = P_FindSectorFromTag((INT16)block->vars[0], i)) >= 0 ;)
@ -1748,12 +1753,15 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
case MT_GHOST: case MT_GHOST:
case MT_OVERLAY: case MT_OVERLAY:
case MT_EMERALDSPAWN: case MT_EMERALDSPAWN:
case MT_GREENORB: case MT_ELEMENTAL_ORB:
case MT_YELLOWORB: case MT_ATTRACT_ORB:
case MT_BLUEORB: case MT_FORCE_ORB:
case MT_BLACKORB: case MT_ARMAGEDDON_ORB:
case MT_WHITEORB: case MT_WHIRLWIND_ORB:
case MT_PITYORB: case MT_PITY_ORB:
case MT_FLAMEAURA_ORB:
case MT_BUBBLEWRAP_ORB:
case MT_THUNDERCOIN_ORB:
case MT_IVSP: case MT_IVSP:
case MT_SUPERSPARK: case MT_SUPERSPARK:
case MT_RAIN: case MT_RAIN:
@ -1782,8 +1790,8 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
break; break;
} }
// Ignore popped monitors, too. // Ignore popped monitors, too.
if (node->m_thing->flags & MF_MONITOR if (node->m_thing->health == 0 // this only really applies for monitors
&& node->m_thing->threshold == 68) || (!(node->m_thing->flags & MF_MONITOR) && (mobjinfo[node->m_thing->type].flags & MF_MONITOR))) // gold monitor support
continue; continue;
// Okay, we found something valid. // Okay, we found something valid.
if (!thing // take either the first thing if (!thing // take either the first thing
@ -1797,10 +1805,24 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
void T_MarioBlockChecker(levelspecthink_t *block) void T_MarioBlockChecker(levelspecthink_t *block)
{ {
line_t *masterline = block->sourceline; line_t *masterline = block->sourceline;
if (block->vars[2] == 1) // Don't update the textures when the block's being bumped upwards.
return;
if (SearchMarioNode(block->sector->touching_thinglist)) if (SearchMarioNode(block->sector->touching_thinglist))
sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].bottomtexture; {
sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].bottomtexture; // Update textures
if (masterline->backsector)
{
block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->ceilingpic; // Update flats to be backside's ceiling
}
}
else else
{
sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].toptexture; sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].toptexture;
if (masterline->backsector)
{
block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->floorpic; // Update flats to be backside's floor
}
}
} }
// This is the Thwomp's 'brain'. It looks around for players nearby, and if // This is the Thwomp's 'brain'. It looks around for players nearby, and if
@ -2520,6 +2542,29 @@ void T_CameraScanner(elevator_t *elevator)
} }
} }
void T_PlaneDisplace(planedisplace_t *pd)
{
sector_t *control, *target;
INT32 direction;
fixed_t diff;
control = &sectors[pd->control];
target = &sectors[pd->affectee];
if (control->floorheight == pd->last_height)
return; // no change, no movement
direction = (control->floorheight > pd->last_height) ? 1 : -1;
diff = FixedMul(control->floorheight-pd->last_height, pd->speed);
if (pd->type == pd_floor || pd->type == pd_both)
T_MovePlane(target, INT32_MAX/2, target->floorheight+diff, 0, 0, direction); // move floor
if (pd->type == pd_ceiling || pd->type == pd_both)
T_MovePlane(target, INT32_MAX/2, target->ceilingheight+diff, 0, 1, direction); // move ceiling
pd->last_height = control->floorheight;
}
// //
// EV_DoFloor // EV_DoFloor
// //
@ -2877,18 +2922,41 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover)
size_t topmostvertex = 0, bottommostvertex = 0; size_t topmostvertex = 0, bottommostvertex = 0;
fixed_t leftx, rightx; fixed_t leftx, rightx;
fixed_t topy, bottomy; fixed_t topy, bottomy;
fixed_t topz; fixed_t topz, bottomz;
fixed_t widthfactor, heightfactor;
fixed_t a, b, c; fixed_t a, b, c;
mobjtype_t type = MT_ROCKCRUMBLE1; mobjtype_t type = MT_ROCKCRUMBLE1;
fixed_t spacing = (32<<FRACBITS);
tic_t lifetime = 3*TICRATE;
INT16 flags = 0;
// If the control sector has a special #define controlsec rover->master->frontsector
// of Section3:7-15, use the custom debris.
if (GETSECSPECIAL(rover->master->frontsector->special, 3) >= 8) if (controlsec->tag != 0)
type = MT_ROCKCRUMBLE1+(GETSECSPECIAL(rover->master->frontsector->special, 3)-7); {
INT32 tagline = P_FindSpecialLineFromTag(14, controlsec->tag, -1);
if (tagline != -1)
{
if (sides[lines[tagline].sidenum[0]].toptexture)
type = (mobjtype_t)sides[lines[tagline].sidenum[0]].toptexture; // Set as object type in p_setup.c...
if (sides[lines[tagline].sidenum[0]].textureoffset)
spacing = sides[lines[tagline].sidenum[0]].textureoffset;
if (sides[lines[tagline].sidenum[0]].rowoffset)
{
if (sides[lines[tagline].sidenum[0]].rowoffset>>FRACBITS != -1)
lifetime = (sides[lines[tagline].sidenum[0]].rowoffset>>FRACBITS);
else
lifetime = 0;
}
flags = lines[tagline].flags;
}
}
#undef controlsec
// soundorg z height never gets set normally, so MEH. // soundorg z height never gets set normally, so MEH.
sec->soundorg.z = sec->floorheight; sec->soundorg.z = sec->floorheight;
S_StartSound(&sec->soundorg, sfx_crumbl); S_StartSound(&sec->soundorg, mobjinfo[type].activesound);
// Find the outermost vertexes in the subsector // Find the outermost vertexes in the subsector
for (i = 0; i < sec->linecount; i++) for (i = 0; i < sec->linecount; i++)
@ -2907,23 +2975,46 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover)
bottommostvertex = i; bottommostvertex = i;
} }
leftx = sec->lines[leftmostvertex]->v1->x+(16<<FRACBITS); leftx = sec->lines[leftmostvertex]->v1->x+(spacing>>1);
rightx = sec->lines[rightmostvertex]->v1->x; rightx = sec->lines[rightmostvertex]->v1->x;
topy = sec->lines[topmostvertex]->v1->y-(16<<FRACBITS); topy = sec->lines[topmostvertex]->v1->y-(spacing>>1);
bottomy = sec->lines[bottommostvertex]->v1->y; bottomy = sec->lines[bottommostvertex]->v1->y;
topz = *rover->topheight-(16<<FRACBITS);
for (a = leftx; a < rightx; a += (32<<FRACBITS)) topz = *rover->topheight-(spacing>>1);
bottomz = *rover->bottomheight;
if (flags & ML_EFFECT1)
{ {
for (b = topy; b > bottomy; b -= (32<<FRACBITS)) widthfactor = (rightx + topy - leftx - bottomy)>>3;
heightfactor = (topz - *rover->bottomheight)>>2;
}
for (a = leftx; a < rightx; a += spacing)
{
for (b = topy; b > bottomy; b -= spacing)
{ {
if (R_PointInSubsector(a, b)->sector == sec) if (R_PointInSubsector(a, b)->sector == sec)
{ {
mobj_t *spawned = NULL; mobj_t *spawned = NULL;
for (c = topz; c > *rover->bottomheight; c -= (32<<FRACBITS)) #ifdef ESLOPE
if (*rover->t_slope)
topz = P_GetZAt(*rover->t_slope, a, b) - (spacing>>1);
if (*rover->b_slope)
bottomz = P_GetZAt(*rover->b_slope, a, b);
#endif
for (c = topz; c > bottomz; c -= spacing)
{ {
spawned = P_SpawnMobj(a, b, c, type); spawned = P_SpawnMobj(a, b, c, type);
spawned->fuse = 3*TICRATE; spawned->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones
if (flags & ML_EFFECT1)
{
P_InstaThrust(spawned, R_PointToAngle2(sec->soundorg.x, sec->soundorg.y, a, b), FixedDiv(P_AproxDistance(a - sec->soundorg.x, b - sec->soundorg.y), widthfactor));
P_SetObjectMomZ(spawned, FixedDiv((c - bottomz), heightfactor), false);
}
spawned->fuse = lifetime;
} }
} }
} }
@ -3083,8 +3174,10 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating,
return 1; return 1;
} }
INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mobj_t *puncher) INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
{ {
sector_t *roversec = rover->master->frontsector;
fixed_t topheight = *rover->topheight;
levelspecthink_t *block; levelspecthink_t *block;
mobj_t *thing; mobj_t *thing;
fixed_t oldx = 0, oldy = 0, oldz = 0; fixed_t oldx = 0, oldy = 0, oldz = 0;
@ -3092,11 +3185,14 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
I_Assert(puncher != NULL); I_Assert(puncher != NULL);
I_Assert(puncher->player != NULL); I_Assert(puncher->player != NULL);
if (sec->floordata || sec->ceilingdata) if (roversec->floordata || roversec->ceilingdata)
return 0; return 0;
if (!(rover->flags & FF_SOLID))
rover->flags |= (FF_SOLID|FF_RENDERALL|FF_CUTLEVEL);
// Find an item to pop out! // Find an item to pop out!
thing = SearchMarioNode(sec->touching_thinglist); thing = SearchMarioNode(roversec->touching_thinglist);
// Found something! // Found something!
if (thing) if (thing)
@ -3106,13 +3202,13 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL); block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL);
P_AddThinker(&block->thinker); P_AddThinker(&block->thinker);
sec->floordata = block; roversec->floordata = block;
sec->ceilingdata = block; roversec->ceilingdata = block;
block->thinker.function.acp1 = (actionf_p1)T_MarioBlock; block->thinker.function.acp1 = (actionf_p1)T_MarioBlock;
// Set up the fields // Set up the fields
block->sector = sec; block->sector = roversec;
block->vars[0] = roversector->tag; // actionsector block->vars[0] = sector->tag; // actionsector
block->vars[1] = 4*FRACUNIT; // speed block->vars[1] = 4*FRACUNIT; // speed
block->vars[2] = 1; // Up // direction block->vars[2] = 1; // Up // direction
block->vars[3] = block->sector->floorheight; // floorwasheight block->vars[3] = block->sector->floorheight; // floorwasheight
@ -3128,8 +3224,8 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
} }
P_UnsetThingPosition(thing); P_UnsetThingPosition(thing);
thing->x = roversector->soundorg.x; thing->x = sector->soundorg.x;
thing->y = roversector->soundorg.y; thing->y = sector->soundorg.y;
thing->z = topheight; thing->z = topheight;
thing->momz = FixedMul(6*FRACUNIT, thing->scale); thing->momz = FixedMul(6*FRACUNIT, thing->scale);
P_SetThingPosition(thing); P_SetThingPosition(thing);
@ -3146,7 +3242,7 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
{ {
if (thing->type == MT_EMMY && thing->spawnpoint && (thing->spawnpoint->options & MTF_OBJECTSPECIAL)) if (thing->type == MT_EMMY && thing->spawnpoint && (thing->spawnpoint->options & MTF_OBJECTSPECIAL))
{ {
mobj_t *tokenobj = P_SpawnMobj(roversector->soundorg.x, roversector->soundorg.y, topheight, MT_TOKEN); mobj_t *tokenobj = P_SpawnMobj(sector->soundorg.x, sector->soundorg.y, topheight, MT_TOKEN);
P_SetTarget(&thing->tracer, tokenobj); P_SetTarget(&thing->tracer, tokenobj);
P_SetTarget(&tokenobj->target, thing); P_SetTarget(&tokenobj->target, thing);
P_SetMobjState(tokenobj, mobjinfo[MT_TOKEN].seestate); P_SetMobjState(tokenobj, mobjinfo[MT_TOKEN].seestate);
@ -3156,15 +3252,15 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
S_StartSound(puncher, sfx_mario9); // Puncher is "close enough" S_StartSound(puncher, sfx_mario9); // Puncher is "close enough"
} }
if (itsamonitor) if (itsamonitor && thing)
{ {
P_UnsetThingPosition(tmthing); P_UnsetThingPosition(thing);
tmthing->x = oldx; thing->x = oldx;
tmthing->y = oldy; thing->y = oldy;
tmthing->z = oldz; thing->z = oldz;
tmthing->momx = 1; thing->momx = 1;
tmthing->momy = 1; thing->momy = 1;
P_SetThingPosition(tmthing); P_SetThingPosition(thing);
} }
} }
else else

View file

@ -142,7 +142,10 @@ boolean P_CanPickupItem(player_t *player, boolean weapon)
if (player->bot && weapon) if (player->bot && weapon)
return false; return false;
if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] <= flashingtics) if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] < UINT16_MAX)
return false;
if (player->mo && player->mo->health <= 0)
return false; return false;
return true; return true;
@ -221,6 +224,65 @@ void P_DoNightsScore(player_t *player)
dummymo->destscale = 2*FRACUNIT; dummymo->destscale = 2*FRACUNIT;
} }
//
// P_DoMatchSuper
//
// Checks if you have all 7 pw_emeralds, then turns you "super". =P
//
void P_DoMatchSuper(player_t *player)
{
UINT16 match_emeralds = player->powers[pw_emeralds];
boolean doteams = false;
int i;
// If this gametype has teams, check every player on your team for emeralds.
if (G_GametypeHasTeams())
{
doteams = true;
for (i = 0; i < MAXPLAYERS; i++)
if (players[i].ctfteam == player->ctfteam)
match_emeralds |= players[i].powers[pw_emeralds];
}
if (!ALL7EMERALDS(match_emeralds))
return;
// Got 'em all? Turn "super"!
emeraldspawndelay = invulntics + 1;
player->powers[pw_emeralds] = 0;
player->powers[pw_invulnerability] = emeraldspawndelay;
player->powers[pw_sneakers] = emeraldspawndelay;
if (P_IsLocalPlayer(player) && !player->powers[pw_super])
{
S_StopMusic();
if (mariomode)
G_GhostAddColor(GHC_INVINCIBLE);
S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
}
// Also steal 50 points from every enemy, sealing your victory.
P_StealPlayerScore(player, 50);
// In a team game?
// Check everyone else on your team for emeralds, and turn those helpful assisting players invincible too.
if (doteams)
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].ctfteam == player->ctfteam
&& players[i].powers[pw_emeralds] != 0)
{
players[i].powers[pw_emeralds] = 0;
player->powers[pw_invulnerability] = invulntics + 1;
player->powers[pw_sneakers] = player->powers[pw_invulnerability];
if (P_IsLocalPlayer(player) && !player->powers[pw_super])
{
S_StopMusic();
if (mariomode)
G_GhostAddColor(GHC_INVINCIBLE);
S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
}
}
}
/** Takes action based on a ::MF_SPECIAL thing touched by a player. /** Takes action based on a ::MF_SPECIAL thing touched by a player.
* Actually, this just checks a few things (heights, toucher->player, no * Actually, this just checks a few things (heights, toucher->player, no
* objectplace, no dead or disappearing things) * objectplace, no dead or disappearing things)
@ -237,6 +299,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{ {
player_t *player; player_t *player;
INT32 i; INT32 i;
UINT8 elementalpierce;
if (objectplacing) if (objectplacing)
return; return;
@ -291,6 +354,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return; return;
#endif #endif
// 0 = none, 1 = elemental pierce, 2 = bubble bounce
elementalpierce = (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (player->pflags & PF_SHIELDABILITY)
? (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2)
: 0);
if (special->flags & MF_BOSS) if (special->flags & MF_BOSS)
{ {
if (special->type == MT_BLACKEGGMAN) if (special->type == MT_BLACKEGGMAN)
@ -300,14 +368,20 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
} }
if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING)) if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
|| ((player->pflags & PF_JUMPED) && !(player->charflags & SF_NOJUMPDAMAGE && !(player->charability == CA_TWINSPIN && player->panim == PA_ABILITY))) || ((player->pflags & PF_JUMPED) && (player->pflags & PF_FORCEJUMPDAMAGE || !(player->charflags & SF_NOJUMPSPIN) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| (player->pflags & (PF_SPINNING|PF_GLIDING)) || (player->pflags & (PF_SPINNING|PF_GLIDING))
|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2) || (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
|| ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0)) || ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object? || player->powers[pw_invulnerability] || player->powers[pw_super]
|| elementalpierce) // Do you possess the ability to subdue the object?
{ {
if (P_MobjFlip(toucher)*toucher->momz < 0) if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1))
toucher->momz = -toucher->momz; {
if (elementalpierce == 2)
P_DoBubbleBounce(player);
else
toucher->momz = -toucher->momz;
}
toucher->momx = -toucher->momx; toucher->momx = -toucher->momx;
toucher->momy = -toucher->momy; toucher->momy = -toucher->momy;
P_DamageMobj(special, toucher, toucher, 1, 0); P_DamageMobj(special, toucher, toucher, 1, 0);
@ -333,7 +407,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
/////ENEMIES!!////////////////////////////////////////// /////ENEMIES!!//////////////////////////////////////////
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
if (special->type == MT_GSNAPPER && !(((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING)) if (special->type == MT_GSNAPPER && !(((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
|| player->powers[pw_invulnerability] || player->powers[pw_super]) || player->powers[pw_invulnerability] || player->powers[pw_super] || elementalpierce)
&& toucher->z < special->z + special->height && toucher->z + toucher->height > special->z) && toucher->z < special->z + special->height && toucher->z + toucher->height > special->z)
{ {
// Can only hit snapper from above // Can only hit snapper from above
@ -346,14 +420,19 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_DamageMobj(toucher, special, special, 1, 0); P_DamageMobj(toucher, special, special, 1, 0);
} }
else if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING)) else if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
|| ((player->pflags & PF_JUMPED) && !(player->charflags & SF_NOJUMPDAMAGE && !(player->charability == CA_TWINSPIN && player->panim == PA_ABILITY))) || ((player->pflags & PF_JUMPED) && (player->pflags & PF_FORCEJUMPDAMAGE || !(player->charflags & SF_NOJUMPSPIN) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| (player->pflags & (PF_SPINNING|PF_GLIDING)) || (player->pflags & (PF_SPINNING|PF_GLIDING))
|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2) || (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
|| ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0)) || ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object? || player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
{ {
if (P_MobjFlip(toucher)*toucher->momz < 0) if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1))
toucher->momz = -toucher->momz; {
if (elementalpierce == 2)
P_DoBubbleBounce(player);
else
toucher->momz = -toucher->momz;
}
P_DamageMobj(special, toucher, toucher, 1, 0); P_DamageMobj(special, toucher, toucher, 1, 0);
} }
@ -445,7 +524,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{ {
INT32 pindex = special->info->mass - (INT32)pw_infinityring; INT32 pindex = special->info->mass - (INT32)pw_infinityring;
player->powers[special->info->mass] += (UINT16)special->info->reactiontime; player->powers[special->info->mass] += (UINT16)special->reactiontime;
player->ringweapons |= 1 << (pindex-1); player->ringweapons |= 1 << (pindex-1);
if (player->powers[special->info->mass] > rw_maximums[pindex]) if (player->powers[special->info->mass] > rw_maximums[pindex])
@ -532,7 +611,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return; return;
if (special->threshold) if (special->threshold)
{
player->powers[pw_emeralds] |= special->info->speed; player->powers[pw_emeralds] |= special->info->speed;
P_DoMatchSuper(player);
}
else else
emeralds |= special->info->speed; emeralds |= special->info->speed;
@ -553,6 +635,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return; return;
player->powers[pw_emeralds] |= special->threshold; player->powers[pw_emeralds] |= special->threshold;
P_DoMatchSuper(player);
break; break;
// Secret emblem thingy // Secret emblem thingy
@ -814,16 +897,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (G_IsSpecialStage(gamemap) && !player->exiting) if (G_IsSpecialStage(gamemap) && !player->exiting)
{ // In special stages, share rings. Everyone gives up theirs to the player who touched the capsule { // In special stages, share rings. Everyone gives up theirs to the player who touched the capsule
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && (&players[i] != player) && players[i].mo->health > 1) if (playeringame[i] && (&players[i] != player) && players[i].rings > 0)
{ {
toucher->health += players[i].mo->health-1; player->rings += players[i].rings;
player->health = toucher->health; players[i].rings = 0;
players[i].mo->health = 1;
players[i].health = players[i].mo->health;
} }
} }
if (!(player->health > 1) || player->exiting) if (player->rings <= 0 || player->exiting)
return; return;
// Mark the player as 'pull into the capsule' // Mark the player as 'pull into the capsule'
@ -1093,15 +1174,33 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
// Mario // // Mario //
// ***** // // ***** //
case MT_SHELL: case MT_SHELL:
if (special->state == &states[S_SHELL]) // Resting anim
{ {
// Kick that sucker around! boolean bounceon = ((P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0));
special->angle = toucher->angle; if (special->threshold == TICRATE) // it's moving
P_InstaThrust(special, special->angle, FixedMul(special->info->speed, special->scale)); {
S_StartSound(toucher, sfx_mario2); if (bounceon)
P_SetMobjState(special, S_SHELL1); {
P_SetTarget(&special->target, toucher); // Stop it!
special->threshold = (3*TICRATE)/2; special->momx = special->momy = 0;
S_StartSound(toucher, sfx_mario2);
P_SetTarget(&special->target, NULL);
special->threshold = TICRATE - 1;
toucher->momz = -toucher->momz;
}
else // can't handle in PIT_CheckThing because of landing-on causing it to stop
P_DamageMobj(toucher, special, special->target, 1, 0);
}
else if (special->threshold == 0)
{
// Kick that sucker around!
special->movedir = ((special->movedir == 1) ? -1 : 1);
P_InstaThrust(special, toucher->angle, (special->info->speed*special->scale));
S_StartSound(toucher, sfx_mario2);
P_SetTarget(&special->target, toucher);
special->threshold = (3*TICRATE)/2;
if (bounceon)
toucher->momz = -toucher->momz;
}
} }
return; return;
case MT_AXE: case MT_AXE:
@ -1134,9 +1233,17 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
case MT_FIREFLOWER: case MT_FIREFLOWER:
if (player->bot) if (player->bot)
return; return;
player->powers[pw_shield] |= SH_FIREFLOWER;
toucher->color = SKINCOLOR_WHITE; S_StartSound(toucher, sfx_mario3);
G_GhostAddColor(GHC_FIREFLOWER);
player->powers[pw_shield] = (player->powers[pw_shield] & SH_NOSTACK)|SH_FIREFLOWER;
if (!(player->powers[pw_super] || (mariomode && player->powers[pw_invulnerability])))
{
player->mo->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
}
break; break;
// *************** // // *************** //
@ -1305,7 +1412,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return; return;
} }
else if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING)) else if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
|| ((player->pflags & PF_JUMPED) && !(player->charflags & SF_NOJUMPDAMAGE)) || ((player->pflags & PF_JUMPED) && (player->pflags & PF_FORCEJUMPDAMAGE || !(player->charflags & SF_NOJUMPSPIN) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0)) || ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
|| (player->pflags & (PF_SPINNING|PF_GLIDING)) || (player->pflags & (PF_SPINNING|PF_GLIDING))
|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object? || player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
@ -1380,7 +1487,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
} }
if (player->powers[pw_invulnerability] || player->powers[pw_flashing] if (player->powers[pw_invulnerability] || player->powers[pw_flashing]
|| (player->powers[pw_super] && !(ALL7EMERALDS(player->powers[pw_emeralds])))) || player->powers[pw_super])
return; return;
if (player->powers[pw_shield] || player->bot) //If One-Hit Shield if (player->powers[pw_shield] || player->bot) //If One-Hit Shield
{ {
@ -1390,11 +1497,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
else else
{ {
P_PlayRinglossSound(toucher); P_PlayRinglossSound(toucher);
if (toucher->health > 10) if (player->rings >= 10)
toucher->health -= 10; player->rings -= 10;
else else
toucher->health = 1; player->rings = 0;
player->health = toucher->health;
} }
P_DoPlayerPain(player, special, NULL); P_DoPlayerPain(player, special, NULL);
@ -1418,7 +1524,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return; return;
case MT_EXTRALARGEBUBBLE: case MT_EXTRALARGEBUBBLE:
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) if (player->powers[pw_shield] & SH_PROTECTWATER)
return; return;
if (maptol & TOL_NIGHTS) if (maptol & TOL_NIGHTS)
return; return;
@ -1496,6 +1602,9 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
if (!player) if (!player)
return; // Impossible! return; // Impossible!
if (!player->mo)
return; // Also impossible!
if (player->spectator) if (player->spectator)
return; // No messages for dying (crushed) spectators. return; // No messages for dying (crushed) spectators.
@ -1503,11 +1612,11 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
return; // Presumably it's obvious what's happening in splitscreen. return; // Presumably it's obvious what's happening in splitscreen.
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUAh_HurtMsg(player, inflictor, source)) if (LUAh_HurtMsg(player, inflictor, source, damagetype))
return; return;
#endif #endif
deadtarget = (player->health <= 0); deadtarget = (player->mo->health <= 0);
// Target's name // Target's name
snprintf(targetname, sizeof(targetname), "%s%s%s", snprintf(targetname, sizeof(targetname), "%s%s%s",
@ -1541,8 +1650,10 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
else switch (inflictor->type) else switch (inflictor->type)
{ {
case MT_PLAYER: case MT_PLAYER:
if ((inflictor->player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB) if (damagetype == DMG_NUKE) // SH_ARMAGEDDON, armageddon shield
str = M_GetText("%s%s's armageddon blast %s %s.\n"); str = M_GetText("%s%s's armageddon blast %s %s.\n");
else if ((inflictor->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL && (inflictor->player->pflags & PF_SHIELDABILITY))
str = M_GetText("%s%s's flame stomp %s %s.\n");
else if (inflictor->player->powers[pw_invulnerability]) else if (inflictor->player->powers[pw_invulnerability])
str = M_GetText("%s%s's invincibility aura %s %s.\n"); str = M_GetText("%s%s's invincibility aura %s %s.\n");
else if (inflictor->player->powers[pw_super]) else if (inflictor->player->powers[pw_super])
@ -1699,7 +1810,7 @@ void P_CheckTimeLimit(void)
return; return;
//Tagmode round end but only on the tic before the //Tagmode round end but only on the tic before the
//XD_EXITLEVEL packet is recieved by all players. //XD_EXITLEVEL packet is received by all players.
if (G_TagGametype()) if (G_TagGametype())
{ {
if (leveltime == (timelimitintics + 1)) if (leveltime == (timelimitintics + 1))
@ -1710,7 +1821,7 @@ void P_CheckTimeLimit(void)
|| (players[i].pflags & PF_TAGGED) || (players[i].pflags & PF_TAGIT)) || (players[i].pflags & PF_TAGGED) || (players[i].pflags & PF_TAGIT))
continue; continue;
CONS_Printf(M_GetText("%s recieved double points for surviving the round.\n"), player_names[i]); CONS_Printf(M_GetText("%s received double points for surviving the round.\n"), player_names[i]);
P_AddPlayerScore(&players[i], players[i].score); P_AddPlayerScore(&players[i], players[i].score);
} }
} }
@ -1950,7 +2061,6 @@ boolean P_CheckRacers(void)
*/ */
void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{ {
mobjtype_t item;
mobj_t *mo; mobj_t *mo;
if (inflictor && (inflictor->type == MT_SHELL || inflictor->type == MT_FIREBALL)) if (inflictor && (inflictor->type == MT_SHELL || inflictor->type == MT_FIREBALL))
@ -1974,7 +2084,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
target->health = 0; // This makes it easy to check if something's dead elsewhere. target->health = 0; // This makes it easy to check if something's dead elsewhere.
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUAh_MobjDeath(target, inflictor, source) || P_MobjWasRemoved(target)) if (LUAh_MobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target))
return; return;
#endif #endif
@ -2166,81 +2276,80 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (source && target && target->player && source->player) if (source && target && target->player && source->player)
P_PlayVictorySound(source); // Killer laughs at you. LAUGHS! BWAHAHAHA! P_PlayVictorySound(source); // Killer laughs at you. LAUGHS! BWAHAHAHA!
#ifdef OLDANIMALSPAWNING
// Drop stuff. // Drop stuff.
// This determines the kind of object spawned // This determines the kind of object spawned
// during the death frame of a thing. // during the death frame of a thing.
if (!mariomode // Don't show birds, etc. in Mario Mode Tails 12-23-2001 if (!mariomode // Don't show birds, etc. in Mario Mode Tails 12-23-2001
&& target->flags & MF_ENEMY) && target->flags & MF_ENEMY)
{ {
if (cv_soniccd.value) mobjtype_t item;
item = MT_SEED; INT32 prandom;
else
switch (target->type)
{ {
INT32 prandom; case MT_REDCRAWLA:
case MT_GOLDBUZZ:
case MT_SKIM:
case MT_UNIDUS:
item = MT_FLICKY_02/*MT_BUNNY*/;
break;
switch (target->type) case MT_BLUECRAWLA:
{ case MT_JETTBOMBER:
case MT_REDCRAWLA: case MT_GFZFISH:
case MT_GOLDBUZZ: item = MT_FLICKY_01/*MT_BIRD*/;
case MT_SKIM: break;
case MT_UNIDUS:
item = MT_BUNNY;
break;
case MT_BLUECRAWLA: case MT_JETTGUNNER:
case MT_JETTBOMBER: case MT_CRAWLACOMMANDER:
case MT_GFZFISH: case MT_REDBUZZ:
item = MT_BIRD; case MT_DETON:
break; item = MT_FLICKY_12/*MT_MOUSE*/;
break;
case MT_JETTGUNNER: case MT_GSNAPPER:
case MT_CRAWLACOMMANDER: case MT_EGGGUARD:
case MT_REDBUZZ: case MT_SPRINGSHELL:
case MT_DETON: item = MT_FLICKY_11/*MT_COW*/;
item = MT_MOUSE; break;
break;
case MT_GSNAPPER: case MT_MINUS:
case MT_EGGGUARD: case MT_VULTURE:
case MT_SPRINGSHELL: case MT_POINTY:
item = MT_COW; case MT_YELLOWSHELL:
break; item = MT_FLICKY_03/*MT_CHICKEN*/;
break;
case MT_MINUS: case MT_AQUABUZZ:
case MT_VULTURE: item = MT_FLICKY_01/*MT_REDBIRD*/;
case MT_POINTY: break;
case MT_YELLOWSHELL:
item = MT_CHICKEN;
break;
case MT_AQUABUZZ: default:
item = MT_REDBIRD; if (target->info->doomednum)
break; prandom = target->info->doomednum%5; // "Random" animal for new enemies.
else
prandom = P_RandomKey(5); // No placable object, just use a random number.
default: switch(prandom)
if (target->info->doomednum) {
prandom = target->info->doomednum%5; // "Random" animal for new enemies. default: item = MT_FLICKY_02/*MT_BUNNY*/; break;
else case 1: item = MT_FLICKY_01/*MT_BIRD*/; break;
prandom = P_RandomKey(5); // No placable object, just use a random number. case 2: item = MT_FLICKY_12/*MT_MOUSE*/; break;
case 3: item = MT_FLICKY_11/*MT_COW*/; break;
switch(prandom) case 4: item = MT_FLICKY_03/*MT_CHICKEN*/; break;
{ }
default: item = MT_BUNNY; break; break;
case 1: item = MT_BIRD; break;
case 2: item = MT_MOUSE; break;
case 3: item = MT_COW; break;
case 4: item = MT_CHICKEN; break;
}
break;
}
} }
mo = P_SpawnMobj(target->x, target->y, target->z + (target->height / 2) - FixedMul(mobjinfo[item].height / 2, target->scale), item); mo = P_SpawnMobj(target->x, target->y, target->z + (target->height / 2) - FixedMul(mobjinfo[item].height / 2, target->scale), item);
mo->destscale = target->scale; mo->destscale = target->scale;
P_SetScale(mo, mo->destscale); P_SetScale(mo, mo->destscale);
} }
else
#endif
// Other death animation effects // Other death animation effects
else switch(target->type) switch(target->type)
{ {
case MT_BOUNCEPICKUP: case MT_BOUNCEPICKUP:
case MT_RAILPICKUP: case MT_RAILPICKUP:
@ -2258,24 +2367,27 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
break; break;
case MT_PLAYER: case MT_PLAYER:
target->fuse = TICRATE*3; // timer before mobj disappears from view (even if not an actual player)
target->momx = target->momy = target->momz = 0;
if (damagetype == DMG_DROWNED) // drowned
{ {
target->movedir = damagetype; // we're MOVING the Damage Into anotheR function... Okay, this is a bit of a hack. target->fuse = TICRATE*3; // timer before mobj disappears from view (even if not an actual player)
if (target->player->charflags & SF_MACHINE) target->momx = target->momy = target->momz = 0;
S_StartSound(target, sfx_fizzle);
if (damagetype == DMG_DROWNED) // drowned
{
target->movedir = damagetype; // we're MOVING the Damage Into anotheR function... Okay, this is a bit of a hack.
if (target->player->charflags & SF_MACHINE)
S_StartSound(target, sfx_fizzle);
else
S_StartSound(target, sfx_drown);
// Don't jump up when drowning
}
else else
S_StartSound(target, sfx_drown); {
// Don't jump up when drowning P_SetObjectMomZ(target, 14*FRACUNIT, false);
} if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // Spikes
else S_StartSound(target, sfx_spkdth);
{ else
P_SetObjectMomZ(target, 14*FRACUNIT, false); P_PlayDeathSound(target);
if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // Spikes }
S_StartSound(target, sfx_spkdth);
else
P_PlayDeathSound(target);
} }
break; break;
default: default:
@ -2442,8 +2554,7 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
player_t *player = target->player; player_t *player = target->player;
tic_t oldnightstime = player->nightstime; tic_t oldnightstime = player->nightstime;
if (!player->powers[pw_flashing] if (!player->powers[pw_flashing])
&& !(player->pflags & PF_GODMODE))
{ {
angle_t fa; angle_t fa;
@ -2558,27 +2669,18 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
return true; return true;
} }
if (target->health <= 1) // Death if (player->rings > 0) // Ring loss
{
P_PlayRinglossSound(target);
P_PlayerRingBurst(player, player->rings);
}
else // Death
{ {
P_PlayDeathSound(target); P_PlayDeathSound(target);
P_PlayVictorySound(source); // Killer laughs at you! LAUGHS! BWAHAHAHHAHAA!! P_PlayVictorySound(source); // Killer laughs at you! LAUGHS! BWAHAHAHHAHAA!!
} }
else if (target->health > 1) // Ring loss
{
P_PlayRinglossSound(target);
P_PlayerRingBurst(player, player->mo->health - 1);
}
if (inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
{
player->health -= 10;
if (player->health < 2)
player->health = 2;
target->health = player->health;
}
else
player->health = target->health = 1;
player->rings = 0;
return true; return true;
} }
@ -2629,7 +2731,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
// Burst weapons and emeralds in Match/CTF only // Burst weapons and emeralds in Match/CTF only
if (source && (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF)) if (source && (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF))
{ {
P_PlayerRingBurst(player, player->health - 1); P_PlayerRingBurst(player, player->rings);
P_PlayerEmeraldBurst(player, false); P_PlayerEmeraldBurst(player, false);
} }
@ -2733,22 +2835,21 @@ void P_RemoveShield(player_t *player)
{ {
if (player->powers[pw_shield] & SH_FORCE) if (player->powers[pw_shield] & SH_FORCE)
{ // Multi-hit { // Multi-hit
if ((player->powers[pw_shield] & 0xFF) == 0) if (player->powers[pw_shield] & SH_FORCEHP)
player->powers[pw_shield] &= ~SH_FORCE;
else
player->powers[pw_shield]--; player->powers[pw_shield]--;
else
player->powers[pw_shield] &= SH_STACK;
} }
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_NONE) else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_NONE)
{ // Second layer shields { // Second layer shields
player->powers[pw_shield] = SH_NONE; if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->powers[pw_super] || (mariomode && player->powers[pw_invulnerability])))
// Reset fireflower
if (!player->powers[pw_super])
{ {
player->mo->color = player->skincolor; player->mo->color = player->skincolor;
G_GhostAddColor(GHC_NORMAL); G_GhostAddColor(GHC_NORMAL);
} }
player->powers[pw_shield] = SH_NONE;
} }
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB) // Give them what's coming to them! else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON) // Give them what's coming to them!
{ {
P_BlackOw(player); // BAM! P_BlackOw(player); // BAM!
player->pflags |= PF_JUMPDOWN; player->pflags |= PF_JUMPDOWN;
@ -2757,7 +2858,7 @@ void P_RemoveShield(player_t *player)
player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK; player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK;
} }
static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage) static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{ {
// Must do pain first to set flashing -- P_RemoveShield can cause damage // Must do pain first to set flashing -- P_RemoveShield can cause damage
P_DoPlayerPain(player, source, inflictor); P_DoPlayerPain(player, source, inflictor);
@ -2766,7 +2867,7 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
if (source && (source->type == MT_SPIKE || (source->type == MT_NULL && source->threshold == 43))) // spikes if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes
S_StartSound(player->mo, sfx_spkdth); S_StartSound(player->mo, sfx_spkdth);
else else
S_StartSound (player->mo, sfx_shldls); // Ba-Dum! Shield loss. S_StartSound (player->mo, sfx_shldls); // Ba-Dum! Shield loss.
@ -2791,15 +2892,12 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{ {
if (!(inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))) P_DoPlayerPain(player, source, inflictor);
{
P_DoPlayerPain(player, source, inflictor);
P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes
S_StartSound(player->mo, sfx_spkdth); S_StartSound(player->mo, sfx_spkdth);
}
if (source && source->player && !player->powers[pw_super]) //don't score points against super players if (source && source->player && !player->powers[pw_super]) //don't score points against super players
{ {
@ -2821,6 +2919,10 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
// Ring loss sound plays despite hitting spikes // Ring loss sound plays despite hitting spikes
P_PlayRinglossSound(player->mo); // Ringledingle! P_PlayRinglossSound(player->mo); // Ringledingle!
P_PlayerRingBurst(player, damage);
player->rings -= damage;
if (player->rings < 0)
player->rings = 0;
} }
/** Damages an object, which may or may not be a player. /** Damages an object, which may or may not be a player.
@ -2869,7 +2971,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// Everything above here can't be forced. // Everything above here can't be forced.
if (!metalrecording) if (!metalrecording)
{ {
UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage); UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage, damagetype);
if (P_MobjWasRemoved(target)) if (P_MobjWasRemoved(target))
return (shouldForce == 1); // mobj was removed return (shouldForce == 1); // mobj was removed
if (shouldForce == 1) if (shouldForce == 1)
@ -2912,7 +3014,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false; return false;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target)) if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
return true; return true;
#endif #endif
@ -2940,7 +3042,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false; return false;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target)) if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
return true; return true;
#endif #endif
@ -2950,7 +3052,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
else if (target->flags & MF_ENEMY) else if (target->flags & MF_ENEMY)
{ {
if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target)) if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
return true; return true;
} }
#endif #endif
@ -2964,18 +3066,24 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (player->exiting) if (player->exiting)
return false; return false;
if (player->pflags & PF_GODMODE)
return false;
if (!(target->player->pflags & (PF_NIGHTSMODE|PF_NIGHTSFALL)) && (maptol & TOL_NIGHTS)) if (!(target->player->pflags & (PF_NIGHTSMODE|PF_NIGHTSFALL)) && (maptol & TOL_NIGHTS))
return false; return false;
switch (damagetype) switch (damagetype)
{ {
case DMG_WATER: case DMG_WATER:
if (player->powers[pw_shield] & SH_PROTECTWATER)
return false; // Invincible to water damage
break;
case DMG_FIRE: case DMG_FIRE:
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) if (player->powers[pw_shield] & SH_PROTECTFIRE)
return false; // Invincible to water/fire damage return false; // Invincible to fire damage
break; break;
case DMG_ELECTRIC: case DMG_ELECTRIC:
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) if (player->powers[pw_shield] & SH_PROTECTELECTRIC)
return false; // Invincible to electric damage return false; // Invincible to electric damage
break; break;
default: default:
@ -2991,11 +3099,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false; // Don't hit yourself with your own paraloop, baka return false; // Don't hit yourself with your own paraloop, baka
if (source && source->player && !cv_friendlyfire.value if (source && source->player && !cv_friendlyfire.value
&& (gametype == GT_COOP && (gametype == GT_COOP
|| (G_GametypeHasTeams() && target->player->ctfteam == source->player->ctfteam))) || (G_GametypeHasTeams() && player->ctfteam == source->player->ctfteam)))
return false; // Don't run eachother over in special stages and team games and such return false; // Don't run eachother over in special stages and team games and such
} }
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUAh_MobjDamage(target, inflictor, source, damage)) if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
return true; return true;
#endif #endif
P_NiGHTSDamage(target, source); // -5s :( P_NiGHTSDamage(target, source); // -5s :(
@ -3004,7 +3112,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (!force && inflictor && inflictor->flags & MF_FIRE) if (!force && inflictor && inflictor->flags & MF_FIRE)
{ {
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) if (player->powers[pw_shield] & SH_PROTECTFIRE)
return false; // Invincible to fire objects return false; // Invincible to fire objects
if (G_PlatformGametype() && inflictor && source && source->player) if (G_PlatformGametype() && inflictor && source && source->player)
@ -3026,12 +3134,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false; return false;
} }
if (!force && player->pflags & PF_GODMODE)
return false;
// Instant-Death // Instant-Death
if (damagetype & DMG_DEATHMASK) if (damagetype & DMG_DEATHMASK)
{
P_KillPlayer(player, source, damage); P_KillPlayer(player, source, damage);
player->rings = 0;
}
else if (metalrecording) else if (metalrecording)
{ {
if (!inflictor) if (!inflictor)
@ -3045,19 +3153,19 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false; // Metal Sonic walk through flame !! return false; // Metal Sonic walk through flame !!
else else
{ // Oh no! Metal Sonic is hit !! { // Oh no! Metal Sonic is hit !!
P_ShieldDamage(player, inflictor, source, damage); P_ShieldDamage(player, inflictor, source, damage, damagetype);
return true; return true;
} }
} }
else if (player->powers[pw_invulnerability] || player->powers[pw_flashing] // ignore bouncing & such in invulnerability else if (player->powers[pw_invulnerability] || player->powers[pw_flashing] // ignore bouncing & such in invulnerability
|| (player->powers[pw_super] && !(ALL7EMERALDS(player->powers[pw_emeralds]) && inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player)))) || player->powers[pw_super])
{ {
if (force || (inflictor && (inflictor->flags & MF_MISSILE) if (force || (inflictor && (inflictor->flags & MF_MISSILE)
&& (inflictor->flags2 & MF2_SUPERFIRE) && (inflictor->flags2 & MF2_SUPERFIRE)
&& player->powers[pw_super])) && player->powers[pw_super]))
{ {
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (!LUAh_MobjDamage(target, inflictor, source, damage)) if (!LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
#endif #endif
P_SuperDamage(player, inflictor, source, damage); P_SuperDamage(player, inflictor, source, damage);
return true; return true;
@ -3066,67 +3174,35 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false; return false;
} }
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
else if (LUAh_MobjDamage(target, inflictor, source, damage)) else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
return true; return true;
#endif #endif
else if (!player->powers[pw_super] && (player->powers[pw_shield] || player->bot)) //If One-Hit Shield else if (player->powers[pw_shield] || player->bot) //If One-Hit Shield
{ {
P_ShieldDamage(player, inflictor, source, damage); P_ShieldDamage(player, inflictor, source, damage, damagetype);
damage = 0; damage = 0;
} }
else if (player->mo->health > 1) // No shield but have rings. else if (player->rings > 0) // No shield but have rings.
{ {
damage = player->mo->health - 1; damage = player->rings;
P_RingDamage(player, inflictor, source, damage, damagetype); P_RingDamage(player, inflictor, source, damage, damagetype);
damage = 0;
}
// To reduce griefing potential, don't allow players to be killed
// by friendly fire. Spilling their rings and other items is enough.
else if (!force && G_GametypeHasTeams()
&& source && source->player && (source->player->ctfteam == player->ctfteam)
&& cv_friendlyfire.value)
{
damage = 0;
P_ShieldDamage(player, inflictor, source, damage, damagetype);
} }
else // No shield, no rings, no invincibility. else // No shield, no rings, no invincibility.
{ {
// To reduce griefing potential, don't allow players to be killed damage = 1;
// by friendly fire. Spilling their rings and other items is enough. P_KillPlayer(player, source, damage);
if (force || !(G_GametypeHasTeams()
&& source && source->player && (source->player->ctfteam == player->ctfteam)
&& cv_friendlyfire.value))
{
damage = 1;
P_KillPlayer(player, source, damage);
}
else
{
damage = 0;
P_ShieldDamage(player, inflictor, source, damage);
}
} }
if (inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
{
if (player->powers[pw_shield])
{
P_RemoveShield(player);
return true;
}
else
{
player->health -= (10 * (1 << (INT32)(player->powers[pw_super] / 10500)));
if (player->health < 2)
player->health = 2;
}
if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
P_PlayerFlagBurst(player, false);
}
else if (damagetype & DMG_DEATHMASK)
player->health = 0;
else
{
player->health -= damage; // mirror mobj health here
target->player->powers[pw_flashing] = flashingtics;
if (damage > 0) // don't spill emeralds/ammo/panels for shield damage
P_PlayerRingBurst(player, damage);
}
if (player->health < 0)
player->health = 0;
P_HitDeathMessages(player, inflictor, source, damagetype); P_HitDeathMessages(player, inflictor, source, damagetype);
P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
@ -3138,13 +3214,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
P_DamageMobj(source, target, target, 1, 0); P_DamageMobj(source, target, target, 1, 0);
// do the damage // do the damage
if (player && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player)) if (damagetype & DMG_DEATHMASK)
{
target->health -= (10 * (1 << (INT32)(player->powers[pw_super] / 10500)));
if (target->health < 2)
target->health = 2;
}
else if (damagetype & DMG_DEATHMASK)
target->health = 0; target->health = 0;
else else
target->health -= damage; target->health -= damage;
@ -3159,10 +3229,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
} }
if (player) if (player)
{ P_ResetPlayer(target->player);
if (!(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
P_ResetPlayer(target->player);
}
else else
switch (target->type) switch (target->type)
{ {
@ -3185,16 +3252,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// if not intent on another player, // if not intent on another player,
// chase after this one // chase after this one
P_SetTarget(&target->target, source); P_SetTarget(&target->target, source);
if (target->state == &states[target->info->spawnstate] && target->info->seestate != S_NULL)
{
if (player)
{
if (!(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
P_SetPlayerMobjState(target, target->info->seestate);
}
else
P_SetMobjState(target, target->info->seestate);
}
} }
return true; return true;
@ -3220,7 +3277,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
return; return;
// If no health, don't spawn ring! // If no health, don't spawn ring!
if (player->mo->health <= 1) if (player->rings <= 0)
num_rings = 0; num_rings = 0;
if (num_rings > 32 && !(player->pflags & PF_NIGHTSFALL)) if (num_rings > 32 && !(player->pflags & PF_NIGHTSFALL))
@ -3230,11 +3287,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
P_PlayerEmeraldBurst(player, false); P_PlayerEmeraldBurst(player, false);
// Spill weapons first // Spill weapons first
if (player->ringweapons) P_PlayerWeaponPanelOrAmmoBurst(player);
P_PlayerWeaponPanelBurst(player);
// Spill the ammo
P_PlayerWeaponAmmoBurst(player);
for (i = 0; i < num_rings; i++) for (i = 0; i < num_rings; i++)
{ {
@ -3491,6 +3544,75 @@ void P_PlayerWeaponAmmoBurst(player_t *player)
} }
} }
void P_PlayerWeaponPanelOrAmmoBurst(player_t *player)
{
mobj_t *mo;
angle_t fa;
fixed_t ns;
INT32 i = 0;
fixed_t z;
#define SETUP_DROP(thingtype) \
z = player->mo->z; \
if (player->mo->eflags & MFE_VERTICALFLIP) \
z += player->mo->height - mobjinfo[thingtype].height; \
fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT)) & FINEMASK; \
ns = FixedMul(3*FRACUNIT, player->mo->scale); \
#define DROP_WEAPON(rwflag, pickup, ammo, power) \
if (player->ringweapons & rwflag) \
{ \
player->ringweapons &= ~rwflag; \
SETUP_DROP(pickup) \
mo = P_SpawnMobj(player->mo->x, player->mo->y, z, pickup); \
mo->reactiontime = 0; \
mo->flags2 |= MF2_DONTRESPAWN; \
mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \
P_SetTarget(&mo->target, player->mo); \
mo->fuse = 12*TICRATE; \
mo->destscale = player->mo->scale; \
P_SetScale(mo, player->mo->scale); \
mo->momx = FixedMul(FINECOSINE(fa),ns); \
if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \
mo->momy = FixedMul(FINESINE(fa),ns); \
P_SetObjectMomZ(mo, 4*FRACUNIT, false); \
if (i & 1) \
P_SetObjectMomZ(mo, 4*FRACUNIT, true); \
++i; \
} \
else if (player->powers[power] > 0) \
{ \
SETUP_DROP(ammo) \
mo = P_SpawnMobj(player->mo->x, player->mo->y, z, ammo); \
mo->health = player->powers[power]; \
mo->flags2 |= MF2_DONTRESPAWN; \
mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \
P_SetTarget(&mo->target, player->mo); \
mo->fuse = 12*TICRATE; \
mo->destscale = player->mo->scale; \
P_SetScale(mo, player->mo->scale); \
mo->momx = FixedMul(FINECOSINE(fa),ns); \
if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \
mo->momy = FixedMul(FINESINE(fa),ns); \
P_SetObjectMomZ(mo, 3*FRACUNIT, false); \
if (i & 1) \
P_SetObjectMomZ(mo, 3*FRACUNIT, true); \
player->powers[power] = 0; \
++i; \
}
DROP_WEAPON(RW_BOUNCE, MT_BOUNCEPICKUP, MT_BOUNCERING, pw_bouncering);
DROP_WEAPON(RW_RAIL, MT_RAILPICKUP, MT_RAILRING, pw_railring);
DROP_WEAPON(RW_AUTO, MT_AUTOPICKUP, MT_AUTOMATICRING, pw_automaticring);
DROP_WEAPON(RW_EXPLODE, MT_EXPLODEPICKUP, MT_EXPLOSIONRING, pw_explosionring);
DROP_WEAPON(RW_SCATTER, MT_SCATTERPICKUP, MT_SCATTERRING, pw_scatterring);
DROP_WEAPON(RW_GRENADE, MT_GRENADEPICKUP, MT_GRENADERING, pw_grenadering);
DROP_WEAPON(0, 0, MT_INFINITYRING, pw_infinityring);
#undef DROP_WEAPON
#undef SETUP_DROP
}
// //
// P_PlayerEmeraldBurst // P_PlayerEmeraldBurst
// //

View file

@ -59,9 +59,10 @@
#define AIMINGTOSLOPE(aiming) FINESINE((aiming>>ANGLETOFINESHIFT) & FINEMASK) #define AIMINGTOSLOPE(aiming) FINESINE((aiming>>ANGLETOFINESHIFT) & FINEMASK)
#define mariomode (maptol & TOL_MARIO)
#define twodlevel (maptol & TOL_2D) #define twodlevel (maptol & TOL_2D)
#define mariomode (maptol & TOL_MARIO)
#define P_GetPlayerHeight(player) FixedMul(player->height, player->mo->scale) #define P_GetPlayerHeight(player) FixedMul(player->height, player->mo->scale)
#define P_GetPlayerSpinHeight(player) FixedMul(player->spinheight, player->mo->scale) #define P_GetPlayerSpinHeight(player) FixedMul(player->spinheight, player->mo->scale)
@ -124,6 +125,7 @@ extern fixed_t t_cam2_dist, t_cam2_height, t_cam2_rotate;
INT32 P_GetPlayerControlDirection(player_t *player); INT32 P_GetPlayerControlDirection(player_t *player);
void P_AddPlayerScore(player_t *player, UINT32 amount); void P_AddPlayerScore(player_t *player, UINT32 amount);
void P_StealPlayerScore(player_t *player, UINT32 amount);
void P_ResetCamera(player_t *player, camera_t *thiscam); void P_ResetCamera(player_t *player, camera_t *thiscam);
boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam); boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam);
void P_SlideCameraMove(camera_t *thiscam); void P_SlideCameraMove(camera_t *thiscam);
@ -142,6 +144,7 @@ boolean P_InQuicksand(mobj_t *mo);
void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative); void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative);
void P_RestoreMusic(player_t *player); void P_RestoreMusic(player_t *player);
void P_SpawnShieldOrb(player_t *player); void P_SpawnShieldOrb(player_t *player);
void P_SwitchShield(player_t *player, UINT16 shieldtype);
mobj_t *P_SpawnGhostMobj(mobj_t *mobj); mobj_t *P_SpawnGhostMobj(mobj_t *mobj);
void P_GivePlayerRings(player_t *player, INT32 num_rings); void P_GivePlayerRings(player_t *player, INT32 num_rings);
void P_GivePlayerLives(player_t *player, INT32 numlives); void P_GivePlayerLives(player_t *player, INT32 numlives);
@ -151,8 +154,9 @@ void P_ResetScore(player_t *player);
boolean P_AutoPause(void); boolean P_AutoPause(void);
void P_DoJumpShield(player_t *player); void P_DoJumpShield(player_t *player);
void P_DoBubbleBounce(player_t *player);
void P_BlackOw(player_t *player); void P_BlackOw(player_t *player);
void P_ElementalFireTrail(player_t *player); void P_ElementalFire(player_t *player, boolean cropcircle);
void P_DoPityCheck(player_t *player); void P_DoPityCheck(player_t *player);
void P_PlayerThink(player_t *player); void P_PlayerThink(player_t *player);
@ -165,7 +169,7 @@ fixed_t P_ReturnThrustX(mobj_t *mo, angle_t angle, fixed_t move);
fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move); fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move);
void P_InstaThrustEvenIn2D(mobj_t *mo, angle_t angle, fixed_t move); void P_InstaThrustEvenIn2D(mobj_t *mo, angle_t angle, fixed_t move);
boolean P_LookForEnemies(player_t *player); boolean P_LookForEnemies(player_t *player, boolean nonenemies);
void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius); void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius);
void P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user void P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user
boolean P_SuperReady(player_t *player); boolean P_SuperReady(player_t *player);
@ -211,6 +215,7 @@ void P_PrecipitationEffects(void);
void P_RemoveMobj(mobj_t *th); void P_RemoveMobj(mobj_t *th);
boolean P_MobjWasRemoved(mobj_t *th); boolean P_MobjWasRemoved(mobj_t *th);
void P_RemoveSavegameMobj(mobj_t *th); void P_RemoveSavegameMobj(mobj_t *th);
UINT8 P_GetMobjSprite2(mobj_t *mobj, UINT8 spr2);
boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state); boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state);
boolean P_SetMobjState(mobj_t *mobj, statenum_t state); boolean P_SetMobjState(mobj_t *mobj, statenum_t state);
void P_RunShields(void); void P_RunShields(void);
@ -290,6 +295,11 @@ boolean P_CheckMissileRange(mobj_t *actor);
void P_NewChaseDir(mobj_t *actor); void P_NewChaseDir(mobj_t *actor);
boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist); boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist);
mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers);
void P_InternalFlickyBubble(mobj_t *actor);
void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez);
void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle);
// //
// P_MAP // P_MAP
// //
@ -380,7 +390,8 @@ typedef struct BasicFF_s
#define DMG_FIRE 2 #define DMG_FIRE 2
#define DMG_ELECTRIC 3 #define DMG_ELECTRIC 3
#define DMG_SPIKE 4 #define DMG_SPIKE 4
//#define DMG_SPECIALSTAGE 5 #define DMG_NUKE 5 // bomb shield
//#define DMG_SPECIALSTAGE 6
//// Death types - cannot be combined with damage types //// Death types - cannot be combined with damage types
#define DMG_INSTAKILL 0x80 #define DMG_INSTAKILL 0x80
#define DMG_DROWNED 0x80+1 #define DMG_DROWNED 0x80+1
@ -399,6 +410,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
void P_PlayerRingBurst(player_t *player, INT32 num_rings); /// \todo better fit in p_user.c void P_PlayerRingBurst(player_t *player, INT32 num_rings); /// \todo better fit in p_user.c
void P_PlayerWeaponPanelBurst(player_t *player); void P_PlayerWeaponPanelBurst(player_t *player);
void P_PlayerWeaponAmmoBurst(player_t *player); void P_PlayerWeaponAmmoBurst(player_t *player);
void P_PlayerWeaponPanelOrAmmoBurst(player_t *player);
void P_PlayerEmeraldBurst(player_t *player, boolean toss); void P_PlayerEmeraldBurst(player_t *player, boolean toss);
void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck); void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck);
@ -413,6 +425,7 @@ void P_ResetStarposts(void);
boolean P_CanPickupItem(player_t *player, boolean weapon); boolean P_CanPickupItem(player_t *player, boolean weapon);
void P_DoNightsScore(player_t *player); void P_DoNightsScore(player_t *player);
void P_DoMatchSuper(player_t *player);
// //
// P_SPEC // P_SPEC

View file

@ -203,7 +203,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
} }
} }
pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED); // I still need these. pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED|PF_SHIELDABILITY); // I still need these.
jumping = object->player->jumping; jumping = object->player->jumping;
secondjump = object->player->secondjump; secondjump = object->player->secondjump;
P_ResetPlayer(object->player); P_ResetPlayer(object->player);
@ -768,8 +768,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
} }
} }
if (tmthing->type == MT_SHELL && tmthing->threshold > TICRATE)
return true;
// damage / explode // damage / explode
if (tmthing->flags & MF_ENEMY) // An actual ENEMY! (Like the deton, for example) if (tmthing->flags & MF_ENEMY) // An actual ENEMY! (Like the deton, for example)
P_DamageMobj(thing, tmthing, tmthing, 1, 0); P_DamageMobj(thing, tmthing, tmthing, 1, 0);
@ -810,7 +808,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmthing->y = thing->y; tmthing->y = thing->y;
P_SetThingPosition(tmthing); P_SetThingPosition(tmthing);
} }
else else if (!(tmthing->type == MT_SHELL && thing->player)) // player collision handled in touchspecial
P_DamageMobj(thing, tmthing, tmthing->target, 1, 0); P_DamageMobj(thing, tmthing, tmthing->target, 1, 0);
// don't traverse any more // don't traverse any more
@ -969,10 +967,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
{ {
if (G_RingSlingerGametype() && (!G_GametypeHasTeams() || tmthing->player->ctfteam != thing->player->ctfteam)) if (G_RingSlingerGametype() && (!G_GametypeHasTeams() || tmthing->player->ctfteam != thing->player->ctfteam))
{ {
if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super]) if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super] || (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) && (tmthing->player->pflags & PF_SHIELDABILITY)))
&& !thing->player->powers[pw_super]) && !thing->player->powers[pw_super])
P_DamageMobj(thing, tmthing, tmthing, 1, 0); P_DamageMobj(thing, tmthing, tmthing, 1, 0);
else if ((thing->player->powers[pw_invulnerability] || thing->player->powers[pw_super]) else if ((thing->player->powers[pw_invulnerability] || thing->player->powers[pw_super] || (((thing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) && (thing->player->pflags & PF_SHIELDABILITY)))
&& !tmthing->player->powers[pw_super]) && !tmthing->player->powers[pw_super])
P_DamageMobj(tmthing, thing, thing, 1, 0); P_DamageMobj(tmthing, thing, thing, 1, 0);
} }
@ -1052,24 +1050,41 @@ static boolean PIT_CheckThing(mobj_t *thing)
else if (thing->z - FixedMul(FRACUNIT, thing->scale) <= tmthing->z + tmthing->height else if (thing->z - FixedMul(FRACUNIT, thing->scale) <= tmthing->z + tmthing->height
&& thing->z + thing->height + FixedMul(FRACUNIT, thing->scale) >= tmthing->z) && thing->z + thing->height + FixedMul(FRACUNIT, thing->scale) >= tmthing->z)
{ {
// 0 = none, 1 = elemental pierce, 2 = bubble bounce
UINT8 elementalpierce = (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (tmthing->player->pflags & PF_SHIELDABILITY)
? (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2)
: 0);
if (thing->flags & MF_MONITOR if (thing->flags & MF_MONITOR
&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING) && (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
|| ((tmthing->player->pflags & PF_JUMPED) || ((tmthing->player->pflags & PF_JUMPED)
&& !(tmthing->player->charflags & SF_NOJUMPDAMAGE && (tmthing->player->pflags & PF_FORCEJUMPDAMAGE
&& !(tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY))) || !(tmthing->player->charflags & SF_NOJUMPSPIN)
|| (tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2) || (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
|| ((tmthing->player->charflags & SF_STOMPDAMAGE) || ((tmthing->player->charflags & SF_STOMPDAMAGE)
&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)))) && (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))
|| elementalpierce))
{ {
player_t *player = tmthing->player;
SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed. SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed.
fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;; fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;;
fixed_t *z = &tmthing->z; // aau.
P_DamageMobj(thing, tmthing, tmthing, 1, 0); // break the monitor P_DamageMobj(thing, tmthing, tmthing, 1, 0); // break the monitor
// Going down? Then bounce back up. // Going down? Then bounce back up.
if ((P_MobjWasRemoved(thing) // Monitor was removed if ((P_MobjWasRemoved(thing) // Monitor was removed
|| !thing->health) // or otherwise popped || !thing->health) // or otherwise popped
&& (flipval*(*momz) < 0)) // monitor is on the floor and you're going down, or on the ceiling and you're going up && (flipval*(*momz) < 0) // monitor is on the floor and you're going down, or on the ceiling and you're going up
*momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically. && (elementalpierce != 1)) // you're not piercing through the monitor...
return false; {
if (elementalpierce == 2)
P_DoBubbleBounce(player);
else
*momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically.
}
if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
return false;
else
*z -= *momz; // to ensure proper collision.
} }
} }
} }
@ -1084,8 +1099,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
else if (thing->flags & MF_MONITOR && tmthing->player else if (thing->flags & MF_MONITOR && tmthing->player
&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING) && (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
|| ((tmthing->player->pflags & PF_JUMPED) || ((tmthing->player->pflags & PF_JUMPED)
&& !(tmthing->player->charflags & SF_NOJUMPDAMAGE && (tmthing->player->pflags & PF_FORCEJUMPDAMAGE
&& !(tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY))) || !(tmthing->player->charflags & SF_NOJUMPSPIN)
|| (tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2) || (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
|| ((tmthing->player->charflags & SF_STOMPDAMAGE) || ((tmthing->player->charflags & SF_STOMPDAMAGE)
&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))) && (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)))
@ -1124,7 +1140,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->player && tmthing->z + tmthing->height > topz if (tmthing->player && tmthing->z + tmthing->height > topz
&& tmthing->z + tmthing->height < tmthing->ceilingz) && tmthing->z + tmthing->height < tmthing->ceilingz)
{ {
tmfloorz = tmceilingz = INT32_MIN; // block while in air if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack...
return false;
tmfloorz = tmceilingz = topz; // block while in air
#ifdef ESLOPE #ifdef ESLOPE
tmceilingslope = NULL; tmceilingslope = NULL;
#endif #endif
@ -1167,7 +1186,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->player && tmthing->z < topz if (tmthing->player && tmthing->z < topz
&& tmthing->z > tmthing->floorz) && tmthing->z > tmthing->floorz)
{ {
tmfloorz = tmceilingz = INT32_MAX; // block while in air if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack...
return false;
tmfloorz = tmceilingz = topz; // block while in air
#ifdef ESLOPE #ifdef ESLOPE
tmfloorslope = NULL; tmfloorslope = NULL;
#endif #endif

View file

@ -572,51 +572,54 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
side_t *side = &sides[linedef->sidenum[0]]; side_t *side = &sides[linedef->sidenum[0]];
fixed_t textop, texbottom, texheight; fixed_t textop, texbottom, texheight;
fixed_t texmid, delta1, delta2; fixed_t texmid, delta1, delta2;
INT32 texnum = R_GetTextureNum(side->midtexture); // make sure the texture is actually valid
// Get the midtexture's height if (texnum) {
texheight = textures[texturetranslation[side->midtexture]]->height << FRACBITS; // Get the midtexture's height
texheight = textures[texnum]->height << FRACBITS;
// Set texbottom and textop to the Z coordinates of the texture's boundaries // Set texbottom and textop to the Z coordinates of the texture's boundaries
#if 0 // #ifdef POLYOBJECTS #if 0 // #ifdef POLYOBJECTS
// don't remove this code unless solid midtextures // don't remove this code unless solid midtextures
// on non-solid polyobjects should NEVER happen in the future // on non-solid polyobjects should NEVER happen in the future
if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) { if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) {
if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
texbottom = back->floorheight + side->rowoffset; texbottom = back->floorheight + side->rowoffset;
textop = back->ceilingheight + side->rowoffset; textop = back->ceilingheight + side->rowoffset;
} else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) { } else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
texbottom = back->floorheight + side->rowoffset; texbottom = back->floorheight + side->rowoffset;
textop = texbottom + texheight*(side->repeatcnt+1); textop = texbottom + texheight*(side->repeatcnt+1);
} else { } else {
textop = back->ceilingheight + side->rowoffset; textop = back->ceilingheight + side->rowoffset;
texbottom = textop - texheight*(side->repeatcnt+1); texbottom = textop - texheight*(side->repeatcnt+1);
} }
} else } else
#endif #endif
{ {
if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
texbottom = openbottom + side->rowoffset; texbottom = openbottom + side->rowoffset;
textop = opentop + side->rowoffset; textop = opentop + side->rowoffset;
} else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) { } else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
texbottom = openbottom + side->rowoffset; texbottom = openbottom + side->rowoffset;
textop = texbottom + texheight*(side->repeatcnt+1); textop = texbottom + texheight*(side->repeatcnt+1);
} else { } else {
textop = opentop + side->rowoffset; textop = opentop + side->rowoffset;
texbottom = textop - texheight*(side->repeatcnt+1); texbottom = textop - texheight*(side->repeatcnt+1);
}
} }
}
texmid = texbottom+(textop-texbottom)/2; texmid = texbottom+(textop-texbottom)/2;
delta1 = abs(mobj->z - texmid); delta1 = abs(mobj->z - texmid);
delta2 = abs(thingtop - texmid); delta2 = abs(thingtop - texmid);
if (delta1 > delta2) { // Below if (delta1 > delta2) { // Below
if (opentop > texbottom) if (opentop > texbottom)
opentop = texbottom; opentop = texbottom;
} else { // Above } else { // Above
if (openbottom < textop) if (openbottom < textop)
openbottom = textop; openbottom = textop;
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -193,6 +193,7 @@ typedef enum
MF2_BOSSDEAD = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.) MF2_BOSSDEAD = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
MF2_AMBUSH = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH MF2_AMBUSH = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH
MF2_LINKDRAW = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position) MF2_LINKDRAW = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
MF2_SHIELD = 1<<29, // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
// free: to and including 1<<31 // free: to and including 1<<31
} mobjflag2_t; } mobjflag2_t;
@ -453,5 +454,6 @@ void P_EmeraldManager(void);
extern mapthing_t *huntemeralds[MAXHUNTEMERALDS]; extern mapthing_t *huntemeralds[MAXHUNTEMERALDS];
extern INT32 numhuntemeralds; extern INT32 numhuntemeralds;
extern boolean runemeraldmanager; extern boolean runemeraldmanager;
extern UINT16 emeraldspawndelay;
extern INT32 numstarposts; extern INT32 numstarposts;
#endif #endif

View file

@ -128,7 +128,7 @@ static void P_NetArchivePlayers(void)
WRITEANGLE(save_p, players[i].aiming); WRITEANGLE(save_p, players[i].aiming);
WRITEANGLE(save_p, players[i].awayviewaiming); WRITEANGLE(save_p, players[i].awayviewaiming);
WRITEINT32(save_p, players[i].awayviewtics); WRITEINT32(save_p, players[i].awayviewtics);
WRITEINT32(save_p, players[i].health); WRITEINT32(save_p, players[i].rings);
WRITESINT8(save_p, players[i].pity); WRITESINT8(save_p, players[i].pity);
WRITEINT32(save_p, players[i].currentweapon); WRITEINT32(save_p, players[i].currentweapon);
@ -308,7 +308,7 @@ static void P_NetUnArchivePlayers(void)
players[i].aiming = READANGLE(save_p); players[i].aiming = READANGLE(save_p);
players[i].awayviewaiming = READANGLE(save_p); players[i].awayviewaiming = READANGLE(save_p);
players[i].awayviewtics = READINT32(save_p); players[i].awayviewtics = READINT32(save_p);
players[i].health = READINT32(save_p); players[i].rings = READINT32(save_p);
players[i].pity = READSINT8(save_p); players[i].pity = READSINT8(save_p);
players[i].currentweapon = READINT32(save_p); players[i].currentweapon = READINT32(save_p);
@ -470,6 +470,7 @@ static void P_NetUnArchivePlayers(void)
#define SD_TAG 0x10 #define SD_TAG 0x10
#define SD_FLOORANG 0x20 #define SD_FLOORANG 0x20
#define SD_CEILANG 0x40 #define SD_CEILANG 0x40
#define SD_TAGLIST 0x80
#define LD_FLAG 0x01 #define LD_FLAG 0x01
#define LD_SPECIAL 0x02 #define LD_SPECIAL 0x02
@ -519,10 +520,9 @@ static void P_NetArchiveWorld(void)
// //
// flats // flats
// //
// P_AddLevelFlat should not add but just return the number if (ss->floorpic != P_CheckLevelFlat(ms->floorpic))
if (ss->floorpic != P_AddLevelFlat(ms->floorpic, levelflats))
diff |= SD_FLOORPIC; diff |= SD_FLOORPIC;
if (ss->ceilingpic != P_AddLevelFlat(ms->ceilingpic, levelflats)) if (ss->ceilingpic != P_CheckLevelFlat(ms->ceilingpic))
diff |= SD_CEILPIC; diff |= SD_CEILPIC;
if (ss->lightlevel != SHORT(ms->lightlevel)) if (ss->lightlevel != SHORT(ms->lightlevel))
@ -545,6 +545,8 @@ static void P_NetArchiveWorld(void)
if (ss->tag != SHORT(ms->tag)) if (ss->tag != SHORT(ms->tag))
diff2 |= SD_TAG; diff2 |= SD_TAG;
if (ss->nexttag != ss->spawn_nexttag || ss->firsttag != ss->spawn_firsttag)
diff2 |= SD_TAGLIST;
// Check if any of the sector's FOFs differ from how they spawned // Check if any of the sector's FOFs differ from how they spawned
if (ss->ffloors) if (ss->ffloors)
@ -592,16 +594,17 @@ static void P_NetArchiveWorld(void)
WRITEFIXED(put, ss->ceiling_xoffs); WRITEFIXED(put, ss->ceiling_xoffs);
if (diff2 & SD_CYOFFS) if (diff2 & SD_CYOFFS)
WRITEFIXED(put, ss->ceiling_yoffs); WRITEFIXED(put, ss->ceiling_yoffs);
if (diff2 & SD_TAG) if (diff2 & SD_TAG) // save only the tag
{
WRITEINT16(put, ss->tag); WRITEINT16(put, ss->tag);
WRITEINT32(put, ss->firsttag);
WRITEINT32(put, ss->nexttag);
}
if (diff2 & SD_FLOORANG) if (diff2 & SD_FLOORANG)
WRITEANGLE(put, ss->floorpic_angle); WRITEANGLE(put, ss->floorpic_angle);
if (diff2 & SD_CEILANG) if (diff2 & SD_CEILANG)
WRITEANGLE(put, ss->ceilingpic_angle); WRITEANGLE(put, ss->ceilingpic_angle);
if (diff2 & SD_TAGLIST) // save both firsttag and nexttag
{ // either of these could be changed even if tag isn't
WRITEINT32(put, ss->firsttag);
WRITEINT32(put, ss->nexttag);
}
// Special case: save the stats of all modified ffloors along with their ffloor "number"s // Special case: save the stats of all modified ffloors along with their ffloor "number"s
// we don't bother with ffloors that haven't changed, that would just add to savegame even more than is really needed // we don't bother with ffloors that haven't changed, that would just add to savegame even more than is really needed
@ -762,12 +765,12 @@ static void P_NetUnArchiveWorld(void)
sectors[i].ceilingheight = READFIXED(get); sectors[i].ceilingheight = READFIXED(get);
if (diff & SD_FLOORPIC) if (diff & SD_FLOORPIC)
{ {
sectors[i].floorpic = P_AddLevelFlat((char *)get, levelflats); sectors[i].floorpic = P_AddLevelFlatRuntime((char *)get);
get += 8; get += 8;
} }
if (diff & SD_CEILPIC) if (diff & SD_CEILPIC)
{ {
sectors[i].ceilingpic = P_AddLevelFlat((char *)get, levelflats); sectors[i].ceilingpic = P_AddLevelFlatRuntime((char *)get);
get += 8; get += 8;
} }
if (diff & SD_LIGHT) if (diff & SD_LIGHT)
@ -784,12 +787,11 @@ static void P_NetUnArchiveWorld(void)
if (diff2 & SD_CYOFFS) if (diff2 & SD_CYOFFS)
sectors[i].ceiling_yoffs = READFIXED(get); sectors[i].ceiling_yoffs = READFIXED(get);
if (diff2 & SD_TAG) if (diff2 & SD_TAG)
sectors[i].tag = READINT16(get); // DON'T use P_ChangeSectorTag
if (diff2 & SD_TAGLIST)
{ {
INT16 tag;
tag = READINT16(get);
sectors[i].firsttag = READINT32(get); sectors[i].firsttag = READINT32(get);
sectors[i].nexttag = READINT32(get); sectors[i].nexttag = READINT32(get);
P_ChangeSectorTag(i, tag);
} }
if (diff2 & SD_FLOORANG) if (diff2 & SD_FLOORANG)
sectors[i].floorpic_angle = READANGLE(get); sectors[i].floorpic_angle = READANGLE(get);
@ -972,6 +974,7 @@ typedef enum
tc_noenemies, tc_noenemies,
tc_eachtime, tc_eachtime,
tc_disappear, tc_disappear,
tc_planedisplace,
#ifdef POLYOBJECTS #ifdef POLYOBJECTS
tc_polyrotate, // haleyjd 03/26/06: polyobjects tc_polyrotate, // haleyjd 03/26/06: polyobjects
tc_polymove, tc_polymove,
@ -1095,7 +1098,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
diff |= MD_TRACER; diff |= MD_TRACER;
if (mobj->friction != ORIG_FRICTION) if (mobj->friction != ORIG_FRICTION)
diff |= MD_FRICTION; diff |= MD_FRICTION;
if (mobj->movefactor != ORIG_FRICTION_FACTOR) if (mobj->movefactor != FRACUNIT)
diff |= MD_MOVEFACTOR; diff |= MD_MOVEFACTOR;
if (mobj->fuse) if (mobj->fuse)
diff |= MD_FUSE; diff |= MD_FUSE;
@ -1535,6 +1538,21 @@ static void SaveDisappearThinker(const thinker_t *th, const UINT8 type)
WRITEINT32(save_p, ht->exists); WRITEINT32(save_p, ht->exists);
} }
//
// SavePlaneDisplaceThinker
//
// Saves a planedisplace_t thinker
//
static void SavePlaneDisplaceThinker(const thinker_t *th, const UINT8 type)
{
const planedisplace_t *ht = (const void *)th;
WRITEUINT8(save_p, type);
WRITEINT32(save_p, ht->affectee);
WRITEINT32(save_p, ht->control);
WRITEFIXED(save_p, ht->last_height);
WRITEFIXED(save_p, ht->speed);
WRITEUINT8(save_p, ht->type);
}
#ifdef POLYOBJECTS #ifdef POLYOBJECTS
// //
@ -1816,6 +1834,12 @@ static void P_NetArchiveThinkers(void)
SaveDisappearThinker(th, tc_disappear); SaveDisappearThinker(th, tc_disappear);
continue; continue;
} }
else if (th->function.acp1 == (actionf_p1)T_PlaneDisplace)
{
SavePlaneDisplaceThinker(th, tc_planedisplace);
continue;
}
#ifdef POLYOBJECTS #ifdef POLYOBJECTS
else if (th->function.acp1 == (actionf_p1)T_PolyObjRotate) else if (th->function.acp1 == (actionf_p1)T_PolyObjRotate)
{ {
@ -2081,7 +2105,7 @@ static void LoadMobjThinker(actionf_p1 thinker)
if (diff & MD_MOVEFACTOR) if (diff & MD_MOVEFACTOR)
mobj->movefactor = READFIXED(save_p); mobj->movefactor = READFIXED(save_p);
else else
mobj->movefactor = ORIG_FRICTION_FACTOR; mobj->movefactor = FRACUNIT;
if (diff & MD_FUSE) if (diff & MD_FUSE)
mobj->fuse = READINT32(save_p); mobj->fuse = READINT32(save_p);
if (diff & MD_WATERTOP) if (diff & MD_WATERTOP)
@ -2484,6 +2508,23 @@ static inline void LoadDisappearThinker(actionf_p1 thinker)
P_AddThinker(&ht->thinker); P_AddThinker(&ht->thinker);
} }
//
// LoadPlaneDisplaceThinker
//
// Loads a planedisplace_t thinker
//
static inline void LoadPlaneDisplaceThinker(actionf_p1 thinker)
{
planedisplace_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
ht->thinker.function.acp1 = thinker;
ht->affectee = READINT32(save_p);
ht->control = READINT32(save_p);
ht->last_height = READFIXED(save_p);
ht->speed = READFIXED(save_p);
ht->type = READUINT8(save_p);
P_AddThinker(&ht->thinker);
}
#ifdef POLYOBJECTS #ifdef POLYOBJECTS
// //
@ -2628,6 +2669,7 @@ static void P_NetUnArchiveThinkers(void)
thinker_t *next; thinker_t *next;
UINT8 tclass; UINT8 tclass;
UINT8 restoreNum = false; UINT8 restoreNum = false;
UINT32 i;
if (READUINT32(save_p) != ARCHIVEBLOCK_THINKERS) if (READUINT32(save_p) != ARCHIVEBLOCK_THINKERS)
I_Error("Bad $$$.sav at archive block Thinkers"); I_Error("Bad $$$.sav at archive block Thinkers");
@ -2648,6 +2690,12 @@ static void P_NetUnArchiveThinkers(void)
iquetail = iquehead = 0; iquetail = iquehead = 0;
P_InitThinkers(); P_InitThinkers();
// clear sector thinker pointers so they don't point to non-existant thinkers for all of eternity
for (i = 0; i < numsectors; i++)
{
sectors[i].floordata = sectors[i].ceilingdata = sectors[i].lightingdata = NULL;
}
// read in saved thinkers // read in saved thinkers
for (;;) for (;;)
{ {
@ -2760,6 +2808,10 @@ static void P_NetUnArchiveThinkers(void)
case tc_disappear: case tc_disappear:
LoadDisappearThinker((actionf_p1)T_Disappear); LoadDisappearThinker((actionf_p1)T_Disappear);
break; break;
case tc_planedisplace:
LoadPlaneDisplaceThinker((actionf_p1)T_PlaneDisplace);
break;
#ifdef POLYOBJECTS #ifdef POLYOBJECTS
case tc_polyrotate: case tc_polyrotate:
LoadPolyrotatetThinker((actionf_p1)T_PolyObjRotate); LoadPolyrotatetThinker((actionf_p1)T_PolyObjRotate);
@ -3306,7 +3358,7 @@ void P_SaveNetGame(void)
{ {
thinker_t *th; thinker_t *th;
mobj_t *mobj; mobj_t *mobj;
INT32 i = 0; INT32 i = 1; // don't start from 0, it'd be confused with a blank pointer otherwise
CV_SaveNetVars(&save_p); CV_SaveNetVars(&save_p);
P_NetArchiveMisc(); P_NetArchiveMisc();

View file

@ -160,6 +160,33 @@ FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg)
I_Error("Invalid or corrupt map.\nLook in log file or text console for technical details."); I_Error("Invalid or corrupt map.\nLook in log file or text console for technical details.");
} }
/** Sets a header's flickies to be equivalent to the original Freed Animals
*
* \param i The header to set flickies for
*/
void P_SetDemoFlickies(INT16 i)
{
mapheaderinfo[i]->numFlickies = 5;
mapheaderinfo[i]->flickies = Z_Realloc(mapheaderinfo[i]->flickies, 5*sizeof(mobjtype_t), PU_STATIC, NULL);
mapheaderinfo[i]->flickies[0] = MT_FLICKY_02/*MT_BUNNY*/;
mapheaderinfo[i]->flickies[1] = MT_FLICKY_01/*MT_BIRD*/;
mapheaderinfo[i]->flickies[2] = MT_FLICKY_12/*MT_MOUSE*/;
mapheaderinfo[i]->flickies[3] = MT_FLICKY_11/*MT_COW*/;
mapheaderinfo[i]->flickies[4] = MT_FLICKY_03/*MT_CHICKEN*/;
}
/** Clears a header's flickies
*
* \param i The header to clear flickies for
*/
void P_DeleteFlickies(INT16 i)
{
if (mapheaderinfo[i]->flickies)
Z_Free(mapheaderinfo[i]->flickies);
mapheaderinfo[i]->flickies = NULL;
mapheaderinfo[i]->numFlickies = 0;
}
#define NUMLAPS_DEFAULT 4 #define NUMLAPS_DEFAULT 4
/** Clears the data from a single map header. /** Clears the data from a single map header.
@ -223,6 +250,12 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
mapheaderinfo[num]->levelflags = 0; mapheaderinfo[num]->levelflags = 0;
DEH_WriteUndoline("MENUFLAGS", va("%d", mapheaderinfo[num]->menuflags), UNDO_NONE); DEH_WriteUndoline("MENUFLAGS", va("%d", mapheaderinfo[num]->menuflags), UNDO_NONE);
mapheaderinfo[num]->menuflags = 0; mapheaderinfo[num]->menuflags = 0;
// Flickies. Nope, no delfile support here either
#if 1 // equivalent to "FlickyList = DEMO"
P_SetDemoFlickies(num);
#else // equivalent to "FlickyList = NONE"
P_DeleteFlickies(num);
#endif
// TODO grades support for delfile (pfft yeah right) // TODO grades support for delfile (pfft yeah right)
P_DeleteGrades(num); P_DeleteGrades(num);
// an even further impossibility, delfile custom opts support // an even further impossibility, delfile custom opts support
@ -241,6 +274,7 @@ void P_AllocMapHeader(INT16 i)
if (!mapheaderinfo[i]) if (!mapheaderinfo[i])
{ {
mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL); mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL);
mapheaderinfo[i]->flickies = NULL;
mapheaderinfo[i]->grades = NULL; mapheaderinfo[i]->grades = NULL;
} }
P_ClearSingleMapHeaderInfo(i + 1); P_ClearSingleMapHeaderInfo(i + 1);
@ -574,6 +608,69 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat)
return (INT32)i; return (INT32)i;
} }
// help function for Lua and $$$.sav reading
// same as P_AddLevelFlat, except this is not setup so we must realloc levelflats to fit in the new flat
// no longer a static func in lua_maplib.c because p_saveg.c also needs it
//
INT32 P_AddLevelFlatRuntime(const char *flatname)
{
size_t i;
levelflat_t *levelflat = levelflats;
//
// first scan through the already found flats
//
for (i = 0; i < numlevelflats; i++, levelflat++)
if (strnicmp(levelflat->name,flatname,8)==0)
break;
// that flat was already found in the level, return the id
if (i == numlevelflats)
{
// allocate new flat memory
levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL);
levelflat = levelflats+i;
// store the name
strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
strupr(levelflat->name);
// store the flat lump number
levelflat->lumpnum = R_GetFlatNumForName(flatname);
#ifndef ZDEBUG
CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
#endif
numlevelflats++;
}
// level flat id
return (INT32)i;
}
// help function for $$$.sav checking
// this simply returns the flat # for the name given
//
INT32 P_CheckLevelFlat(const char *flatname)
{
size_t i;
levelflat_t *levelflat = levelflats;
//
// scan through the already found flats
//
for (i = 0; i < numlevelflats; i++, levelflat++)
if (strnicmp(levelflat->name,flatname,8)==0)
break;
if (i == numlevelflats)
return 0; // ??? flat was not found, this should not happen!
// level flat id
return (INT32)i;
}
static void P_LoadSectors(lumpnum_t lumpnum) static void P_LoadSectors(lumpnum_t lumpnum)
{ {
UINT8 *data; UINT8 *data;
@ -614,6 +711,7 @@ static void P_LoadSectors(lumpnum_t lumpnum)
ss->special = SHORT(ms->special); ss->special = SHORT(ms->special);
ss->tag = SHORT(ms->tag); ss->tag = SHORT(ms->tag);
ss->nexttag = ss->firsttag = -1; ss->nexttag = ss->firsttag = -1;
ss->spawn_nexttag = ss->spawn_firsttag = -1;
memset(&ss->soundorg, 0, sizeof(ss->soundorg)); memset(&ss->soundorg, 0, sizeof(ss->soundorg));
ss->validcount = 0; ss->validcount = 0;
@ -1463,6 +1561,8 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
sd->text[6] = 0; sd->text[6] = 0;
break; break;
} }
case 4: // Speed pad parameters
case 414: // Play SFX case 414: // Play SFX
{ {
sd->toptexture = sd->midtexture = sd->bottomtexture = 0; sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
@ -1476,6 +1576,8 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
break; break;
} }
case 14: // Bustable block parameters
case 15: // Fan particle spawner parameters
case 425: // Calls P_SetMobjState on calling mobj case 425: // Calls P_SetMobjState on calling mobj
case 434: // Custom Power case 434: // Custom Power
case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors
@ -2086,6 +2188,7 @@ static void P_LevelInitStuff(void)
// special stage tokens, emeralds, and ring total // special stage tokens, emeralds, and ring total
tokenbits = 0; tokenbits = 0;
runemeraldmanager = false; runemeraldmanager = false;
emeraldspawndelay = 60*TICRATE;
nummaprings = 0; nummaprings = 0;
// emerald hunt // emerald hunt
@ -2128,7 +2231,7 @@ static void P_LevelInitStuff(void)
players[i].gotcontinue = false; players[i].gotcontinue = false;
players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0; players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0;
players[i].health = 1; players[i].rings = 0;
players[i].aiming = 0; players[i].aiming = 0;
players[i].pflags &= ~PF_TIMEOVER; players[i].pflags &= ~PF_TIMEOVER;
@ -2585,7 +2688,7 @@ boolean P_SetupLevel(boolean skipprecip)
lastloadedmaplumpnum = W_GetNumForName(maplumpname = G_BuildMapName(gamemap)); lastloadedmaplumpnum = W_GetNumForName(maplumpname = G_BuildMapName(gamemap));
R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette); R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
CON_ReSetupBackColormap(mapheaderinfo[gamemap-1]->palette); CON_SetupBackColormap();
// SRB2 determines the sky texture to be used depending on the map header. // SRB2 determines the sky texture to be used depending on the map header.
P_SetupLevelSky(mapheaderinfo[gamemap-1]->skynum, true); P_SetupLevelSky(mapheaderinfo[gamemap-1]->skynum, true);

View file

@ -47,6 +47,8 @@ typedef struct
extern size_t numlevelflats; extern size_t numlevelflats;
extern levelflat_t *levelflats; extern levelflat_t *levelflats;
INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat); INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat);
INT32 P_AddLevelFlatRuntime(const char *flatname);
INT32 P_CheckLevelFlat(const char *flatname);
extern size_t nummapthings; extern size_t nummapthings;
extern mapthing_t *mapthings; extern mapthing_t *mapthings;
@ -66,6 +68,9 @@ void P_WriteThings(lumpnum_t lump);
size_t P_PrecacheLevelFlats(void); size_t P_PrecacheLevelFlats(void);
void P_AllocMapHeader(INT16 i); void P_AllocMapHeader(INT16 i);
void P_SetDemoFlickies(INT16 i);
void P_DeleteFlickies(INT16 i);
// Needed for NiGHTS // Needed for NiGHTS
void P_ReloadRings(void); void P_ReloadRings(void);
void P_DeleteGrades(INT16 i); void P_DeleteGrades(INT16 i);

View file

@ -804,6 +804,39 @@ void P_SlopeLaunch(mobj_t *mo)
mo->standingslope = NULL; mo->standingslope = NULL;
} }
//
// P_GetWallTransferMomZ
//
// It would be nice to have a single function that does everything necessary for slope-to-wall transfer.
// However, it needs to be seperated out in P_XYMovement to take into account momentum before and after hitting the wall.
// This just performs the necessary calculations for getting the base vertical momentum; the horizontal is already reasonably calculated by P_SlideMove.
fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope)
{
vector3_t slopemom, axis;
angle_t ang;
if (mo->standingslope->flags & SL_NOPHYSICS)
return 0;
// If there's physics, time for launching.
// Doesn't kill the vertical momentum as much as P_SlopeLaunch does.
ang = slope->zangle + ANG15*((slope->zangle > 0) ? 1 : -1);
if (ang > ANGLE_90 && ang < ANGLE_180)
ang = ((slope->zangle > 0) ? ANGLE_90 : InvAngle(ANGLE_90)); // hard cap of directly upwards
slopemom.x = mo->momx;
slopemom.y = mo->momy;
slopemom.z = 3*(mo->momz/2);
axis.x = -slope->d.y;
axis.y = slope->d.x;
axis.z = 0;
FV3_Rotate(&slopemom, &axis, ang >> ANGLETOFINESHIFT);
return 2*(slopemom.z/3);
}
// Function to help handle landing on slopes // Function to help handle landing on slopes
void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
{ {

View file

@ -37,6 +37,7 @@ fixed_t P_GetZAt(pslope_t *slope, fixed_t x, fixed_t y);
void P_QuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope); void P_QuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope);
void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope); void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope);
void P_SlopeLaunch(mobj_t *mo); void P_SlopeLaunch(mobj_t *mo);
fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope);
void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope); void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope);
void P_ButteredSlope(mobj_t *mo); void P_ButteredSlope(mobj_t *mo);

View file

@ -51,6 +51,9 @@ mobj_t *skyboxmo[2];
// Amount (dx, dy) vector linedef is shifted right to get scroll amount // Amount (dx, dy) vector linedef is shifted right to get scroll amount
#define SCROLL_SHIFT 5 #define SCROLL_SHIFT 5
// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
#define MAXFLATSIZE (2048<<FRACBITS)
/** Animated texture descriptor /** Animated texture descriptor
* This keeps track of an animated texture or an animated flat. * This keeps track of an animated texture or an animated flat.
* \sa P_UpdateSpecials, P_InitPicAnims, animdef_t * \sa P_UpdateSpecials, P_InitPicAnims, animdef_t
@ -108,6 +111,7 @@ static void P_AddFakeFloorsByLine(size_t line, ffloortype_e ffloorflags, thinker
static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec); static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec);
static void Add_Friction(INT32 friction, INT32 movefactor, INT32 affectee, INT32 referrer); static void Add_Friction(INT32 friction, INT32 movefactor, INT32 affectee, INT32 referrer);
static void P_AddSpikeThinker(sector_t *sec, INT32 referrer); static void P_AddSpikeThinker(sector_t *sec, INT32 referrer);
static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control, INT32 affectee);
//SoM: 3/7/2000: New sturcture without limits. //SoM: 3/7/2000: New sturcture without limits.
@ -1519,6 +1523,8 @@ static inline void P_InitTagLists(void)
size_t j = (unsigned)sectors[i].tag % numsectors; size_t j = (unsigned)sectors[i].tag % numsectors;
sectors[i].nexttag = sectors[j].firsttag; sectors[i].nexttag = sectors[j].firsttag;
sectors[j].firsttag = (INT32)i; sectors[j].firsttag = (INT32)i;
sectors[i].spawn_nexttag = sectors[i].nexttag;
sectors[j].spawn_firsttag = sectors[j].firsttag;
} }
for (i = numlines - 1; i != (size_t)-1; i--) for (i = numlines - 1; i != (size_t)-1; i--)
@ -1621,10 +1627,10 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
if (!playeringame[i] || players[i].spectator) if (!playeringame[i] || players[i].spectator)
continue; continue;
if (!players[i].mo || players[i].mo->health < 1) if (!players[i].mo || players[i].rings <= 0)
continue; continue;
rings += players[i].mo->health-1; rings += players[i].rings;
} }
} }
else else
@ -1632,7 +1638,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
if (!(actor && actor->player)) if (!(actor && actor->player))
return false; // no player to count rings from here, sorry return false; // no player to count rings from here, sorry
rings = actor->health-1; rings = actor->player->rings;
} }
if (triggerline->flags & ML_NOCLIMB) if (triggerline->flags & ML_NOCLIMB)
@ -2438,7 +2444,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
{ {
fixed_t sfxnum; fixed_t sfxnum;
sfxnum = sides[line->sidenum[0]].toptexture; //P_AproxDistance(line->dx, line->dy)>>FRACBITS; sfxnum = sides[line->sidenum[0]].toptexture;
if (line->tag != 0 && line->flags & ML_EFFECT5) if (line->tag != 0 && line->flags & ML_EFFECT5)
{ {
@ -3593,10 +3599,9 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
break; break;
case 9: // Ring Drainer (Floor Touch) case 9: // Ring Drainer (Floor Touch)
case 10: // Ring Drainer (No Floor Touch) case 10: // Ring Drainer (No Floor Touch)
if (leveltime % (TICRATE/2) == 0 && player->mo->health > 1) if (leveltime % (TICRATE/2) == 0 && player->rings > 0)
{ {
player->mo->health--; player->rings--;
player->health--;
S_StartSound(player->mo, sfx_itemup); S_StartSound(player->mo, sfx_itemup);
} }
break; break;
@ -3604,7 +3609,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || player->exiting || player->bot) if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || player->exiting || player->bot)
break; break;
if (!(player->powers[pw_shield] || player->mo->health > 1)) // Don't do anything if no shield or rings anyway if (!(player->powers[pw_shield] || player->rings > 0)) // Don't do anything if no shield or rings anyway
break; break;
if (player->powers[pw_shield]) if (player->powers[pw_shield])
@ -3612,14 +3617,13 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
P_RemoveShield(player); P_RemoveShield(player);
S_StartSound(player->mo, sfx_shldls); // Ba-Dum! Shield loss. S_StartSound(player->mo, sfx_shldls); // Ba-Dum! Shield loss.
} }
else if (player->mo->health > 1) else if (player->rings > 0)
{ {
P_PlayRinglossSound(player->mo); P_PlayRinglossSound(player->mo);
if (player->mo->health > 10) if (player->rings >= 10)
player->mo->health -= 10; player->rings -= 10;
else else
player->mo->health = 1; player->rings = 0;
player->health = player->mo->health;
} }
P_DoPlayerPain(player, NULL, NULL); // this does basically everything that was here before P_DoPlayerPain(player, NULL, NULL); // this does basically everything that was here before
@ -3628,7 +3632,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
P_PlayerFlagBurst(player, false); P_PlayerFlagBurst(player, false);
break; break;
case 12: // Space Countdown case 12: // Space Countdown
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_ELEMENTAL && !player->powers[pw_spacetime]) if (!(player->powers[pw_shield] & SH_PROTECTWATER) && !player->powers[pw_spacetime])
player->powers[pw_spacetime] = spacetimetics + 1; player->powers[pw_spacetime] = spacetimetics + 1;
break; break;
case 13: // Ramp Sector (Increase step-up/down) case 13: // Ramp Sector (Increase step-up/down)
@ -3728,14 +3732,13 @@ DoneSection2:
// Process Section 3 // Process Section 3
switch (special) switch (special)
{ {
case 1: // Ice/Sludge case 1: // Unused
case 2: // Wind/Current case 2: // Wind/Current
case 3: // Ice/Sludge and Wind/Current case 3: // Unused
case 4: // Conveyor Belt case 4: // Conveyor Belt
break; break;
case 5: // Speed pad w/o spin case 5: // Speed pad
case 6: // Speed pad w/ spin
if (player->powers[pw_flashing] != 0 && player->powers[pw_flashing] < TICRATE/2) if (player->powers[pw_flashing] != 0 && player->powers[pw_flashing] < TICRATE/2)
break; break;
@ -3745,9 +3748,16 @@ DoneSection2:
{ {
angle_t lineangle; angle_t lineangle;
fixed_t linespeed; fixed_t linespeed;
fixed_t sfxnum;
lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y); lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y);
linespeed = P_AproxDistance(lines[i].v2->x-lines[i].v1->x, lines[i].v2->y-lines[i].v1->y); linespeed = sides[lines[i].sidenum[0]].textureoffset;
if (linespeed == 0)
{
CONS_Debug(DBG_GAMELOGIC, "ERROR: Speed pad (tag %d) at zero speed.\n", sector->tag);
break;
}
player->mo->angle = lineangle; player->mo->angle = lineangle;
@ -3777,7 +3787,7 @@ DoneSection2:
P_InstaThrust(player->mo, player->mo->angle, linespeed); P_InstaThrust(player->mo, player->mo->angle, linespeed);
if (GETSECSPECIAL(sector->special, 3) == 6 && (player->charability2 == CA2_SPINDASH)) if ((lines[i].flags & ML_EFFECT5) && (player->charability2 == CA2_SPINDASH)) // Roll!
{ {
if (!(player->pflags & PF_SPINNING)) if (!(player->pflags & PF_SPINNING))
player->pflags |= PF_SPINNING; player->pflags |= PF_SPINNING;
@ -3786,19 +3796,26 @@ DoneSection2:
} }
player->powers[pw_flashing] = TICRATE/3; player->powers[pw_flashing] = TICRATE/3;
S_StartSound(player->mo, sfx_spdpad);
sfxnum = sides[lines[i].sidenum[0]].toptexture;
if (!sfxnum)
sfxnum = sfx_spdpad;
S_StartSound(player->mo, sfxnum);
} }
break; break;
case 7: // Bustable block sprite parameter case 6: // Unused
case 8: case 7: // Unused
case 9: case 8: // Unused
case 10: case 9: // Unused
case 11: case 10: // Unused
case 12: case 11: // Unused
case 13: case 12: // Unused
case 14: case 13: // Unused
case 15: case 14: // Unused
case 15: // Unused
break; break;
} }
@ -3968,8 +3985,14 @@ DoneSection2:
} }
// Grab speed and sequence values // Grab speed and sequence values
speed = abs(lines[lineindex].dx)/8; speed = abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8;
sequence = abs(lines[lineindex].dy)>>FRACBITS; sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS;
if (speed == 0)
{
CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence);
break;
}
// scan the thinkers // scan the thinkers
// to find the first waypoint // to find the first waypoint
@ -4041,8 +4064,14 @@ DoneSection2:
} }
// Grab speed and sequence values // Grab speed and sequence values
speed = -(abs(lines[lineindex].dx)/8); // Negative means reverse speed = -abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8; // Negative means reverse
sequence = abs(lines[lineindex].dy)>>FRACBITS; sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS;
if (speed == 0)
{
CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence);
break;
}
// scan the thinkers // scan the thinkers
// to find the last waypoint // to find the last waypoint
@ -4082,6 +4111,7 @@ DoneSection2:
player->speed = speed; player->speed = speed;
player->pflags |= PF_SPINNING; player->pflags |= PF_SPINNING;
player->pflags &= ~(PF_JUMPED|PF_GLIDING|PF_SLIDING|PF_CANCARRY); player->pflags &= ~(PF_JUMPED|PF_GLIDING|PF_SLIDING|PF_CANCARRY);
player->climbing = 0;
if (player->mo->state-states != S_PLAY_SPIN) if (player->mo->state-states != S_PLAY_SPIN)
{ {
@ -4180,8 +4210,14 @@ DoneSection2:
} }
// Grab speed and sequence values // Grab speed and sequence values
speed = abs(lines[lineindex].dx)/8; speed = abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8;
sequence = abs(lines[lineindex].dy)>>FRACBITS; sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS;
if (speed == 0)
{
CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence);
break;
}
// Find the closest waypoint // Find the closest waypoint
// Find the preceding waypoint // Find the preceding waypoint
@ -4797,6 +4833,13 @@ void P_UpdateSpecials(void)
} }
} }
/** Gets a 3Dfloor by control sector.
*
* \param sec Target sector.
* \param sec2 Control sector.
* \return Pointer to found 3Dfloor, or NULL.
* \sa P_GetFFloorByID
*/
static inline ffloor_t *P_GetFFloorBySec(sector_t *sec, sector_t *sec2) static inline ffloor_t *P_GetFFloorBySec(sector_t *sec, sector_t *sec2)
{ {
ffloor_t *rover; ffloor_t *rover;
@ -4809,6 +4852,26 @@ static inline ffloor_t *P_GetFFloorBySec(sector_t *sec, sector_t *sec2)
return NULL; return NULL;
} }
/** Gets a 3Dfloor by ID number.
*
* \param sec Target sector.
* \param id ID of 3Dfloor in target sector. Note that the first FOF's ID is 0.
* \return Pointer to found 3Dfloor, or NULL.
* \sa P_GetFFloorBySec
*/
ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id)
{
ffloor_t *rover;
UINT16 i = 0;
if (!sec->ffloors)
return NULL;
for (rover = sec->ffloors; rover; rover = rover->next)
if (i++ == id)
return rover;
return NULL;
}
/** Adds a newly formed 3Dfloor structure to a sector's ffloors list. /** Adds a newly formed 3Dfloor structure to a sector's ffloors list.
* *
* \param sec Target sector. * \param sec Target sector.
@ -5001,7 +5064,8 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
if ((flags & FF_MARIO)) if ((flags & FF_MARIO))
{ {
P_AddBlockThinker(sec2, master); if (!(flags & FF_SHATTERBOTTOM)) // Don't change the textures of a brick block, just a question block
P_AddBlockThinker(sec2, master);
CheckForMarioBlocks = true; CheckForMarioBlocks = true;
} }
@ -5101,6 +5165,33 @@ static inline void P_AddBridgeThinker(line_t *sourceline, sector_t *sec)
} }
*/ */
/**
* Adds a plane displacement thinker.
* Whenever the "control" sector moves,
* the "affectee" sector's floor or ceiling plane moves too!
*
* \param speed Rate of movement relative to control sector
* \param control Control sector.
* \param affectee Target sector.
* \sa P_SpawnSpecials, T_PlaneDisplace
* \author Monster Iestyn
*/
static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control, INT32 affectee)
{
planedisplace_t *displace;
// create and initialize new displacement thinker
displace = Z_Calloc(sizeof (*displace), PU_LEVSPEC, NULL);
P_AddThinker(&displace->thinker);
displace->thinker.function.acp1 = (actionf_p1)T_PlaneDisplace;
displace->affectee = affectee;
displace->control = control;
displace->last_height = sectors[control].floorheight;
displace->speed = speed;
displace->type = type;
}
/** Adds a Mario block thinker, which changes the block's texture between blank /** Adds a Mario block thinker, which changes the block's texture between blank
* and ? depending on whether it has contents. * and ? depending on whether it has contents.
* Needed in case objects respawn inside. * Needed in case objects respawn inside.
@ -5339,7 +5430,7 @@ static inline void P_AddCameraScanner(sector_t *sourcesec, sector_t *actionsecto
elevator->distance = FixedInt(AngleFixed(angle)); elevator->distance = FixedInt(AngleFixed(angle));
} }
static const ffloortype_e laserflags = FF_EXISTS|FF_RENDERALL|FF_NOSHADE|FF_EXTRA|FF_CUTEXTRA; static const ffloortype_e laserflags = FF_EXISTS|FF_RENDERALL|FF_NOSHADE|FF_EXTRA|FF_CUTEXTRA|FF_TRANSLUCENT;
/** Flashes a laser block. /** Flashes a laser block.
* *
@ -5359,10 +5450,12 @@ void T_LaserFlash(laserthink_t *flash)
if (!ffloor || !(ffloor->flags & FF_EXISTS)) if (!ffloor || !(ffloor->flags & FF_EXISTS))
return; return;
if (leveltime & 1) if (leveltime & 2)
ffloor->flags |= FF_RENDERALL; //ffloor->flags |= FF_RENDERALL;
ffloor->alpha = 0xB0;
else else
ffloor->flags &= ~FF_RENDERALL; //ffloor->flags &= ~FF_RENDERALL;
ffloor->alpha = 0x90;
sourcesec = ffloor->master->frontsector; // Less to type! sourcesec = ffloor->master->frontsector; // Less to type!
@ -5386,6 +5479,10 @@ void T_LaserFlash(laserthink_t *flash)
&& thing->flags & MF_BOSS) && thing->flags & MF_BOSS)
continue; // Don't hurt bosses continue; // Don't hurt bosses
// Don't endlessly kill egg guard shields (or anything else for that matter)
if (thing->health <= 0)
continue;
top = P_GetSpecialTopZ(thing, sourcesec, sector); top = P_GetSpecialTopZ(thing, sourcesec, sector);
bottom = P_GetSpecialBottomZ(thing, sourcesec, sector); bottom = P_GetSpecialBottomZ(thing, sourcesec, sector);
@ -5586,32 +5683,27 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Init line EFFECTs // Init line EFFECTs
for (i = 0; i < numlines; i++) for (i = 0; i < numlines; i++)
{ {
// set line specials to 0 here too, same reason as above if (lines[i].special != 7) // This is a hack. I can at least hope nobody wants to prevent flat alignment with arbitrary skin setups...
if (netgame || multiplayer)
{ {
// future: nonet flag? // set line specials to 0 here too, same reason as above
} if (netgame || multiplayer)
else if ((lines[i].flags & ML_NETONLY) == ML_NETONLY) {
{ // future: nonet flag?
lines[i].special = 0; }
continue; else if ((lines[i].flags & ML_NETONLY) == ML_NETONLY)
}
else
{
if (players[consoleplayer].charability == CA_THOK && (lines[i].flags & ML_NOSONIC))
{ {
lines[i].special = 0; lines[i].special = 0;
continue; continue;
} }
if (players[consoleplayer].charability == CA_FLY && (lines[i].flags & ML_NOTAILS)) else
{ {
lines[i].special = 0; if ((players[consoleplayer].charability == CA_THOK && (lines[i].flags & ML_NOSONIC))
continue; || (players[consoleplayer].charability == CA_FLY && (lines[i].flags & ML_NOTAILS))
} || (players[consoleplayer].charability == CA_GLIDEANDCLIMB && (lines[i].flags & ML_NOKNUX)))
if (players[consoleplayer].charability == CA_GLIDEANDCLIMB && (lines[i].flags & ML_NOKNUX)) {
{ lines[i].special = 0;
lines[i].special = 0; continue;
continue; }
} }
} }
@ -5657,47 +5749,53 @@ void P_SpawnSpecials(INT32 fromnetsave)
break; break;
#endif #endif
case 7: // Flat alignment case 7: // Flat alignment - redone by toast
if (lines[i].flags & ML_EFFECT4) // Align angle if ((lines[i].flags & (ML_NOSONIC|ML_NOTAILS)) != (ML_NOSONIC|ML_NOTAILS)) // If you can do something...
{ {
if (!(lines[i].flags & ML_EFFECT5)) // Align floor unless ALLTRIGGER flag is set angle_t flatangle = InvAngle(R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y));
fixed_t xoffs;
fixed_t yoffs;
if (lines[i].flags & ML_NOKNUX) // Set offset through x and y texture offsets if NOKNUX flag is set
{ {
for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) xoffs = sides[lines[i].sidenum[0]].textureoffset;
sectors[s].spawn_flrpic_angle = sectors[s].floorpic_angle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y); yoffs = sides[lines[i].sidenum[0]].rowoffset;
}
else // Otherwise, set calculated offsets such that line's v1 is the apparent origin
{
fixed_t cosinecomponent = FINECOSINE(flatangle>>ANGLETOFINESHIFT);
fixed_t sinecomponent = FINESINE(flatangle>>ANGLETOFINESHIFT);
xoffs = (-FixedMul(lines[i].v1->x, cosinecomponent) % MAXFLATSIZE) + (FixedMul(lines[i].v1->y, sinecomponent) % MAXFLATSIZE); // No danger of overflow thanks to the strategically placed modulo operations.
yoffs = (FixedMul(lines[i].v1->x, sinecomponent) % MAXFLATSIZE) + (FixedMul(lines[i].v1->y, cosinecomponent) % MAXFLATSIZE); // Ditto.
} }
if (!(lines[i].flags & ML_BOUNCY)) // Align ceiling unless BOUNCY flag is set for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
{ {
for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) if (!(lines[i].flags & ML_NOSONIC)) // Modify floor flat alignment unless NOSONIC flag is set
sectors[s].spawn_ceilpic_angle = sectors[s].ceilingpic_angle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y);
}
}
else // Do offsets
{
if (!(lines[i].flags & ML_BLOCKMONSTERS)) // Align floor unless BLOCKMONSTERS flag is set
{
for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
{ {
sectors[s].floor_xoffs += lines[i].dx; sectors[s].spawn_flrpic_angle = sectors[s].floorpic_angle = flatangle;
sectors[s].floor_yoffs += lines[i].dy; sectors[s].floor_xoffs += xoffs;
sectors[s].floor_yoffs += yoffs;
// saved for netgames // saved for netgames
sectors[s].spawn_flr_xoffs = sectors[s].floor_xoffs; sectors[s].spawn_flr_xoffs = sectors[s].floor_xoffs;
sectors[s].spawn_flr_yoffs = sectors[s].floor_yoffs; sectors[s].spawn_flr_yoffs = sectors[s].floor_yoffs;
} }
}
if (!(lines[i].flags & ML_NOCLIMB)) // Align ceiling unless NOCLIMB flag is set if (!(lines[i].flags & ML_NOTAILS)) // Modify ceiling flat alignment unless NOTAILS flag is set
{
for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
{ {
sectors[s].ceiling_xoffs += lines[i].dx; sectors[s].spawn_ceilpic_angle = sectors[s].ceilingpic_angle = flatangle;
sectors[s].ceiling_yoffs += lines[i].dy; sectors[s].ceiling_xoffs += xoffs;
sectors[s].ceiling_yoffs += yoffs;
// saved for netgames // saved for netgames
sectors[s].spawn_ceil_xoffs = sectors[s].ceiling_xoffs; sectors[s].spawn_ceil_xoffs = sectors[s].ceiling_xoffs;
sectors[s].spawn_ceil_yoffs = sectors[s].ceiling_yoffs; sectors[s].spawn_ceil_yoffs = sectors[s].ceiling_yoffs;
} }
} }
} }
else // Otherwise, print a helpful warning. Can I do no less?
CONS_Alert(CONS_WARNING,
M_GetText("Flat alignment linedef (tag %d) doesn't have anything to do.\nConsider changing the linedef's flag configuration or removing it entirely.\n"),
lines[i].tag);
break; break;
case 8: // Sector Parameters case 8: // Sector Parameters
@ -5809,6 +5907,19 @@ void P_SpawnSpecials(INT32 fromnetsave)
P_AddBridgeThinker(&lines[i], &sectors[s]);*/ P_AddBridgeThinker(&lines[i], &sectors[s]);*/
break; break;
case 66: // Displace floor by front sector
for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
P_AddPlaneDisplaceThinker(pd_floor, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s);
break;
case 67: // Displace ceiling by front sector
for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
P_AddPlaneDisplaceThinker(pd_ceiling, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s);
break;
case 68: // Displace both floor AND ceiling by front sector
for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
P_AddPlaneDisplaceThinker(pd_both, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s);
break;
case 100: // FOF (solid, opaque, shadows) case 100: // FOF (solid, opaque, shadows)
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers); P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers);
break; break;
@ -5823,11 +5934,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too // Draw the 'insides' of the block too
if (lines[i].flags & ML_NOCLIMB) if (lines[i].flags & ML_NOCLIMB)
{ {
ffloorflags |= FF_CUTLEVEL; ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
ffloorflags |= FF_BOTHPLANES; ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
ffloorflags |= FF_ALLSIDES;
ffloorflags &= ~FF_EXTRA;
ffloorflags &= ~FF_CUTEXTRA;
} }
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@ -5934,11 +6042,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too // Draw the 'insides' of the block too
if (lines[i].flags & ML_EFFECT2) if (lines[i].flags & ML_EFFECT2)
{ {
ffloorflags |= FF_CUTLEVEL; ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
ffloorflags |= FF_BOTHPLANES; ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
ffloorflags |= FF_ALLSIDES;
ffloorflags &= ~FF_EXTRA;
ffloorflags &= ~FF_CUTEXTRA;
} }
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@ -5952,11 +6057,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too // Draw the 'insides' of the block too
if (lines[i].flags & ML_EFFECT2) if (lines[i].flags & ML_EFFECT2)
{ {
ffloorflags |= FF_CUTLEVEL; ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
ffloorflags |= FF_BOTHPLANES; ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
ffloorflags |= FF_ALLSIDES;
ffloorflags &= ~FF_EXTRA;
ffloorflags &= ~FF_CUTEXTRA;
} }
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@ -5980,11 +6082,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too // Draw the 'insides' of the block too
if (lines[i].flags & ML_EFFECT2) if (lines[i].flags & ML_EFFECT2)
{ {
ffloorflags |= FF_CUTLEVEL; ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
ffloorflags |= FF_BOTHPLANES; ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
ffloorflags |= FF_ALLSIDES;
ffloorflags &= ~FF_EXTRA;
ffloorflags &= ~FF_CUTEXTRA;
} }
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@ -5998,11 +6097,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too // Draw the 'insides' of the block too
if (lines[i].flags & ML_EFFECT2) if (lines[i].flags & ML_EFFECT2)
{ {
ffloorflags |= FF_CUTLEVEL; ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
ffloorflags |= FF_BOTHPLANES; ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
ffloorflags |= FF_ALLSIDES;
ffloorflags &= ~FF_EXTRA;
ffloorflags &= ~FF_CUTEXTRA;
} }
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@ -6180,7 +6276,13 @@ void P_SpawnSpecials(INT32 fromnetsave)
break; break;
case 250: // Mario Block case 250: // Mario Block
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL|FF_MARIO, secthinkers); ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL|FF_MARIO;
if (lines[i].flags & ML_NOCLIMB)
ffloorflags |= FF_SHATTERBOTTOM;
if (lines[i].flags & ML_EFFECT1)
ffloorflags &= ~(FF_SOLID|FF_RENDERALL|FF_CUTLEVEL);
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
break; break;
case 251: // A THWOMP! case 251: // A THWOMP!
@ -6194,10 +6296,11 @@ void P_SpawnSpecials(INT32 fromnetsave)
break; break;
case 252: // Shatter block (breaks when touched) case 252: // Shatter block (breaks when touched)
ffloorflags = FF_EXISTS|FF_RENDERALL|FF_BUSTUP|FF_SHATTER;
if (lines[i].flags & ML_NOCLIMB) if (lines[i].flags & ML_NOCLIMB)
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP|FF_SHATTER|FF_SHATTERBOTTOM, secthinkers); ffloorflags |= FF_SOLID|FF_SHATTERBOTTOM;
else
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_RENDERALL|FF_BUSTUP|FF_SHATTER, secthinkers); P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
break; break;
case 253: // Translucent shatter block (see 76) case 253: // Translucent shatter block (see 76)
@ -6205,10 +6308,11 @@ void P_SpawnSpecials(INT32 fromnetsave)
break; break;
case 254: // Bustable block case 254: // Bustable block
ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP;
if (lines[i].flags & ML_NOCLIMB) if (lines[i].flags & ML_NOCLIMB)
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP|FF_ONLYKNUX, secthinkers); ffloorflags |= FF_ONLYKNUX;
else
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP, secthinkers); P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
break; break;
case 255: // Spin bust block (breaks when jumped or spun downwards onto) case 255: // Spin bust block (breaks when jumped or spun downwards onto)
@ -6220,10 +6324,11 @@ void P_SpawnSpecials(INT32 fromnetsave)
break; break;
case 257: // Quicksand case 257: // Quicksand
ffloorflags = FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES;
if (lines[i].flags & ML_EFFECT5) if (lines[i].flags & ML_EFFECT5)
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES|FF_RIPPLE, secthinkers); ffloorflags |= FF_RIPPLE;
else
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES, secthinkers); P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
break; break;
case 258: // Laser block case 258: // Laser block
@ -6984,7 +7089,6 @@ void T_Disappear(disappear_t *d)
/** Adds friction thinker. /** Adds friction thinker.
* *
* \param friction Friction value, 0xe800 is normal. * \param friction Friction value, 0xe800 is normal.
* \param movefactor Inertia factor.
* \param affectee Target sector. * \param affectee Target sector.
* \param roverfriction FOF or not * \param roverfriction FOF or not
* \sa T_Friction, P_SpawnFriction * \sa T_Friction, P_SpawnFriction
@ -7022,22 +7126,10 @@ void T_Friction(friction_t *f)
sec = sectors + f->affectee; sec = sectors + f->affectee;
// Make sure the sector type hasn't changed // Get FOF control sector
if (f->roverfriction) if (f->roverfriction)
{
referrer = sectors + f->referrer; referrer = sectors + f->referrer;
if (!(GETSECSPECIAL(referrer->special, 3) == 1
|| GETSECSPECIAL(referrer->special, 3) == 3))
return;
}
else
{
if (!(GETSECSPECIAL(sec->special, 3) == 1
|| GETSECSPECIAL(sec->special, 3) == 3))
return;
}
// Assign the friction value to players on the floor, non-floating, // Assign the friction value to players on the floor, non-floating,
// and clipped. Normally the object's friction value is kept at // and clipped. Normally the object's friction value is kept at
// ORIG_FRICTION and this thinker changes it for icy or muddy floors. // ORIG_FRICTION and this thinker changes it for icy or muddy floors.
@ -7067,14 +7159,16 @@ void T_Friction(friction_t *f)
|| (f->friction < thing->friction)) || (f->friction < thing->friction))
{ {
thing->friction = f->friction; thing->friction = f->friction;
thing->movefactor = f->movefactor; if (thing->player)
thing->movefactor = f->movefactor;
} }
} }
else if (P_GetSpecialBottomZ(thing, sec, sec) == thing->floorz && (thing->friction == ORIG_FRICTION // normal friction? else if (P_GetSpecialBottomZ(thing, sec, sec) == thing->floorz && (thing->friction == ORIG_FRICTION // normal friction?
|| f->friction < thing->friction)) || f->friction < thing->friction))
{ {
thing->friction = f->friction; thing->friction = f->friction;
thing->movefactor = f->movefactor; if (thing->player)
thing->movefactor = f->movefactor;
} }
} }
node = node->m_thinglist_next; node = node->m_thinglist_next;
@ -7090,33 +7184,32 @@ static void P_SpawnFriction(void)
size_t i; size_t i;
line_t *l = lines; line_t *l = lines;
register INT32 s; register INT32 s;
fixed_t length; // line length controls magnitude fixed_t strength; // frontside texture offset controls magnitude
fixed_t friction; // friction value to be applied during movement fixed_t friction; // friction value to be applied during movement
INT32 movefactor; // applied to each player move to simulate inertia INT32 movefactor; // applied to each player move to simulate inertia
for (i = 0; i < numlines; i++, l++) for (i = 0; i < numlines; i++, l++)
if (l->special == 540) if (l->special == 540)
{ {
length = P_AproxDistance(l->dx, l->dy)>>FRACBITS; strength = sides[l->sidenum[0]].textureoffset>>FRACBITS;
friction = (0x1EB8*length)/0x80 + 0xD000; if (strength > 0) // sludge
strength = strength*2; // otherwise, the maximum sludginess value is +967...
// The following might seem odd. At the time of movement,
// the move distance is multiplied by 'friction/0x10000', so a
// higher friction value actually means 'less friction'.
friction = ORIG_FRICTION - (0x1EB8*strength)/0x80; // ORIG_FRICTION is 0xE800
if (friction > FRACUNIT) if (friction > FRACUNIT)
friction = FRACUNIT; friction = FRACUNIT;
if (friction < 0) if (friction < 0)
friction = 0; friction = 0;
// The following check might seem odd. At the time of movement, movefactor = FixedDiv(ORIG_FRICTION, friction);
// the move distance is multiplied by 'friction/0x10000', so a if (movefactor < FRACUNIT)
// higher friction value actually means 'less friction'. movefactor = 8*movefactor - 7*FRACUNIT;
if (friction > ORIG_FRICTION) // ice
movefactor = ((0x10092 - friction)*(0x70))/0x158;
else else
movefactor = ((friction - 0xDB34)*(0xA))/0x80; movefactor = FRACUNIT;
// killough 8/28/98: prevent odd situations
if (movefactor < 32)
movefactor = 32;
for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
Add_Friction(friction, movefactor, s, -1); Add_Friction(friction, movefactor, s, -1);
@ -7366,12 +7459,10 @@ void T_Pusher(pusher_t *p)
{ {
referrer = &sectors[p->referrer]; referrer = &sectors[p->referrer];
if (!(GETSECSPECIAL(referrer->special, 3) == 2 if (GETSECSPECIAL(referrer->special, 3) != 2)
|| GETSECSPECIAL(referrer->special, 3) == 3))
return; return;
} }
else if (!(GETSECSPECIAL(sec->special, 3) == 2 else if (GETSECSPECIAL(sec->special, 3) != 2)
|| GETSECSPECIAL(sec->special, 3) == 3))
return; return;
// For constant pushers (wind/current) there are 3 situations: // For constant pushers (wind/current) there are 3 situations:

View file

@ -64,6 +64,8 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller); void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller);
void P_ChangeSectorTag(UINT32 sector, INT16 newtag); void P_ChangeSectorTag(UINT32 sector, INT16 newtag);
ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id);
// //
// P_LIGHTS // P_LIGHTS
// //
@ -323,7 +325,7 @@ INT32 EV_StartCrumble(sector_t *sector, ffloor_t *rover,
INT32 EV_DoContinuousFall(sector_t *sec, sector_t *pbacksector, fixed_t spd, boolean backwards); INT32 EV_DoContinuousFall(sector_t *sec, sector_t *pbacksector, fixed_t spd, boolean backwards);
INT32 EV_MarioBlock(sector_t *sector, sector_t *roversector, fixed_t topheight, mobj_t *puncher); INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher);
void T_MoveFloor(floormove_t *movefloor); void T_MoveFloor(floormove_t *movefloor);
@ -386,7 +388,7 @@ typedef struct
{ {
thinker_t thinker; ///< Thinker structure for friction. thinker_t thinker; ///< Thinker structure for friction.
INT32 friction; ///< Friction value, 0xe800 = normal. INT32 friction; ///< Friction value, 0xe800 = normal.
INT32 movefactor; ///< Inertia factor when adding to momentum. INT32 movefactor; ///< Inertia factor when adding to momentum, FRACUNIT = normal.
INT32 affectee; ///< Number of affected sector. INT32 affectee; ///< Number of affected sector.
INT32 referrer; ///< If roverfriction == true, then this will contain the sector # of the control sector where the effect was applied. INT32 referrer; ///< If roverfriction == true, then this will contain the sector # of the control sector where the effect was applied.
UINT8 roverfriction; ///< flag for whether friction originated from a FOF or not UINT8 roverfriction; ///< flag for whether friction originated from a FOF or not
@ -448,6 +450,26 @@ void T_Disappear(disappear_t *d);
void T_Pusher(pusher_t *p); void T_Pusher(pusher_t *p);
mobj_t *P_GetPushThing(UINT32 s); mobj_t *P_GetPushThing(UINT32 s);
// Plane displacement
typedef struct
{
thinker_t thinker; ///< Thinker structure for plane displacement effect.
INT32 affectee; ///< Number of affected sector.
INT32 control; ///< Control sector used to control plane positions.
fixed_t last_height; ///< Last known height of control sector.
fixed_t speed; ///< Plane movement speed.
/** Types of plane displacement effects.
*/
enum
{
pd_floor, ///< Displace floor.
pd_ceiling, ///< Displace ceiling.
pd_both, ///< Displace both floor AND ceiling.
} type;
} planedisplace_t;
void T_PlaneDisplace(planedisplace_t *pd);
void P_CalcHeight(player_t *player); void P_CalcHeight(player_t *player);
sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo); sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo);

View file

@ -469,7 +469,7 @@ static inline void P_DoSpecialStageStuff(void)
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i]) if (playeringame[i])
{ {
ssrings += (players[i].mo->health-1); ssrings += players[i].rings;
// If in water, deplete timer 6x as fast. // If in water, deplete timer 6x as fast.
if ((players[i].mo->eflags & MFE_TOUCHWATER) if ((players[i].mo->eflags & MFE_TOUCHWATER)

File diff suppressed because it is too large Load diff

View file

@ -859,6 +859,7 @@ static void R_Subsector(size_t num)
static sector_t tempsec; // Deep water hack static sector_t tempsec; // Deep water hack
extracolormap_t *floorcolormap; extracolormap_t *floorcolormap;
extracolormap_t *ceilingcolormap; extracolormap_t *ceilingcolormap;
fixed_t floorcenterz, ceilingcenterz;
#ifdef RANGECHECK #ifdef RANGECHECK
if (num >= numsubsectors) if (num >= numsubsectors)
@ -879,6 +880,18 @@ static void R_Subsector(size_t num)
floorcolormap = ceilingcolormap = frontsector->extra_colormap; floorcolormap = ceilingcolormap = frontsector->extra_colormap;
floorcenterz =
#ifdef ESLOPE
frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->floorheight;
ceilingcenterz =
#ifdef ESLOPE
frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->ceilingheight;
// Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps. // Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps.
if (frontsector->ffloors) if (frontsector->ffloors)
{ {
@ -891,19 +904,11 @@ static void R_Subsector(size_t num)
sub->sector->moved = frontsector->moved = false; sub->sector->moved = frontsector->moved = false;
} }
light = R_GetPlaneLight(frontsector, light = R_GetPlaneLight(frontsector, floorcenterz, false);
#ifdef ESLOPE
frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->floorheight, false);
if (frontsector->floorlightsec == -1) if (frontsector->floorlightsec == -1)
floorlightlevel = *frontsector->lightlist[light].lightlevel; floorlightlevel = *frontsector->lightlist[light].lightlevel;
floorcolormap = frontsector->lightlist[light].extra_colormap; floorcolormap = frontsector->lightlist[light].extra_colormap;
light = R_GetPlaneLight(frontsector, light = R_GetPlaneLight(frontsector, ceilingcenterz, false);
#ifdef ESLOPE
frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->ceilingheight, false);
if (frontsector->ceilinglightsec == -1) if (frontsector->ceilinglightsec == -1)
ceilinglightlevel = *frontsector->lightlist[light].lightlevel; ceilinglightlevel = *frontsector->lightlist[light].lightlevel;
ceilingcolormap = frontsector->lightlist[light].extra_colormap; ceilingcolormap = frontsector->lightlist[light].extra_colormap;
@ -920,6 +925,9 @@ static void R_Subsector(size_t num)
{ {
floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, floorlightlevel, floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, floorlightlevel,
frontsector->floor_xoffs, frontsector->floor_yoffs, frontsector->floorpic_angle, floorcolormap, NULL frontsector->floor_xoffs, frontsector->floor_yoffs, frontsector->floorpic_angle, floorcolormap, NULL
#ifdef POLYOBJECTS_PLANES
, NULL
#endif
#ifdef ESLOPE #ifdef ESLOPE
, frontsector->f_slope , frontsector->f_slope
#endif #endif
@ -939,6 +947,9 @@ static void R_Subsector(size_t num)
ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic, ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic,
ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle, ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle,
ceilingcolormap, NULL ceilingcolormap, NULL
#ifdef POLYOBJECTS_PLANES
, NULL
#endif
#ifdef ESLOPE #ifdef ESLOPE
, frontsector->c_slope , frontsector->c_slope
#endif #endif
@ -956,7 +967,7 @@ static void R_Subsector(size_t num)
if (frontsector->ffloors) if (frontsector->ffloors)
{ {
ffloor_t *rover; ffloor_t *rover;
fixed_t heightcheck, planecenterz, floorcenterz, ceilingcenterz; fixed_t heightcheck, planecenterz;
for (rover = frontsector->ffloors; rover && numffloors < MAXFFLOORS; rover = rover->next) for (rover = frontsector->ffloors; rover && numffloors < MAXFFLOORS; rover = rover->next)
{ {
@ -975,18 +986,6 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = NULL; ffloor[numffloors].plane = NULL;
ffloor[numffloors].polyobj = NULL; ffloor[numffloors].polyobj = NULL;
floorcenterz =
#ifdef ESLOPE
frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->floorheight;
ceilingcenterz =
#ifdef ESLOPE
frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->ceilingheight;
heightcheck = heightcheck =
#ifdef ESLOPE #ifdef ESLOPE
*rover->b_slope ? P_GetZAt(*rover->b_slope, viewx, viewy) : *rover->b_slope ? P_GetZAt(*rover->b_slope, viewx, viewy) :
@ -1009,6 +1008,9 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = R_FindPlane(*rover->bottomheight, *rover->bottompic, ffloor[numffloors].plane = R_FindPlane(*rover->bottomheight, *rover->bottompic,
*frontsector->lightlist[light].lightlevel, *rover->bottomxoffs, *frontsector->lightlist[light].lightlevel, *rover->bottomxoffs,
*rover->bottomyoffs, *rover->bottomangle, frontsector->lightlist[light].extra_colormap, rover *rover->bottomyoffs, *rover->bottomangle, frontsector->lightlist[light].extra_colormap, rover
#ifdef POLYOBJECTS_PLANES
, NULL
#endif
#ifdef ESLOPE #ifdef ESLOPE
, *rover->b_slope , *rover->b_slope
#endif #endif
@ -1052,6 +1054,9 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = R_FindPlane(*rover->topheight, *rover->toppic, ffloor[numffloors].plane = R_FindPlane(*rover->topheight, *rover->toppic,
*frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, *rover->topangle, *frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, *rover->topangle,
frontsector->lightlist[light].extra_colormap, rover frontsector->lightlist[light].extra_colormap, rover
#ifdef POLYOBJECTS_PLANES
, NULL
#endif
#ifdef ESLOPE #ifdef ESLOPE
, *rover->t_slope , *rover->t_slope
#endif #endif
@ -1093,8 +1098,8 @@ static void R_Subsector(size_t num)
polysec = po->lines[0]->backsector; polysec = po->lines[0]->backsector;
ffloor[numffloors].plane = NULL; ffloor[numffloors].plane = NULL;
if (polysec->floorheight <= frontsector->ceilingheight if (polysec->floorheight <= ceilingcenterz
&& polysec->floorheight >= frontsector->floorheight && polysec->floorheight >= floorcenterz
&& (viewz < polysec->floorheight)) && (viewz < polysec->floorheight))
{ {
fixed_t xoff, yoff; fixed_t xoff, yoff;
@ -1118,11 +1123,13 @@ static void R_Subsector(size_t num)
polysec->floorpic_angle-po->angle, polysec->floorpic_angle-po->angle,
NULL, NULL,
NULL NULL
#ifdef POLYOBJECTS_PLANES
, po
#endif
#ifdef ESLOPE #ifdef ESLOPE
, NULL // will ffloors be slopable eventually? , NULL // will ffloors be slopable eventually?
#endif #endif
); );
//ffloor[numffloors].plane->polyobj = po;
ffloor[numffloors].height = polysec->floorheight; ffloor[numffloors].height = polysec->floorheight;
ffloor[numffloors].polyobj = po; ffloor[numffloors].polyobj = po;
@ -1139,8 +1146,8 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = NULL; ffloor[numffloors].plane = NULL;
if (polysec->ceilingheight >= frontsector->floorheight if (polysec->ceilingheight >= floorcenterz
&& polysec->ceilingheight <= frontsector->ceilingheight && polysec->ceilingheight <= ceilingcenterz
&& (viewz > polysec->ceilingheight)) && (viewz > polysec->ceilingheight))
{ {
fixed_t xoff, yoff; fixed_t xoff, yoff;
@ -1162,11 +1169,13 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic, ffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic,
polysec->lightlevel, xoff, yoff, polysec->ceilingpic_angle-po->angle, polysec->lightlevel, xoff, yoff, polysec->ceilingpic_angle-po->angle,
NULL, NULL NULL, NULL
#ifdef POLYOBJECTS_PLANES
, po
#endif
#ifdef ESLOPE #ifdef ESLOPE
, NULL // will ffloors be slopable eventually? , NULL // will ffloors be slopable eventually?
#endif #endif
); );
//ffloor[numffloors].plane->polyobj = po;
ffloor[numffloors].polyobj = po; ffloor[numffloors].polyobj = po;
ffloor[numffloors].height = polysec->ceilingheight; ffloor[numffloors].height = polysec->ceilingheight;

View file

@ -160,6 +160,7 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, INT32 orig
if (position < 0) if (position < 0)
{ {
count += position; count += position;
source -= position; // start further down the column
position = 0; position = 0;
} }
@ -173,6 +174,44 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, INT32 orig
} }
} }
static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, INT32 originy, INT32 cacheheight, INT32 patchheight)
{
INT32 count, position;
UINT8 *source, *dest;
INT32 topdelta, prevdelta = -1;
while (patch->topdelta != 0xff)
{
topdelta = patch->topdelta;
if (topdelta <= prevdelta)
topdelta += prevdelta;
prevdelta = topdelta;
topdelta = patchheight-patch->length-topdelta;
source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
count = patch->length;
position = originy + topdelta;
if (position < 0)
{
count += position;
source += position; // start further UP the column
position = 0;
}
if (position + count > cacheheight)
count = cacheheight - position;
dest = cache + position;
if (count > 0)
{
for (; dest < cache + position + count; --source)
*dest++ = *source;
}
patch = (column_t *)((UINT8 *)patch + patch->length + 4);
}
}
// //
// R_GenerateTexture // R_GenerateTexture
// //
@ -191,7 +230,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
texture_t *texture; texture_t *texture;
texpatch_t *patch; texpatch_t *patch;
patch_t *realpatch; patch_t *realpatch;
int x, x1, x2, i; int x, x1, x2, i, width, height;
size_t blocksize; size_t blocksize;
column_t *patchcol; column_t *patchcol;
UINT32 *colofs; UINT32 *colofs;
@ -239,6 +278,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
if (holey) if (holey)
{ {
texture->holes = true; texture->holes = true;
texture->flip = patch->flip;
blocksize = W_LumpLengthPwad(patch->wad, patch->lump); blocksize = W_LumpLengthPwad(patch->wad, patch->lump);
block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function
&texturecache[texnum]); &texturecache[texnum]);
@ -249,6 +289,14 @@ static UINT8 *R_GenerateTexture(size_t texnum)
colofs = (UINT32 *)(void *)(block + 8); colofs = (UINT32 *)(void *)(block + 8);
texturecolumnofs[texnum] = colofs; texturecolumnofs[texnum] = colofs;
blocktex = block; blocktex = block;
if (patch->flip & 1) // flip the patch horizontally
{
UINT32 *realcolofs = (UINT32 *)realpatch->columnofs;
for (x = 0; x < texture->width; x++)
colofs[x] = realcolofs[texture->width-1-x]; // swap with the offset of the other side of the texture
}
// we can't as easily flip the patch vertically sadly though,
// we have wait until the texture itself is drawn to do that
for (x = 0; x < texture->width; x++) for (x = 0; x < texture->width; x++)
colofs[x] = LONG(LONG(colofs[x]) + 3); colofs[x] = LONG(LONG(colofs[x]) + 3);
goto done; goto done;
@ -259,11 +307,12 @@ static UINT8 *R_GenerateTexture(size_t texnum)
// multi-patch textures (or 'composite') // multi-patch textures (or 'composite')
texture->holes = false; texture->holes = false;
texture->flip = 0;
blocksize = (texture->width * 4) + (texture->width * texture->height); blocksize = (texture->width * 4) + (texture->width * texture->height);
texturememory += blocksize; texturememory += blocksize;
block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]); block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]);
memset(block, 0xF7, blocksize+1); // Transparency hack memset(block, 0xFF, blocksize+1); // Transparency hack
// columns lookup table // columns lookup table
colofs = (UINT32 *)(void *)block; colofs = (UINT32 *)(void *)block;
@ -277,7 +326,9 @@ static UINT8 *R_GenerateTexture(size_t texnum)
{ {
realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
x1 = patch->originx; x1 = patch->originx;
x2 = x1 + SHORT(realpatch->width); width = SHORT(realpatch->width);
height = SHORT(realpatch->height);
x2 = x1 + width;
if (x1 < 0) if (x1 < 0)
x = 0; x = 0;
@ -289,11 +340,17 @@ static UINT8 *R_GenerateTexture(size_t texnum)
for (; x < x2; x++) for (; x < x2; x++)
{ {
patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1])); if (patch->flip & 1)
patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x]));
else
patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1]));
// generate column ofset lookup // generate column ofset lookup
colofs[x] = LONG((x * texture->height) + (texture->width*4)); colofs[x] = LONG((x * texture->height) + (texture->width*4));
R_DrawColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height); if (patch->flip & 2)
R_DrawFlippedColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height, height);
else
R_DrawColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height);
} }
} }
@ -303,6 +360,32 @@ done:
return blocktex; return blocktex;
} }
//
// R_GetTextureNum
//
// Returns the actual texture id that we should use.
// This can either be texnum, the current frame for texnum's anim (if animated),
// or 0 if not valid.
//
INT32 R_GetTextureNum(INT32 texnum)
{
if (texnum < 0 || texnum >= numtextures)
return 0;
return texturetranslation[texnum];
}
//
// R_CheckTextureCache
//
// Use this if you need to make sure the texture is cached before R_GetColumn calls
// e.g.: midtextures and FOF walls
//
void R_CheckTextureCache(INT32 tex)
{
if (!texturecache[tex])
R_GenerateTexture(tex);
}
// //
// R_GetColumn // R_GetColumn
// //
@ -469,6 +552,7 @@ void R_LoadTextures(void)
texture->height = SHORT(patchlump->height)*patchcount; texture->height = SHORT(patchlump->height)*patchcount;
texture->patchcount = patchcount; texture->patchcount = patchcount;
texture->holes = false; texture->holes = false;
texture->flip = 0;
// Allocate information for the texture's patches. // Allocate information for the texture's patches.
for (k = 0; k < patchcount; k++) for (k = 0; k < patchcount; k++)
@ -479,6 +563,7 @@ void R_LoadTextures(void)
patch->originy = (INT16)(k*patchlump->height); patch->originy = (INT16)(k*patchlump->height);
patch->wad = (UINT16)w; patch->wad = (UINT16)w;
patch->lump = texstart + j; patch->lump = texstart + j;
patch->flip = 0;
} }
Z_Unlock(patchlump); Z_Unlock(patchlump);
@ -502,6 +587,7 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
char *patchName = NULL; char *patchName = NULL;
INT16 patchXPos; INT16 patchXPos;
INT16 patchYPos; INT16 patchYPos;
UINT8 flip = 0;
texpatch_t *resultPatch = NULL; texpatch_t *resultPatch = NULL;
lumpnum_t patchLumpNum; lumpnum_t patchLumpNum;
@ -598,6 +684,47 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
} }
Z_Free(texturesToken); Z_Free(texturesToken);
// Patch parameters block (OPTIONAL)
// added by Monster Iestyn (22/10/16)
// Left Curly Brace
texturesToken = M_GetToken(NULL);
if (texturesToken == NULL)
; // move on and ignore, R_ParseTextures will deal with this
else
{
if (strcmp(texturesToken,"{")==0)
{
Z_Free(texturesToken);
texturesToken = M_GetToken(NULL);
if (texturesToken == NULL)
{
I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName);
}
while (strcmp(texturesToken,"}")!=0)
{
if (stricmp(texturesToken, "FLIPX")==0)
flip |= 1;
else if (stricmp(texturesToken, "FLIPY")==0)
flip |= 2;
Z_Free(texturesToken);
texturesToken = M_GetToken(NULL);
if (texturesToken == NULL)
{
I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName);
}
}
}
else
{
// this is not what we wanted...
// undo last read so R_ParseTextures can re-get the token for its own purposes
M_UnGetToken();
}
Z_Free(texturesToken);
}
if (actuallyLoadPatch == true) if (actuallyLoadPatch == true)
{ {
// Check lump exists // Check lump exists
@ -608,6 +735,7 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
resultPatch->originy = patchYPos; resultPatch->originy = patchYPos;
resultPatch->lump = patchLumpNum & 65535; resultPatch->lump = patchLumpNum & 65535;
resultPatch->wad = patchLumpNum>>16; resultPatch->wad = patchLumpNum>>16;
resultPatch->flip = flip;
// Clean up a little after ourselves // Clean up a little after ourselves
Z_Free(patchName); Z_Free(patchName);
// Then return it // Then return it

View file

@ -31,6 +31,7 @@ typedef struct
// Block origin (always UL), which has already accounted for the internal origin of the patch. // Block origin (always UL), which has already accounted for the internal origin of the patch.
INT16 originx, originy; INT16 originx, originy;
UINT16 wad, lump; UINT16 wad, lump;
UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both
} texpatch_t; } texpatch_t;
// A maptexturedef_t describes a rectangular texture, // A maptexturedef_t describes a rectangular texture,
@ -42,6 +43,7 @@ typedef struct
char name[8]; char name[8];
INT16 width, height; INT16 width, height;
boolean holes; boolean holes;
UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both
// All the patches[patchcount] are drawn back to front into the cached texture. // All the patches[patchcount] are drawn back to front into the cached texture.
INT16 patchcount; INT16 patchcount;
@ -65,6 +67,9 @@ extern CV_PossibleValue_t Color_cons_t[];
void R_LoadTextures(void); void R_LoadTextures(void);
void R_FlushTextureCache(void); void R_FlushTextureCache(void);
INT32 R_GetTextureNum(INT32 texnum);
void R_CheckTextureCache(INT32 tex);
// Retrieve column data for span blitting. // Retrieve column data for span blitting.
UINT8 *R_GetColumn(fixed_t tex, INT32 col); UINT8 *R_GetColumn(fixed_t tex, INT32 col);

View file

@ -203,6 +203,7 @@ typedef struct r_lightlist_s
fixed_t heightstep; fixed_t heightstep;
fixed_t botheight; fixed_t botheight;
fixed_t botheightstep; fixed_t botheightstep;
fixed_t startheight; // for repeating midtextures
INT16 lightlevel; INT16 lightlevel;
extracolormap_t *extra_colormap; extracolormap_t *extra_colormap;
lighttable_t *rcolormap; lighttable_t *rcolormap;
@ -224,15 +225,6 @@ typedef struct linechain_s
// ZDoom C++ to Legacy C conversion Tails 04-29-2002 (for slopes)
typedef struct secplane_t
{
// the plane is defined as a*x + b*y + c*z + d = 0
// ic is 1/c, for faster Z calculations
fixed_t a, b, c, d, ic;
} secplane_t;
// Slopes // Slopes
#ifdef ESLOPE #ifdef ESLOPE
typedef enum { typedef enum {
@ -392,6 +384,7 @@ typedef struct sector_s
#endif #endif
// these are saved for netgames, so do not let Lua touch these! // these are saved for netgames, so do not let Lua touch these!
INT32 spawn_nexttag, spawn_firsttag; // the actual nexttag/firsttag values may differ if the sector's tag was changed
// offsets sector spawned with (via linedef type 7) // offsets sector spawned with (via linedef type 7)
fixed_t spawn_flr_xoffs, spawn_flr_yoffs; fixed_t spawn_flr_xoffs, spawn_flr_yoffs;

View file

@ -162,6 +162,7 @@ void R_DrawSplat_8(void);
void R_DrawTranslucentSplat_8(void); void R_DrawTranslucentSplat_8(void);
void R_DrawTranslucentSpan_8(void); void R_DrawTranslucentSpan_8(void);
void R_Draw2sMultiPatchColumn_8(void); void R_Draw2sMultiPatchColumn_8(void);
void R_Draw2sMultiPatchTranslucentColumn_8(void);
void R_DrawFogSpan_8(void); void R_DrawFogSpan_8(void);
void R_DrawFogColumn_8(void); void R_DrawFogColumn_8(void);
void R_DrawColumnShadowed_8(void); void R_DrawColumnShadowed_8(void);

View file

@ -203,6 +203,103 @@ void R_Draw2sMultiPatchColumn_8(void)
} }
} }
void R_Draw2sMultiPatchTranslucentColumn_8(void)
{
INT32 count;
register UINT8 *dest;
register fixed_t frac;
fixed_t fracstep;
count = dc_yh - dc_yl;
if (count < 0) // Zero length, column does not exceed a pixel.
return;
#ifdef RANGECHECK
if ((unsigned)dc_x >= (unsigned)vid.width || dc_yl < 0 || dc_yh >= vid.height)
return;
#endif
// Framebuffer destination address.
// Use ylookup LUT to avoid multiply with ScreenWidth.
// Use columnofs LUT for subwindows?
//dest = ylookup[dc_yl] + columnofs[dc_x];
dest = &topleft[dc_yl*vid.width + dc_x];
count++;
// Determine scaling, which is the only mapping to be done.
fracstep = dc_iscale;
//frac = dc_texturemid + (dc_yl - centery)*fracstep;
frac = (dc_texturemid + FixedMul((dc_yl << FRACBITS) - centeryfrac, fracstep))*(!dc_hires);
// Inner loop that does the actual texture mapping, e.g. a DDA-like scaling.
// This is as fast as it gets.
{
register const UINT8 *source = dc_source;
register const UINT8 *transmap = dc_transmap;
register const lighttable_t *colormap = dc_colormap;
register INT32 heightmask = dc_texheight-1;
register UINT8 val;
if (dc_texheight & heightmask) // not a power of 2 -- killough
{
heightmask++;
heightmask <<= FRACBITS;
if (frac < 0)
while ((frac += heightmask) < 0);
else
while (frac >= heightmask)
frac -= heightmask;
do
{
// Re-map color indices from wall texture column
// using a lighting/special effects LUT.
// heightmask is the Tutti-Frutti fix
val = source[frac>>FRACBITS];
if (val != TRANSPARENTPIXEL)
*dest = colormap[*(transmap + (val<<8) + (*dest))];
dest += vid.width;
// Avoid overflow.
if (fracstep > 0x7FFFFFFF - frac)
frac += fracstep - heightmask;
else
frac += fracstep;
while (frac >= heightmask)
frac -= heightmask;
} while (--count);
}
else
{
while ((count -= 2) >= 0) // texture height is a power of 2
{
val = source[(frac>>FRACBITS) & heightmask];
if (val != TRANSPARENTPIXEL)
*dest = colormap[*(transmap + (val<<8) + (*dest))];
dest += vid.width;
frac += fracstep;
val = source[(frac>>FRACBITS) & heightmask];
if (val != TRANSPARENTPIXEL)
*dest = colormap[*(transmap + (val<<8) + (*dest))];
dest += vid.width;
frac += fracstep;
}
if (count & 1)
{
val = source[(frac>>FRACBITS) & heightmask];
if (val != TRANSPARENTPIXEL)
*dest = colormap[*(transmap + (val<<8) + (*dest))];
}
}
}
}
/** \brief The R_DrawShadeColumn_8 function /** \brief The R_DrawShadeColumn_8 function
Experiment to make software go faster. Taken from the Boom source Experiment to make software go faster. Taken from the Boom source
*/ */

View file

@ -148,7 +148,6 @@ consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo,
consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_soniccd = {"soniccd", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_translucenthud = {"translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_translucenthud = {"translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@ -366,69 +365,6 @@ fixed_t R_PointToDist(fixed_t x, fixed_t y)
return R_PointToDist2(viewx, viewy, x, y); return R_PointToDist2(viewx, viewy, x, y);
} }
/***************************************
*** Zdoom C++ to Legacy C conversion ***
****************************************/
// Utility to find the Z height at an XY location in a sector (for slopes)
fixed_t R_SecplaneZatPoint(secplane_t *secplane, fixed_t x, fixed_t y)
{
return FixedMul(secplane->ic, -secplane->d - DMulScale16(secplane->a, x, secplane->b, y));
}
// Returns the value of z at (x,y) if d is equal to dist
fixed_t R_SecplaneZatPointDist (secplane_t *secplane, fixed_t x, fixed_t y, fixed_t dist)
{
return FixedMul(secplane->ic, -dist - DMulScale16(secplane->a, x, secplane->b, y));
}
// Flips the plane's vertical orientiation, so that if it pointed up,
// it will point down, and vice versa.
void R_SecplaneFlipVert(secplane_t *secplane)
{
secplane->a = -secplane->a;
secplane->b = -secplane->b;
secplane->c = -secplane->c;
secplane->d = -secplane->d;
secplane->ic = -secplane->ic;
}
// Returns true if 2 planes are the same
boolean R_ArePlanesSame(secplane_t *original, secplane_t *other)
{
return original->a == other->a && original->b == other->b
&& original->c == other->c && original->d == other->d;
}
// Returns true if 2 planes are different
boolean R_ArePlanesDifferent(secplane_t *original, secplane_t *other)
{
return original->a != other->a || original->b != other->b
|| original->c != other->c || original->d != other->d;
}
// Moves a plane up/down by hdiff units
void R_SecplaneChangeHeight(secplane_t *secplane, fixed_t hdiff)
{
secplane->d = secplane->d - FixedMul(hdiff, secplane->c);
}
// Returns how much this plane's height would change if d were set to oldd
fixed_t R_SecplaneHeightDiff(secplane_t *secplane, fixed_t oldd)
{
return FixedMul(oldd - secplane->d, secplane->ic);
}
fixed_t R_SecplanePointToDist(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z)
{
return -TMulScale16(secplane->a, x, y, secplane->b, z, secplane->c);
}
fixed_t R_SecplanePointToDist2(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z)
{
return -TMulScale16(secplane->a, x, secplane->b, y, z, secplane->c);
}
// //
// R_ScaleFromGlobalAngle // R_ScaleFromGlobalAngle
// Returns the texture mapping scale for the current line (horizontal span) // Returns the texture mapping scale for the current line (horizontal span)
@ -1424,7 +1360,6 @@ void R_RegisterEngineStuff(void)
{ {
CV_RegisterVar(&cv_gravity); CV_RegisterVar(&cv_gravity);
CV_RegisterVar(&cv_tailspickup); CV_RegisterVar(&cv_tailspickup);
CV_RegisterVar(&cv_soniccd);
CV_RegisterVar(&cv_allowmlook); CV_RegisterVar(&cv_allowmlook);
CV_RegisterVar(&cv_homremoval); CV_RegisterVar(&cv_homremoval);
CV_RegisterVar(&cv_flipcam); CV_RegisterVar(&cv_flipcam);

View file

@ -61,18 +61,6 @@ angle_t R_PointToAngle2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1);
fixed_t R_PointToDist(fixed_t x, fixed_t y); fixed_t R_PointToDist(fixed_t x, fixed_t y);
fixed_t R_PointToDist2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1); fixed_t R_PointToDist2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1);
// ZDoom C++ to Legacy C conversion Tails 04-29-2002
fixed_t R_SecplaneZatPoint(secplane_t *secplane, fixed_t x, fixed_t y);
fixed_t R_SecplaneZatPointDist(secplane_t *secplane, fixed_t x, fixed_t y,
fixed_t dist);
void R_SecplaneFlipVert(secplane_t *secplane);
boolean R_ArePlanesSame(secplane_t *original, secplane_t *other);
boolean R_ArePlanesDifferent(secplane_t *original, secplane_t *other);
void R_SecplaneChangeHeight(secplane_t *secplane, fixed_t hdiff);
fixed_t R_SecplaneHeightDiff(secplane_t *secplane, fixed_t oldd);
fixed_t R_SecplanePointToDist(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z);
fixed_t R_SecplanePointToDist2(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z);
fixed_t R_ScaleFromGlobalAngle(angle_t visangle); fixed_t R_ScaleFromGlobalAngle(angle_t visangle);
subsector_t *R_PointInSubsector(fixed_t x, fixed_t y); subsector_t *R_PointInSubsector(fixed_t x, fixed_t y);
subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y); subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y);

View file

@ -431,6 +431,9 @@ static visplane_t *new_visplane(unsigned hash)
visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel,
fixed_t xoff, fixed_t yoff, angle_t plangle, extracolormap_t *planecolormap, fixed_t xoff, fixed_t yoff, angle_t plangle, extracolormap_t *planecolormap,
ffloor_t *pfloor ffloor_t *pfloor
#ifdef POLYOBJECTS_PLANES
, polyobj_t *polyobj
#endif
#ifdef ESLOPE #ifdef ESLOPE
, pslope_t *slope , pslope_t *slope
#endif #endif
@ -470,6 +473,8 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel,
#ifdef POLYOBJECTS_PLANES #ifdef POLYOBJECTS_PLANES
if (check->polyobj && pfloor) if (check->polyobj && pfloor)
continue; continue;
if (polyobj != check->polyobj)
continue;
#endif #endif
if (height == check->height && picnum == check->picnum if (height == check->height && picnum == check->picnum
&& lightlevel == check->lightlevel && lightlevel == check->lightlevel
@ -504,7 +509,7 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel,
check->viewangle = viewangle; check->viewangle = viewangle;
check->plangle = plangle; check->plangle = plangle;
#ifdef POLYOBJECTS_PLANES #ifdef POLYOBJECTS_PLANES
check->polyobj = NULL; check->polyobj = polyobj;
#endif #endif
#ifdef ESLOPE #ifdef ESLOPE
check->slope = slope; check->slope = slope;
@ -720,7 +725,11 @@ void R_DrawPlanes(void)
continue; continue;
} }
if (pl->ffloor != NULL) if (pl->ffloor != NULL
#ifdef POLYOBJECTS_PLANES
|| pl->polyobj != NULL
#endif
)
continue; continue;
R_DrawSinglePlane(pl); R_DrawSinglePlane(pl);

View file

@ -97,6 +97,9 @@ void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2);
void R_DrawPlanes(void); void R_DrawPlanes(void);
visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t xoff, fixed_t yoff, angle_t plangle, visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t xoff, fixed_t yoff, angle_t plangle,
extracolormap_t *planecolormap, ffloor_t *ffloor extracolormap_t *planecolormap, ffloor_t *ffloor
#ifdef POLYOBJECTS_PLANES
, polyobj_t *polyobj
#endif
#ifdef ESLOPE #ifdef ESLOPE
, pslope_t *slope , pslope_t *slope
#endif #endif

View file

@ -271,11 +271,20 @@ static void R_Render2sidedMultiPatchColumn(column_t *column)
if (colfunc == wallcolfunc) if (colfunc == wallcolfunc)
twosmultipatchfunc(); twosmultipatchfunc();
else if (colfunc == fuzzcolfunc)
twosmultipatchtransfunc();
else else
colfunc(); colfunc();
} }
} }
// quick wrapper for R_DrawFlippedMaskedColumn so it can be set as a colfunc_2s value
// uses column2s_length for texture->height as above
static void R_DrawFlippedMaskedSegColumn(column_t *column)
{
R_DrawFlippedMaskedColumn(column, column2s_length);
}
void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
{ {
size_t pindex; size_t pindex;
@ -300,7 +309,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
curline = ds->curline; curline = ds->curline;
frontsector = curline->frontsector; frontsector = curline->frontsector;
backsector = curline->backsector; backsector = curline->backsector;
texnum = texturetranslation[curline->sidedef->midtexture]; texnum = R_GetTextureNum(curline->sidedef->midtexture);
windowbottom = windowtop = sprbotscreen = INT32_MAX; windowbottom = windowtop = sprbotscreen = INT32_MAX;
// hack translucent linedef types (900-909 for transtables 1-9) // hack translucent linedef types (900-909 for transtables 1-9)
@ -344,10 +353,21 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
rw_scalestep = ds->scalestep; rw_scalestep = ds->scalestep;
spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep;
// Texture must be cached before setting colfunc_2s,
// otherwise texture[texnum]->holes may be false when it shouldn't be
R_CheckTextureCache(texnum);
// handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures // handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures
// are not stored per-column with post info in SRB2 // are not stored per-column with post info in SRB2
if (textures[texnum]->holes) if (textures[texnum]->holes)
colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture {
if (textures[texnum]->flip & 2) // vertically flipped?
{
colfunc_2s = R_DrawFlippedMaskedSegColumn;
column2s_length = textures[texnum]->height;
}
else
colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture
}
else else
{ {
colfunc_2s = R_Render2sidedMultiPatchColumn; // render multipatch with no holes (no post_t info) colfunc_2s = R_Render2sidedMultiPatchColumn; // render multipatch with no holes (no post_t info)
@ -391,6 +411,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
rlight->height = (centeryfrac) - FixedMul((light->height - viewz), spryscale); rlight->height = (centeryfrac) - FixedMul((light->height - viewz), spryscale);
rlight->heightstep = -FixedMul(rw_scalestep, (light->height - viewz)); rlight->heightstep = -FixedMul(rw_scalestep, (light->height - viewz));
#endif #endif
rlight->startheight = rlight->height; // keep starting value here to reset for each repeat
rlight->lightlevel = *light->lightlevel; rlight->lightlevel = *light->lightlevel;
rlight->extra_colormap = light->extra_colormap; rlight->extra_colormap = light->extra_colormap;
rlight->flags = light->flags; rlight->flags = light->flags;
@ -484,6 +505,14 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
{ {
rw_scalestep = ds->scalestep; rw_scalestep = ds->scalestep;
spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep;
if (dc_numlights)
{ // reset all lights to their starting heights
for (i = 0; i < dc_numlights; i++)
{
rlight = &dc_lightlist[i];
rlight->height = rlight->startheight;
}
}
} }
#ifndef ESLOPE #ifndef ESLOPE
@ -700,6 +729,14 @@ static void R_DrawRepeatMaskedColumn(column_t *col)
} while (sprtopscreen < sprbotscreen); } while (sprtopscreen < sprbotscreen);
} }
static void R_DrawRepeatFlippedMaskedColumn(column_t *col)
{
do {
R_DrawFlippedMaskedColumn(col, column2s_length);
sprtopscreen += dc_texheight*spryscale;
} while (sprtopscreen < sprbotscreen);
}
// //
// R_RenderThickSideRange // R_RenderThickSideRange
// Renders all the thick sides in the given range. // Renders all the thick sides in the given range.
@ -746,7 +783,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
curline = ds->curline; curline = ds->curline;
backsector = pfloor->target; backsector = pfloor->target;
frontsector = curline->frontsector == pfloor->target ? curline->backsector : curline->frontsector; frontsector = curline->frontsector == pfloor->target ? curline->backsector : curline->frontsector;
texnum = texturetranslation[sides[pfloor->master->sidenum[0]].midtexture]; texnum = R_GetTextureNum(sides[pfloor->master->sidenum[0]].midtexture);
colfunc = wallcolfunc; colfunc = wallcolfunc;
@ -754,7 +791,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
{ {
size_t linenum = curline->linedef-backsector->lines[0]; size_t linenum = curline->linedef-backsector->lines[0];
newline = pfloor->master->frontsector->lines[0] + linenum; newline = pfloor->master->frontsector->lines[0] + linenum;
texnum = texturetranslation[sides[newline->sidenum[0]].midtexture]; texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
} }
if (pfloor->flags & FF_TRANSLUCENT) if (pfloor->flags & FF_TRANSLUCENT)
@ -1024,10 +1061,21 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
dc_texturemid += offsetvalue; dc_texturemid += offsetvalue;
// Texture must be cached before setting colfunc_2s,
// otherwise texture[texnum]->holes may be false when it shouldn't be
R_CheckTextureCache(texnum);
//faB: handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures //faB: handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures
// are not stored per-column with post info anymore in Doom Legacy // are not stored per-column with post info anymore in Doom Legacy
if (textures[texnum]->holes) if (textures[texnum]->holes)
colfunc_2s = R_DrawRepeatMaskedColumn; //render the usual 2sided single-patch packed texture {
if (textures[texnum]->flip & 2) // vertically flipped?
{
colfunc_2s = R_DrawRepeatFlippedMaskedColumn;
column2s_length = textures[texnum]->height;
}
else
colfunc_2s = R_DrawRepeatMaskedColumn; // render the usual 2sided single-patch packed texture
}
else else
{ {
colfunc_2s = R_Render2sidedMultiPatchColumn; //render multipatch with no holes (no post_t info) colfunc_2s = R_Render2sidedMultiPatchColumn; //render multipatch with no holes (no post_t info)
@ -1936,14 +1984,16 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (!backsector) if (!backsector)
{ {
fixed_t texheight;
// single sided line // single sided line
midtexture = texturetranslation[sidedef->midtexture]; midtexture = R_GetTextureNum(sidedef->midtexture);
texheight = textureheight[midtexture];
// a single sided line is terminal, so it must mark ends // a single sided line is terminal, so it must mark ends
markfloor = markceiling = true; markfloor = markceiling = true;
#ifdef ESLOPE #ifdef ESLOPE
if (linedef->flags & ML_EFFECT2) { if (linedef->flags & ML_EFFECT2) {
if (linedef->flags & ML_DONTPEGBOTTOM) if (linedef->flags & ML_DONTPEGBOTTOM)
rw_midtexturemid = frontsector->floorheight + textureheight[sidedef->midtexture] - viewz; rw_midtexturemid = frontsector->floorheight + texheight - viewz;
else else
rw_midtexturemid = frontsector->ceilingheight - viewz; rw_midtexturemid = frontsector->ceilingheight - viewz;
} }
@ -1952,10 +2002,10 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (linedef->flags & ML_DONTPEGBOTTOM) if (linedef->flags & ML_DONTPEGBOTTOM)
{ {
#ifdef ESLOPE #ifdef ESLOPE
rw_midtexturemid = worldbottom + textureheight[sidedef->midtexture]; rw_midtexturemid = worldbottom + texheight;
rw_midtextureslide = floorfrontslide; rw_midtextureslide = floorfrontslide;
#else #else
vtop = frontsector->floorheight + textureheight[sidedef->midtexture]; vtop = frontsector->floorheight + texheight;
// bottom of texture at bottom // bottom of texture at bottom
rw_midtexturemid = vtop - viewz; rw_midtexturemid = vtop - viewz;
#endif #endif
@ -2187,76 +2237,50 @@ void R_StoreWallRange(INT32 start, INT32 stop)
#endif #endif
) )
{ {
fixed_t texheight;
// top texture // top texture
if ((linedef->flags & (ML_DONTPEGTOP) && (linedef->flags & ML_DONTPEGBOTTOM)) if ((linedef->flags & (ML_DONTPEGTOP) && (linedef->flags & ML_DONTPEGBOTTOM))
&& linedef->sidenum[1] != 0xffff) && linedef->sidenum[1] != 0xffff)
{ {
// Special case... use offsets from 2nd side but only if it has a texture. // Special case... use offsets from 2nd side but only if it has a texture.
side_t *def = &sides[linedef->sidenum[1]]; side_t *def = &sides[linedef->sidenum[1]];
toptexture = texturetranslation[def->toptexture]; toptexture = R_GetTextureNum(def->toptexture);
if (!toptexture) //Second side has no texture, use the first side's instead. if (!toptexture) //Second side has no texture, use the first side's instead.
toptexture = texturetranslation[sidedef->toptexture]; toptexture = R_GetTextureNum(sidedef->toptexture);
texheight = textureheight[toptexture];
#ifdef ESLOPE
if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
if (linedef->flags & ML_DONTPEGTOP)
rw_toptexturemid = frontsector->ceilingheight - viewz;
else
rw_toptexturemid = backsector->ceilingheight - viewz;
} else
#endif
if (linedef->flags & ML_DONTPEGTOP)
{
// top of texture at top
rw_toptexturemid = worldtop;
#ifdef ESLOPE
rw_toptextureslide = ceilingfrontslide;
#endif
}
else
{
#ifdef ESLOPE
rw_toptexturemid = worldhigh + textureheight[def->toptexture];
rw_toptextureslide = ceilingbackslide;
#else
vtop = backsector->ceilingheight + textureheight[def->toptexture];
// bottom of texture
rw_toptexturemid = vtop - viewz;
#endif
}
} }
else else
{ {
toptexture = texturetranslation[sidedef->toptexture]; toptexture = R_GetTextureNum(sidedef->toptexture);
texheight = textureheight[toptexture];
}
#ifdef ESLOPE #ifdef ESLOPE
if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
if (linedef->flags & ML_DONTPEGTOP)
rw_toptexturemid = frontsector->ceilingheight - viewz;
else
rw_toptexturemid = backsector->ceilingheight - viewz;
} else
#endif
if (linedef->flags & ML_DONTPEGTOP) if (linedef->flags & ML_DONTPEGTOP)
{ rw_toptexturemid = frontsector->ceilingheight - viewz;
// top of texture at top
rw_toptexturemid = worldtop;
#ifdef ESLOPE
rw_toptextureslide = ceilingfrontslide;
#endif
}
else else
{ rw_toptexturemid = backsector->ceilingheight - viewz;
#ifdef ESLOPE } else
rw_toptexturemid = worldhigh + textureheight[sidedef->toptexture]; #endif
rw_toptextureslide = ceilingbackslide; if (linedef->flags & ML_DONTPEGTOP)
#else {
vtop = backsector->ceilingheight + textureheight[sidedef->toptexture]; // top of texture at top
// bottom of texture rw_toptexturemid = worldtop;
rw_toptexturemid = vtop - viewz; #ifdef ESLOPE
rw_toptextureslide = ceilingfrontslide;
#endif
}
else
{
#ifdef ESLOPE
rw_toptexturemid = worldhigh + texheight;
rw_toptextureslide = ceilingbackslide;
#else
vtop = backsector->ceilingheight + texheight;
// bottom of texture
rw_toptexturemid = vtop - viewz;
#endif #endif
}
} }
} }
// check BOTTOM TEXTURE // check BOTTOM TEXTURE
@ -2267,7 +2291,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
) //seulement si VISIBLE!!! ) //seulement si VISIBLE!!!
{ {
// bottom texture // bottom texture
bottomtexture = texturetranslation[sidedef->bottomtexture]; bottomtexture = R_GetTextureNum(sidedef->bottomtexture);
#ifdef ESLOPE #ifdef ESLOPE
if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
@ -2552,7 +2576,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
ds_p->numthicksides = numthicksides = i; ds_p->numthicksides = numthicksides = i;
} }
if (sidedef->midtexture) if (sidedef->midtexture > 0 && sidedef->midtexture < numtextures)
{ {
// masked midtexture // masked midtexture
if (!ds_p->thicksidecol) if (!ds_p->thicksidecol)
@ -3164,12 +3188,12 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (maskedtexture && !(ds_p->silhouette & SIL_TOP)) if (maskedtexture && !(ds_p->silhouette & SIL_TOP))
{ {
ds_p->silhouette |= SIL_TOP; ds_p->silhouette |= SIL_TOP;
ds_p->tsilheight = sidedef->midtexture ? INT32_MIN: INT32_MAX; ds_p->tsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MIN: INT32_MAX;
} }
if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM)) if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM))
{ {
ds_p->silhouette |= SIL_BOTTOM; ds_p->silhouette |= SIL_BOTTOM;
ds_p->bsilheight = sidedef->midtexture ? INT32_MAX: INT32_MIN; ds_p->bsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MAX: INT32_MIN;
} }
ds_p++; ds_p++;
} }

View file

@ -712,7 +712,7 @@ void R_DrawMaskedColumn(column_t *column)
dc_texturemid = basetexturemid; dc_texturemid = basetexturemid;
} }
static void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight) void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
{ {
INT32 topscreen; INT32 topscreen;
INT32 bottomscreen; INT32 bottomscreen;
@ -1349,7 +1349,7 @@ static void R_ProjectSprite(mobj_t *thing)
if (sortscale < linkscale) if (sortscale < linkscale)
dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0) dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0)
sortscale = linkscale; // now make sure it's linked sortscale = linkscale; // now make sure it's linked
} }
// PORTAL SPRITE CLIPPING // PORTAL SPRITE CLIPPING
@ -1885,21 +1885,25 @@ static void R_CreateDrawNodes(void)
entry->ffloor = ds->thicksides[i]; entry->ffloor = ds->thicksides[i];
} }
} }
#ifdef POLYOBJECTS_PLANES
// Check for a polyobject plane, but only if this is a front line
if (ds->curline->polyseg && ds->curline->polyseg->visplane && !ds->curline->side) {
plane = ds->curline->polyseg->visplane;
R_PlaneBounds(plane);
if (plane->low < con_clipviewtop || plane->high > vid.height || plane->high > plane->low)
;
else {
// Put it in!
entry = R_CreateDrawNode(&nodehead);
entry->plane = plane;
entry->seg = ds;
}
ds->curline->polyseg->visplane = NULL;
}
#endif
if (ds->maskedtexturecol) if (ds->maskedtexturecol)
{ {
#ifdef POLYOBJECTS_PLANES
// Check for a polyobject plane, but only if this is a front line
if (ds->curline->polyseg && ds->curline->polyseg->visplane && !ds->curline->side) {
// Put it in!
entry = R_CreateDrawNode(&nodehead);
entry->plane = ds->curline->polyseg->visplane;
entry->seg = ds;
ds->curline->polyseg->visplane->polyobj = ds->curline->polyseg;
ds->curline->polyseg->visplane = NULL;
}
#endif
entry = R_CreateDrawNode(&nodehead); entry = R_CreateDrawNode(&nodehead);
entry->seg = ds; entry->seg = ds;
} }
@ -1942,6 +1946,29 @@ static void R_CreateDrawNodes(void)
} }
} }
#ifdef POLYOBJECTS_PLANES
// find all the remaining polyobject planes and add them on the end of the list
// probably this is a terrible idea if we wanted them to be sorted properly
// but it works getting them in for now
for (i = 0; i < numPolyObjects; i++)
{
if (!PolyObjects[i].visplane)
continue;
plane = PolyObjects[i].visplane;
R_PlaneBounds(plane);
if (plane->low < con_clipviewtop || plane->high > vid.height || plane->high > plane->low)
{
PolyObjects[i].visplane = NULL;
continue;
}
entry = R_CreateDrawNode(&nodehead);
entry->plane = plane;
// note: no seg is set, for what should be obvious reasons
PolyObjects[i].visplane = NULL;
}
#endif
if (visspritecount == 0) if (visspritecount == 0)
return; return;
@ -1998,13 +2025,16 @@ static void R_CreateDrawNodes(void)
if (x1 < r2->plane->minx) x1 = r2->plane->minx; if (x1 < r2->plane->minx) x1 = r2->plane->minx;
if (x2 > r2->plane->maxx) x2 = r2->plane->maxx; if (x2 > r2->plane->maxx) x2 = r2->plane->maxx;
for (i = x1; i <= x2; i++) if (r2->seg) // if no seg set, assume the whole thing is in front or something stupid
{ {
if (r2->seg->frontscale[i] > rover->sortscale) for (i = x1; i <= x2; i++)
break; {
if (r2->seg->frontscale[i] > rover->sortscale)
break;
}
if (i > x2)
continue;
} }
if (i > x2)
continue;
entry = R_CreateDrawNode(NULL); entry = R_CreateDrawNode(NULL);
(entry->prev = r2->prev)->next = entry; (entry->prev = r2->prev)->next = entry;
@ -2880,6 +2910,7 @@ void R_AddSkins(UINT16 wadnum)
GETFLAG(STOMPDAMAGE) GETFLAG(STOMPDAMAGE)
GETFLAG(MARIODAMAGE) GETFLAG(MARIODAMAGE)
GETFLAG(MACHINE) GETFLAG(MACHINE)
GETFLAG(NOSPINDASHDUST)
#undef GETFLAG #undef GETFLAG
else // let's check if it's a sound, otherwise error out else // let's check if it's a sound, otherwise error out

View file

@ -46,6 +46,7 @@ extern fixed_t windowtop;
extern fixed_t windowbottom; 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_SortVisSprites(void); void R_SortVisSprites(void);
//faB: find sprites in wadfile, replace existing, add new ones //faB: find sprites in wadfile, replace existing, add new ones

View file

@ -607,6 +607,13 @@ void S_StartSound(const void *origin, sfxenum_t sfx_id)
sfx_id = sfx_mario6; sfx_id = sfx_mario6;
break; break;
case sfx_shield: case sfx_shield:
case sfx_wirlsg:
case sfx_forcsg:
case sfx_elemsg:
case sfx_armasg:
case sfx_s3k3e:
case sfx_s3k3f:
case sfx_s3k41:
sfx_id = sfx_mario3; sfx_id = sfx_mario3;
break; break;
case sfx_itemup: case sfx_itemup:

View file

@ -30,7 +30,7 @@
#include "f_finale.h" #include "f_finale.h"
#if defined (USEASM) //&& (!defined (_MSC_VER) || (_MSC_VER <= 1200)) #if defined (USEASM) && !defined (NORUSEASM)//&& (!defined (_MSC_VER) || (_MSC_VER <= 1200))
#define RUSEASM //MSC.NET can't patch itself #define RUSEASM //MSC.NET can't patch itself
#endif #endif
@ -49,6 +49,7 @@ void (*splatfunc)(void); // span drawer w/ transparency
void (*basespanfunc)(void); // default span func for color mode void (*basespanfunc)(void); // default span func for color mode
void (*transtransfunc)(void); // translucent translated column drawer void (*transtransfunc)(void); // translucent translated column drawer
void (*twosmultipatchfunc)(void); // for cols with transparent pixels void (*twosmultipatchfunc)(void); // for cols with transparent pixels
void (*twosmultipatchtransfunc)(void); // for cols with transparent pixels AND translucency
// ------------------ // ------------------
// global video state // global video state
@ -127,6 +128,7 @@ void SCR_SetMode(void)
fuzzcolfunc = R_DrawTranslucentColumn_8; fuzzcolfunc = R_DrawTranslucentColumn_8;
walldrawerfunc = R_DrawWallColumn_8; walldrawerfunc = R_DrawWallColumn_8;
twosmultipatchfunc = R_Draw2sMultiPatchColumn_8; twosmultipatchfunc = R_Draw2sMultiPatchColumn_8;
twosmultipatchtransfunc = R_Draw2sMultiPatchTranslucentColumn_8;
#ifdef RUSEASM #ifdef RUSEASM
if (R_ASM) if (R_ASM)
{ {

View file

@ -136,6 +136,7 @@ extern void (*basespanfunc)(void);
extern void (*splatfunc)(void); extern void (*splatfunc)(void);
extern void (*transtransfunc)(void); extern void (*transtransfunc)(void);
extern void (*twosmultipatchfunc)(void); extern void (*twosmultipatchfunc)(void);
extern void (*twosmultipatchtransfunc)(void);
// ----- // -----
// CPUID // CPUID

View file

@ -295,6 +295,7 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\i_tcp.c" /> <ClCompile Include="..\i_tcp.c" />
<ClCompile Include="..\lua_baselib.c" /> <ClCompile Include="..\lua_baselib.c" />
<ClCompile Include="..\lua_blockmaplib.c" />
<ClCompile Include="..\lua_consolelib.c" /> <ClCompile Include="..\lua_consolelib.c" />
<ClCompile Include="..\lua_hooklib.c" /> <ClCompile Include="..\lua_hooklib.c" />
<ClCompile Include="..\lua_hudlib.c" /> <ClCompile Include="..\lua_hudlib.c" />

View file

@ -633,6 +633,9 @@
<ClCompile Include="..\lua_baselib.c"> <ClCompile Include="..\lua_baselib.c">
<Filter>LUA</Filter> <Filter>LUA</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\lua_blockmaplib.c">
<Filter>LUA</Filter>
</ClCompile>
<ClCompile Include="..\lua_consolelib.c"> <ClCompile Include="..\lua_consolelib.c">
<Filter>LUA</Filter> <Filter>LUA</Filter>
</ClCompile> </ClCompile>

View file

@ -2647,6 +2647,47 @@ INT32 I_PutEnv(char *variable)
#endif #endif
} }
INT32 I_ClipboardCopy(const char *data, size_t size)
{
char storage[256];
if (size > 255)
size = 255;
memcpy(storage, data, size);
storage[size] = 0;
if (SDL_SetClipboardText(storage))
return 0;
return -1;
}
const char *I_ClipboardPaste(void)
{
static char clipboard_modified[256];
char *clipboard_contents, *i = clipboard_modified;
if (!SDL_HasClipboardText())
return NULL;
clipboard_contents = SDL_GetClipboardText();
memcpy(clipboard_modified, clipboard_contents, 255);
SDL_free(clipboard_contents);
clipboard_modified[255] = 0;
while (*i)
{
if (*i == '\n' || *i == '\r')
{ // End on newline
*i = 0;
break;
}
else if (*i == '\t')
*i = ' '; // Tabs become spaces
else if (*i < 32 || (unsigned)*i > 127)
*i = '?'; // Nonprintable chars become question marks
++i;
}
return (const char *)&clipboard_modified;
}
/** \brief The isWadPathOk function /** \brief The isWadPathOk function
\param path string path to check \param path string path to check

View file

@ -33,14 +33,6 @@
#pragma warning(default : 4214 4244) #pragma warning(default : 4214 4244)
#endif #endif
#if SDL_VERSION_ATLEAST(1,3,0)
#define SDLK_EQUALS SDLK_KP_EQUALSAS400
#define SDLK_LMETA SDLK_LGUI
#define SDLK_RMETA SDLK_RGUI
#else
#define HAVE_SDLMETAKEYS
#endif
#ifdef HAVE_TTF #ifdef HAVE_TTF
#include "i_ttf.h" #include "i_ttf.h"
#endif #endif
@ -189,14 +181,14 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
wasfullscreen = SDL_TRUE; wasfullscreen = SDL_TRUE;
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
} }
else if (!fullscreen && wasfullscreen) else if (wasfullscreen)
{ {
wasfullscreen = SDL_FALSE; wasfullscreen = SDL_FALSE;
SDL_SetWindowFullscreen(window, 0); SDL_SetWindowFullscreen(window, 0);
SDL_SetWindowSize(window, width, height); SDL_SetWindowSize(window, width, height);
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(1), SDL_WINDOWPOS_CENTERED_DISPLAY(1)); SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(1), SDL_WINDOWPOS_CENTERED_DISPLAY(1));
} }
else if (!wasfullscreen) else
{ {
// Reposition window only in windowed mode // Reposition window only in windowed mode
SDL_SetWindowSize(window, width, height); SDL_SetWindowSize(window, width, height);
@ -282,129 +274,70 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code)
} }
switch (code) switch (code)
{ {
case SDL_SCANCODE_F11: // F11 and F12 are // F11 and F12 are separated from the rest of the function keys
return KEY_F11; // separated from the case SDL_SCANCODE_F11: return KEY_F11;
case SDL_SCANCODE_F12: // rest of the function case SDL_SCANCODE_F12: return KEY_F12;
return KEY_F12; // keys
case SDL_SCANCODE_KP_0: case SDL_SCANCODE_KP_0: return KEY_KEYPAD0;
return KEY_KEYPAD0; case SDL_SCANCODE_KP_1: return KEY_KEYPAD1;
case SDL_SCANCODE_KP_1: case SDL_SCANCODE_KP_2: return KEY_KEYPAD2;
return KEY_KEYPAD1; case SDL_SCANCODE_KP_3: return KEY_KEYPAD3;
case SDL_SCANCODE_KP_2: case SDL_SCANCODE_KP_4: return KEY_KEYPAD4;
return KEY_KEYPAD2; case SDL_SCANCODE_KP_5: return KEY_KEYPAD5;
case SDL_SCANCODE_KP_3: case SDL_SCANCODE_KP_6: return KEY_KEYPAD6;
return KEY_KEYPAD3; case SDL_SCANCODE_KP_7: return KEY_KEYPAD7;
case SDL_SCANCODE_KP_4: case SDL_SCANCODE_KP_8: return KEY_KEYPAD8;
return KEY_KEYPAD4; case SDL_SCANCODE_KP_9: return KEY_KEYPAD9;
case SDL_SCANCODE_KP_5:
return KEY_KEYPAD5;
case SDL_SCANCODE_KP_6:
return KEY_KEYPAD6;
case SDL_SCANCODE_KP_7:
return KEY_KEYPAD7;
case SDL_SCANCODE_KP_8:
return KEY_KEYPAD8;
case SDL_SCANCODE_KP_9:
return KEY_KEYPAD9;
case SDL_SCANCODE_RETURN: case SDL_SCANCODE_RETURN: return KEY_ENTER;
return KEY_ENTER; case SDL_SCANCODE_ESCAPE: return KEY_ESCAPE;
case SDL_SCANCODE_ESCAPE: case SDL_SCANCODE_BACKSPACE: return KEY_BACKSPACE;
return KEY_ESCAPE; case SDL_SCANCODE_TAB: return KEY_TAB;
case SDL_SCANCODE_BACKSPACE: case SDL_SCANCODE_SPACE: return KEY_SPACE;
return KEY_BACKSPACE; case SDL_SCANCODE_MINUS: return KEY_MINUS;
case SDL_SCANCODE_TAB: case SDL_SCANCODE_EQUALS: return KEY_EQUALS;
return KEY_TAB; case SDL_SCANCODE_LEFTBRACKET: return '[';
case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RIGHTBRACKET: return ']';
return KEY_SPACE; case SDL_SCANCODE_BACKSLASH: return '\\';
case SDL_SCANCODE_MINUS: case SDL_SCANCODE_NONUSHASH: return '#';
return KEY_MINUS; case SDL_SCANCODE_SEMICOLON: return ';';
case SDL_SCANCODE_EQUALS: case SDL_SCANCODE_APOSTROPHE: return '\'';
return KEY_EQUALS; case SDL_SCANCODE_GRAVE: return '`';
case SDL_SCANCODE_LEFTBRACKET: case SDL_SCANCODE_COMMA: return ',';
return '['; case SDL_SCANCODE_PERIOD: return '.';
case SDL_SCANCODE_RIGHTBRACKET: case SDL_SCANCODE_SLASH: return '/';
return ']'; case SDL_SCANCODE_CAPSLOCK: return KEY_CAPSLOCK;
case SDL_SCANCODE_BACKSLASH: case SDL_SCANCODE_PRINTSCREEN: return 0; // undefined?
return '\\'; case SDL_SCANCODE_SCROLLLOCK: return KEY_SCROLLLOCK;
case SDL_SCANCODE_NONUSHASH: case SDL_SCANCODE_PAUSE: return KEY_PAUSE;
return '#'; case SDL_SCANCODE_INSERT: return KEY_INS;
case SDL_SCANCODE_SEMICOLON: case SDL_SCANCODE_HOME: return KEY_HOME;
return ';'; case SDL_SCANCODE_PAGEUP: return KEY_PGUP;
case SDL_SCANCODE_APOSTROPHE: case SDL_SCANCODE_DELETE: return KEY_DEL;
return '\''; case SDL_SCANCODE_END: return KEY_END;
case SDL_SCANCODE_GRAVE: case SDL_SCANCODE_PAGEDOWN: return KEY_PGDN;
return '`'; case SDL_SCANCODE_RIGHT: return KEY_RIGHTARROW;
case SDL_SCANCODE_COMMA: case SDL_SCANCODE_LEFT: return KEY_LEFTARROW;
return ','; case SDL_SCANCODE_DOWN: return KEY_DOWNARROW;
case SDL_SCANCODE_PERIOD: case SDL_SCANCODE_UP: return KEY_UPARROW;
return '.'; case SDL_SCANCODE_NUMLOCKCLEAR: return KEY_NUMLOCK;
case SDL_SCANCODE_SLASH: case SDL_SCANCODE_KP_DIVIDE: return KEY_KPADSLASH;
return '/'; case SDL_SCANCODE_KP_MULTIPLY: return '*'; // undefined?
case SDL_SCANCODE_CAPSLOCK: case SDL_SCANCODE_KP_MINUS: return KEY_MINUSPAD;
return KEY_CAPSLOCK; case SDL_SCANCODE_KP_PLUS: return KEY_PLUSPAD;
case SDL_SCANCODE_PRINTSCREEN: case SDL_SCANCODE_KP_ENTER: return KEY_ENTER;
return 0; // undefined? case SDL_SCANCODE_KP_PERIOD: return KEY_KPADDEL;
case SDL_SCANCODE_SCROLLLOCK: case SDL_SCANCODE_NONUSBACKSLASH: return '\\';
return KEY_SCROLLLOCK;
case SDL_SCANCODE_PAUSE:
return KEY_PAUSE;
case SDL_SCANCODE_INSERT:
return KEY_INS;
case SDL_SCANCODE_HOME:
return KEY_HOME;
case SDL_SCANCODE_PAGEUP:
return KEY_PGUP;
case SDL_SCANCODE_DELETE:
return KEY_DEL;
case SDL_SCANCODE_END:
return KEY_END;
case SDL_SCANCODE_PAGEDOWN:
return KEY_PGDN;
case SDL_SCANCODE_RIGHT:
return KEY_RIGHTARROW;
case SDL_SCANCODE_LEFT:
return KEY_LEFTARROW;
case SDL_SCANCODE_DOWN:
return KEY_DOWNARROW;
case SDL_SCANCODE_UP:
return KEY_UPARROW;
case SDL_SCANCODE_NUMLOCKCLEAR:
return KEY_NUMLOCK;
case SDL_SCANCODE_KP_DIVIDE:
return KEY_KPADSLASH;
case SDL_SCANCODE_KP_MULTIPLY:
return '*'; // undefined?
case SDL_SCANCODE_KP_MINUS:
return KEY_MINUSPAD;
case SDL_SCANCODE_KP_PLUS:
return KEY_PLUSPAD;
case SDL_SCANCODE_KP_ENTER:
return KEY_ENTER;
case SDL_SCANCODE_KP_PERIOD:
return KEY_KPADDEL;
case SDL_SCANCODE_NONUSBACKSLASH:
return '\\';
case SDL_SCANCODE_LSHIFT: case SDL_SCANCODE_LSHIFT: return KEY_LSHIFT;
return KEY_LSHIFT; case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT;
case SDL_SCANCODE_RSHIFT: case SDL_SCANCODE_LCTRL: return KEY_LCTRL;
return KEY_RSHIFT; case SDL_SCANCODE_RCTRL: return KEY_RCTRL;
case SDL_SCANCODE_LCTRL: case SDL_SCANCODE_LALT: return KEY_LALT;
return KEY_LCTRL; case SDL_SCANCODE_RALT: return KEY_RALT;
case SDL_SCANCODE_RCTRL: case SDL_SCANCODE_LGUI: return KEY_LEFTWIN;
return KEY_RCTRL; case SDL_SCANCODE_RGUI: return KEY_RIGHTWIN;
case SDL_SCANCODE_LALT: default: break;
return KEY_LALT;
case SDL_SCANCODE_RALT:
return KEY_RALT;
case SDL_SCANCODE_LGUI:
return KEY_LEFTWIN;
case SDL_SCANCODE_RGUI:
return KEY_RIGHTWIN;
default:
break;
} }
#ifdef HWRENDER #ifdef HWRENDER
DBG_Printf("Unknown incoming scancode: %d, represented %c\n", DBG_Printf("Unknown incoming scancode: %d, represented %c\n",
@ -432,15 +365,10 @@ static void VID_Command_NumModes_f (void)
CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes()); CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes());
} }
// SDL2 doesn't have SDL_GetVideoSurface or a lot of the SDL_Surface flags that SDL 1.2 had
static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText) static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
{ {
#if 1
(void)infoSurface;
(void)SurfaceText;
SDL2STUB();
#else
INT32 vfBPP; INT32 vfBPP;
const SDL_Surface *VidSur = SDL_GetVideoSurface();
if (!infoSurface) if (!infoSurface)
return; return;
@ -453,49 +381,12 @@ static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
CONS_Printf("\x82" "%s\n", SurfaceText); CONS_Printf("\x82" "%s\n", SurfaceText);
CONS_Printf(M_GetText(" %ix%i at %i bit color\n"), infoSurface->w, infoSurface->h, vfBPP); CONS_Printf(M_GetText(" %ix%i at %i bit color\n"), infoSurface->w, infoSurface->h, vfBPP);
if (infoSurface->flags&SDL_HWSURFACE) if (infoSurface->flags&SDL_PREALLOC)
CONS_Printf("%s", M_GetText(" Stored in video memory\n"));
else if (infoSurface->flags&SDL_OPENGL)
CONS_Printf("%s", M_GetText(" Stored in an OpenGL context\n"));
else if (infoSurface->flags&SDL_PREALLOC)
CONS_Printf("%s", M_GetText(" Uses preallocated memory\n")); CONS_Printf("%s", M_GetText(" Uses preallocated memory\n"));
else else
CONS_Printf("%s", M_GetText(" Stored in system memory\n")); CONS_Printf("%s", M_GetText(" Stored in system memory\n"));
if (infoSurface->flags&SDL_ASYNCBLIT)
CONS_Printf("%s", M_GetText(" Uses asynchronous blits if possible\n"));
else
CONS_Printf("%s", M_GetText(" Uses synchronous blits if possible\n"));
if (infoSurface->flags&SDL_ANYFORMAT)
CONS_Printf("%s", M_GetText(" Allows any pixel-format\n"));
if (infoSurface->flags&SDL_HWPALETTE)
CONS_Printf("%s", M_GetText(" Has exclusive palette access\n"));
else if (VidSur == infoSurface)
CONS_Printf("%s", M_GetText(" Has nonexclusive palette access\n"));
if (infoSurface->flags&SDL_DOUBLEBUF)
CONS_Printf("%s", M_GetText(" Double buffered\n"));
else if (VidSur == infoSurface)
CONS_Printf("%s", M_GetText(" No hardware flipping\n"));
if (infoSurface->flags&SDL_FULLSCREEN)
CONS_Printf("%s", M_GetText(" Full screen\n"));
else if (infoSurface->flags&SDL_RESIZABLE)
CONS_Printf("%s", M_GetText(" Resizable window\n"));
else if (VidSur == infoSurface)
CONS_Printf("%s", M_GetText(" Nonresizable window\n"));
if (infoSurface->flags&SDL_HWACCEL)
CONS_Printf("%s", M_GetText(" Uses hardware acceleration blit\n"));
if (infoSurface->flags&SDL_SRCCOLORKEY)
CONS_Printf("%s", M_GetText(" Use colorkey blitting\n"));
if (infoSurface->flags&SDL_RLEACCEL) if (infoSurface->flags&SDL_RLEACCEL)
CONS_Printf("%s", M_GetText(" Colorkey RLE acceleration blit\n")); CONS_Printf("%s", M_GetText(" Colorkey RLE acceleration blit\n"));
if (infoSurface->flags&SDL_SRCALPHA)
CONS_Printf("%s", M_GetText(" Use alpha blending acceleration blit\n"));
#endif
} }
static void VID_Command_Info_f (void) static void VID_Command_Info_f (void)
@ -579,23 +470,6 @@ static void VID_Command_Mode_f (void)
setmodeneeded = modenum+1; // request vid mode change setmodeneeded = modenum+1; // request vid mode change
} }
#if 0
#if defined(RPC_NO_WINDOWS_H)
static VOID MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(hWnd);
UNREFERENCED_PARAMETER(message);
UNREFERENCED_PARAMETER(wParam);
switch (message)
{
case WM_SETTEXT:
COM_BufAddText((LPCSTR)lParam);
break;
}
}
#endif
#endif
static inline void SDLJoyRemap(event_t *event) static inline void SDLJoyRemap(event_t *event)
{ {
(void)event; (void)event;
@ -954,218 +828,6 @@ void I_GetEvent(void)
// In order to make wheels act like buttons, we have to set their state to Up. // In order to make wheels act like buttons, we have to set their state to Up.
// This is because wheel messages don't have an up/down state. // This is because wheel messages don't have an up/down state.
gamekeydown[KEY_MOUSEWHEELDOWN] = gamekeydown[KEY_MOUSEWHEELUP] = 0; gamekeydown[KEY_MOUSEWHEELDOWN] = gamekeydown[KEY_MOUSEWHEELUP] = 0;
#if 0
SDL_Event inputEvent;
static SDL_bool sdlquit = SDL_FALSE; //Alam: once, just once
event_t event;
if (!graphics_started)
return;
memset(&inputEvent, 0x00, sizeof(inputEvent));
while (SDL_PollEvent(&inputEvent))
{
memset(&event,0x00,sizeof (event_t));
switch (inputEvent.type)
{
case SDL_ACTIVEEVENT:
if (inputEvent.active.state & (SDL_APPACTIVE|SDL_APPINPUTFOCUS))
{
// pause music when alt-tab
if (inputEvent.active.gain /*&& !paused */)
{
static SDL_bool firsttimeonmouse = SDL_TRUE;
if (!firsttimeonmouse)
{
if (cv_usemouse.value) I_StartupMouse();
}
else firsttimeonmouse = SDL_FALSE;
//if (!netgame && !con_destlines) paused = false;
if (gamestate == GS_LEVEL)
if (!paused) I_ResumeSong(0); //resume it
}
else /*if (!paused)*/
{
if (!disable_mouse)
SDLforceUngrabMouse();
if (!netgame && gamestate == GS_LEVEL) paused = true;
memset(gamekeydown, 0, NUMKEYS);
//S_PauseSound();
if (gamestate == GS_LEVEL)
I_PauseSong(0); //pause it
}
}
if (MOUSE_MENU)
{
SDLdoUngrabMouse();
break;
}
if ((SDL_APPMOUSEFOCUS&inputEvent.active.state) && USE_MOUSEINPUT && inputEvent.active.gain)
HalfWarpMouse(realwidth, realheight);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
/// \todo inputEvent.key.which?
if (inputEvent.type == SDL_KEYUP)
event.type = ev_keyup;
else if (inputEvent.type == SDL_KEYDOWN)
event.type = ev_keydown;
else break;
event.data1 = SDLatekey(inputEvent.key.keysym.sym);
if (event.data1) D_PostEvent(&event);
break;
case SDL_MOUSEMOTION:
/// \todo inputEvent.motion.which
if (MOUSE_MENU)
{
SDLdoUngrabMouse();
break;
}
//if (USE_MOUSEINPUT) TODO SDL2 stub
{
// If the event is from warping the pointer back to middle
// of the screen then ignore it.
if ((inputEvent.motion.x == realwidth/2) &&
(inputEvent.motion.y == realheight/2))
{
break;
}
else
{
event.data2 = +inputEvent.motion.xrel;
event.data3 = -inputEvent.motion.yrel;
}
event.type = ev_mouse;
D_PostEvent(&event);
// Warp the pointer back to the middle of the window
// or we cannot move any further if it's at a border.
if ((inputEvent.motion.x < (realwidth/2 )-(realwidth/4 )) ||
(inputEvent.motion.y < (realheight/2)-(realheight/4)) ||
(inputEvent.motion.x > (realwidth/2 )+(realwidth/4 )) ||
(inputEvent.motion.y > (realheight/2)+(realheight/4) ) )
{
//if (SDL_GRAB_ON == SDL_WM_GrabInput(SDL_GRAB_QUERY) || !mousegrabok)
HalfWarpMouse(realwidth, realheight);
}
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
/// \todo inputEvent.button.which
if (USE_MOUSEINPUT)
{
if (inputEvent.type == SDL_MOUSEBUTTONUP)
event.type = ev_keyup;
else if (inputEvent.type == SDL_MOUSEBUTTONDOWN)
event.type = ev_keydown;
else break;
if (inputEvent.button.button==SDL_BUTTON_WHEELUP || inputEvent.button.button==SDL_BUTTON_WHEELDOWN)
{
if (inputEvent.type == SDL_MOUSEBUTTONUP)
event.data1 = 0; //Alam: dumb! this could be a real button with some mice
else
event.data1 = KEY_MOUSEWHEELUP + inputEvent.button.button - SDL_BUTTON_WHEELUP;
}
else if (inputEvent.button.button == SDL_BUTTON_MIDDLE)
event.data1 = KEY_MOUSE1+2;
else if (inputEvent.button.button == SDL_BUTTON_RIGHT)
event.data1 = KEY_MOUSE1+1;
else if (inputEvent.button.button <= MOUSEBUTTONS)
event.data1 = KEY_MOUSE1 + inputEvent.button.button - SDL_BUTTON_LEFT;
if (event.data1) D_PostEvent(&event);
}
break;
case SDL_JOYAXISMOTION:
inputEvent.jaxis.which++;
inputEvent.jaxis.axis++;
event.data1 = event.data2 = event.data3 = INT32_MAX;
if (cv_usejoystick.value == inputEvent.jaxis.which)
{
event.type = ev_joystick;
}
else if (cv_usejoystick.value == inputEvent.jaxis.which)
{
event.type = ev_joystick2;
}
else break;
//axis
if (inputEvent.jaxis.axis > JOYAXISSET*2)
break;
//vaule
if (inputEvent.jaxis.axis%2)
{
event.data1 = inputEvent.jaxis.axis / 2;
event.data2 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
}
else
{
inputEvent.jaxis.axis--;
event.data1 = inputEvent.jaxis.axis / 2;
event.data3 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
}
D_PostEvent(&event);
break;
case SDL_JOYBALLMOTION:
case SDL_JOYHATMOTION:
break; //NONE
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
inputEvent.jbutton.which++;
if (cv_usejoystick.value == inputEvent.jbutton.which)
event.data1 = KEY_JOY1;
else if (cv_usejoystick.value == inputEvent.jbutton.which)
event.data1 = KEY_2JOY1;
else break;
if (inputEvent.type == SDL_JOYBUTTONUP)
event.type = ev_keyup;
else if (inputEvent.type == SDL_JOYBUTTONDOWN)
event.type = ev_keydown;
else break;
if (inputEvent.jbutton.button < JOYBUTTONS)
event.data1 += inputEvent.jbutton.button;
else
break;
SDLJoyRemap(&event);
if (event.type != ev_console) D_PostEvent(&event);
break;
case SDL_QUIT:
if (!sdlquit)
{
sdlquit = SDL_TRUE;
M_QuitResponse('y');
}
break;
#if defined(RPC_NO_WINDOWS_H)
case SDL_SYSWMEVENT:
MainWndproc(inputEvent.syswm.msg->hwnd,
inputEvent.syswm.msg->msg,
inputEvent.syswm.msg->wParam,
inputEvent.syswm.msg->lParam);
break;
#endif
case SDL_VIDEORESIZE:
if (gamestate == GS_LEVEL || gamestate == GS_TITLESCREEN || gamestate == GS_EVALUATION)
setmodeneeded = VID_GetModeForSize(inputEvent.resize.w,inputEvent.resize.h)+1;
if (render_soft == rendermode)
{
SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
if (vidSurface) SDL_SetColors(vidSurface, localPalette, 0, 256);
}
else
SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
if (!vidSurface)
I_Error("Could not reset vidmode: %s\n",SDL_GetError());
break;
case SDL_VIDEOEXPOSE:
exposevideo = SDL_TRUE;
break;
default:
break;
}
}
//reset wheel like in win32, I don't understand it but works
#endif
} }
void I_StartupMouse(void) void I_StartupMouse(void)
@ -1494,11 +1156,6 @@ void VID_PrepareModeList(void)
#endif #endif
} }
static inline void SDLESSet(void)
{
SDL2STUB();
}
INT32 VID_SetMode(INT32 modeNum) INT32 VID_SetMode(INT32 modeNum)
{ {
SDLdoUngrabMouse(); SDLdoUngrabMouse();
@ -1550,6 +1207,12 @@ INT32 VID_SetMode(INT32 modeNum)
static SDL_bool Impl_CreateWindow(SDL_bool fullscreen) static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
{ {
int flags = 0; int flags = 0;
if (rendermode == render_none) // dedicated
{
return SDL_TRUE; // Monster Iestyn -- not sure if it really matters what we return here tbh
}
if (window != NULL) if (window != NULL)
{ {
return SDL_FALSE; return SDL_FALSE;
@ -1568,38 +1231,43 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
#ifdef HWRENDER #ifdef HWRENDER
if (rendermode == render_opengl) if (rendermode == render_opengl)
{ {
window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, flags |= SDL_WINDOW_OPENGL;
realwidth, realheight, flags | SDL_WINDOW_OPENGL);
if (window != NULL)
{
sdlglcontext = SDL_GL_CreateContext(window);
if (sdlglcontext == NULL)
{
SDL_DestroyWindow(window);
I_Error("Failed to create a GL context: %s\n", SDL_GetError());
}
else
{
SDL_GL_MakeCurrent(window, sdlglcontext);
}
}
else return SDL_FALSE;
} }
#endif #endif
// Create a window
window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
realwidth, realheight, flags);
if (window == NULL)
{
CONS_Printf(M_GetText("Couldn't create window: %s\n"), SDL_GetError());
return SDL_FALSE;
}
// Renderer-specific stuff
#ifdef HWRENDER
if (rendermode == render_opengl)
{
sdlglcontext = SDL_GL_CreateContext(window);
if (sdlglcontext == NULL)
{
SDL_DestroyWindow(window);
I_Error("Failed to create a GL context: %s\n", SDL_GetError());
}
SDL_GL_MakeCurrent(window, sdlglcontext);
}
else
#endif
if (rendermode == render_soft) if (rendermode == render_soft)
{ {
window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0));
realwidth, realheight, flags); if (renderer == NULL)
if (window != NULL)
{ {
renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0)); CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError());
if (renderer != NULL) return SDL_FALSE;
{
SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
}
else return SDL_FALSE;
} }
else return SDL_FALSE; SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
} }
return SDL_TRUE; return SDL_TRUE;
@ -1620,7 +1288,7 @@ static void Impl_SetWindowIcon(void)
{ {
return; return;
} }
SDL2STUB(); //SDL2STUB(); // Monster Iestyn: why is this stubbed?
SDL_SetWindowIcon(window, icoSurface); SDL_SetWindowIcon(window, icoSurface);
} }
@ -1718,7 +1386,6 @@ void I_StartupGraphics(void)
borderlesswindow = M_CheckParm("-borderless"); borderlesswindow = M_CheckParm("-borderless");
//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2); //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
SDLESSet();
VID_Command_ModeList_f(); VID_Command_ModeList_f();
#ifdef HWRENDER #ifdef HWRENDER
if (M_CheckParm("-opengl") || rendermode == render_opengl) if (M_CheckParm("-opengl") || rendermode == render_opengl)

View file

@ -1214,7 +1214,7 @@
C01FCF4B08A954540054247B /* Debug */ = { C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CURRENT_PROJECT_VERSION = 2.1.14; CURRENT_PROJECT_VERSION = 2.1.17;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)", "$(inherited)",
NORMALSRB2, NORMALSRB2,
@ -1226,7 +1226,7 @@
C01FCF4C08A954540054247B /* Release */ = { C01FCF4C08A954540054247B /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CURRENT_PROJECT_VERSION = 2.1.14; CURRENT_PROJECT_VERSION = 2.1.17;
GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (

View file

@ -220,7 +220,7 @@ static Mix_Chunk *ds2chunk(void *stream)
break; break;
default: // convert arbitrary hz to 44100. default: // convert arbitrary hz to 44100.
step = 0; step = 0;
frac = ((UINT32)freq << FRACBITS) / 44100; frac = ((UINT32)freq << FRACBITS) / 44100 + 1; //Add 1 to counter truncation.
while (i < samples) while (i < samples)
{ {
o = (INT16)(*s+0x80)<<8; // changed signedness and shift up to 16 bits o = (INT16)(*s+0x80)<<8; // changed signedness and shift up to 16 bits

View file

@ -24,7 +24,6 @@ boolean OglSdlSurface(INT32 w, INT32 h);
void OglSdlFinishUpdate(boolean vidwait); void OglSdlFinishUpdate(boolean vidwait);
extern SDL_Window *window;
extern SDL_Renderer *renderer; extern SDL_Renderer *renderer;
extern SDL_GLContext sdlglcontext; extern SDL_GLContext sdlglcontext;
extern Uint16 realwidth; extern Uint16 realwidth;

View file

@ -71,4 +71,7 @@ void I_GetConsoleEvents(void);
void SDLforceUngrabMouse(void); void SDLforceUngrabMouse(void);
// Needed for some WIN32 functions
extern SDL_Window *window;
#endif #endif

View file

@ -2666,6 +2666,18 @@ INT32 I_PutEnv(char *variable)
#endif #endif
} }
INT32 I_ClipboardCopy(const char *data, size_t size)
{
(void)data;
(void)size;
return -1;
}
char *I_ClipboardPaste(void)
{
return NULL;
}
/** \brief The isWadPathOk function /** \brief The isWadPathOk function
\param path string path to check \param path string path to check

View file

@ -1214,7 +1214,7 @@
C01FCF4B08A954540054247B /* Debug */ = { C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CURRENT_PROJECT_VERSION = 2.1.14; CURRENT_PROJECT_VERSION = 2.1.17;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)", "$(inherited)",
NORMALSRB2, NORMALSRB2,
@ -1226,7 +1226,7 @@
C01FCF4C08A954540054247B /* Release */ = { C01FCF4C08A954540054247B /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CURRENT_PROJECT_VERSION = 2.1.14; CURRENT_PROJECT_VERSION = 2.1.17;
GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (

View file

@ -165,8 +165,12 @@ sfxinfo_t S_sfx[NUMSFX] =
{"rail1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"rail1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"rail2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"rail2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"rlaunc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"rlaunc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"shield", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"shield", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // generic GET!
{"shldls", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"wirlsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Whirlwind GET!
{"forcsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Force GET!
{"elemsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Elemental GET!
{"armasg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Armaggeddon GET!
{"shldls", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // You LOSE!
{"spdpad", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"spdpad", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"spkdth", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"spkdth", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"spring", false, 112, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"spring", false, 112, 0, -1, NULL, 0, -1, -1, LUMPERROR},
@ -183,6 +187,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"wdjump", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"wdjump", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"mswarp", false, 60, 16, -1, NULL, 0, -1, -1, LUMPERROR}, {"mswarp", false, 60, 16, -1, NULL, 0, -1, -1, LUMPERROR},
{"mspogo", false, 60, 8, -1, NULL, 0, -1, -1, LUMPERROR}, {"mspogo", false, 60, 8, -1, NULL, 0, -1, -1, LUMPERROR},
{"boingf", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR},
// Menu, interface // Menu, interface
{"chchng", false, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"chchng", false, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR},

View file

@ -229,6 +229,10 @@ typedef enum
sfx_rail2, sfx_rail2,
sfx_rlaunc, sfx_rlaunc,
sfx_shield, sfx_shield,
sfx_wirlsg,
sfx_forcsg,
sfx_elemsg,
sfx_armasg,
sfx_shldls, sfx_shldls,
sfx_spdpad, sfx_spdpad,
sfx_spkdth, sfx_spkdth,
@ -246,6 +250,7 @@ typedef enum
sfx_wdjump, sfx_wdjump,
sfx_mswarp, sfx_mswarp,
sfx_mspogo, sfx_mspogo,
sfx_boingf,
// Menu, interface // Menu, interface
sfx_chchng, sfx_chchng,

Some files were not shown because too many files have changed in this diff Show more