Merge branch 'master' of http://git.magicalgirl.moe/STJr/SRB2Internal.git into unca_scrooge

# Conflicts:
#	src/dehacked.c
#	src/info.h
#	src/p_enemy.c
#	src/p_inter.c
This commit is contained in:
toasterbabe 2017-07-08 11:41:20 +01:00
commit a5f336e51a
179 changed files with 20786 additions and 13823 deletions

63
.circleci/config.yml Normal file
View file

@ -0,0 +1,63 @@
version: 2
jobs:
build:
working_directory: /root/SRB2
docker:
- image: debian:jessie
environment:
CC: ccache gcc -m32
PKG_CONFIG_LIBDIR: /usr/lib/i386-linux-gnu/pkgconfig
LIBGME_CFLAGS: -I/usr/include
LIBGME_LDFLAGS: -lgme
CCACHE_COMPRESS: true
WFLAGS: -Wno-unsuffixed-float-constants
GCC49: true
#- image: ubuntu:trusty
# environment:
# CC: ccache gcc -m32
# PKG_CONFIG_LIBDIR: /usr/lib/i386-linux-gnu/pkgconfig
# LIBGME_CFLAGS: -I/usr/include
# LIBGME_LDFLAGS: -lgme
# CCACHE_COMPRESS: true
# WFLAGS: -Wno-unsuffixed-float-constants
# GCC48: true
steps:
- run:
name: Add i386 arch
command: dpkg --add-architecture i386
- run:
name: Update APT listing
command: apt-get -qq update
- run:
name: Support S3 upload
command: apt-get -qq -y install ca-certificates
- restore_cache:
keys:
- v1-SRB2-APT
- run:
name: Install SDK
command: apt-get -qq -y install git build-essential nasm libpng12-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 gettext ccache wget gcc-multilib upx
- save_cache:
key: v1-SRB2-APT
paths:
- /var/cache/apt/archives
- checkout
- run:
name: Clean build
command: make -C src LINUX=1 clean
- restore_cache:
keys:
- v1-SRB2-{{ .Branch }}-{{ checksum "objs/Linux/SDL/Release/depend.dep" }}
- run:
name: Compile
command: make -C src LINUX=1 ERRORMODE=1 -k
- store_artifacts:
path: /root/SRB2/bin/Linux/Release/
destination: bin
- save_cache:
key: v1-SRB2-{{ .Branch }}-{{ checksum "objs/Linux/SDL/Release/depend.dep" }}
paths:
- /root/.ccache

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.19
LANGUAGES C) LANGUAGES C)
if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})

View file

@ -2,6 +2,7 @@
[![Build status](https://ci.appveyor.com/api/projects/status/399d4hcw9yy7hg2y?svg=true)](https://ci.appveyor.com/project/STJr/srb2) [![Build status](https://ci.appveyor.com/api/projects/status/399d4hcw9yy7hg2y?svg=true)](https://ci.appveyor.com/project/STJr/srb2)
[![Build status](https://travis-ci.org/STJr/SRB2.svg?branch=master)](https://travis-ci.org/STJr/SRB2) [![Build status](https://travis-ci.org/STJr/SRB2.svg?branch=master)](https://travis-ci.org/STJr/SRB2)
[![CircleCI](https://circleci.com/gh/STJr/SRB2/tree/master.svg?style=svg)](https://circleci.com/gh/STJr/SRB2/tree/master)
[Sonic Robo Blast 2](https://srb2.org/) is a 3D Sonic the Hedgehog fangame based on a modified version of [Doom Legacy](http://doomlegacy.sourceforge.net/). [Sonic Robo Blast 2](https://srb2.org/) is a 3D Sonic the Hedgehog fangame based on a modified version of [Doom Legacy](http://doomlegacy.sourceforge.net/).

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.19.{branch}-{build}
os: MinGW os: MinGW
environment: environment:
@ -47,7 +47,7 @@ before_build:
- upx -V - upx -V
- ccache -V - ccache -V
- ccache -s - ccache -s
- set SRB2_MFLAGS=-C src MINGW=1 WARNINGMODE=1 GCC53=1 CCACHE=1 - set SRB2_MFLAGS=-C src MINGW=1 WARNINGMODE=1 GCC63=1 CCACHE=1 NOOBJDUMP=1
build_script: build_script:
- cmd: mingw32-make.exe %SRB2_MFLAGS% %CONFIGURATION%=1 clean - cmd: mingw32-make.exe %SRB2_MFLAGS% %CONFIGURATION%=1 clean
@ -58,26 +58,29 @@ after_build:
- cmd: git rev-parse --short %APPVEYOR_REPO_COMMIT%>%TMP%/gitshort.txt - cmd: git rev-parse --short %APPVEYOR_REPO_COMMIT%>%TMP%/gitshort.txt
- cmd: set /P GITSHORT=<%TMP%/gitshort.txt - cmd: set /P GITSHORT=<%TMP%/gitshort.txt
- set BUILD_ARCHIVE=%APPVEYOR_REPO_BRANCH%-%GITSHORT%-%CONFIGURATION%.7z - set BUILD_ARCHIVE=%APPVEYOR_REPO_BRANCH%-%GITSHORT%-%CONFIGURATION%.7z
- set BUILDSARCHIVE=%APPVEYOR_REPO_BRANCH%-%CONFIGURATION%.7z
- cmd: 7z a %BUILD_ARCHIVE% bin\Mingw\Release -xr!.gitignore - cmd: 7z a %BUILD_ARCHIVE% bin\Mingw\Release -xr!.gitignore
- appveyor PushArtifact %BUILD_ARCHIVE% - appveyor PushArtifact %BUILD_ARCHIVE%
- cmd: copy %BUILD_ARCHIVE% %BUILDSARCHIVE%
- appveyor PushArtifact %BUILDSARCHIVE%
test: off test: off
deploy: #deploy:
- provider: FTP # - provider: FTP
protocol: ftps # protocol: ftps
host: # host:
secure: NsLJEPIBvmwCOj8Tg8RoRQ== # secure: NsLJEPIBvmwCOj8Tg8RoRQ==
username: # username:
secure: ejxi5mvk7oLYu7QtbYojajEPigMy0mokaKhuEVuDZcA= # secure: ejxi5mvk7oLYu7QtbYojajEPigMy0mokaKhuEVuDZcA=
password: # password:
secure: Hbn6Uy3lT0YZ88yFJ3aW4w== # secure: Hbn6Uy3lT0YZ88yFJ3aW4w==
folder: appveyor # folder: appveyor
application: # application:
active_mode: false # active_mode: false
on: # on:
branch: master # branch: master
appveyor_repo_tag: true # appveyor_repo_tag: true
on_finish: on_finish:

View file

@ -1,3 +1,3 @@
/srb2sdl.exe *.exe
/srb2win.exe *.mo
/r_opengl.dll r_opengl.dll

View file

@ -1,3 +1,3 @@
/srb2sdl.exe *.exe
/srb2win.exe *.mo
/r_opengl.dll r_opengl.dll

3
debian/docs vendored
View file

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

8
objs/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
#All folders
SRB2.res
depend.dep
depend.ped
*.o
#VC9 folder only
/VC9/Win32
/VC9/x64

View file

@ -1 +1,2 @@
/depend.dep # DON'T REMOVE
# This keeps the folder from disappearing

View file

@ -1 +1,2 @@
/depend.dep # DON'T REMOVE
# This keeps the folder from disappearing

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

View file

@ -1,3 +1,2 @@
/SRB2.res # DON'T REMOVE
/depend.dep # This keeps the folder from disappearing
/*.o

View file

@ -1,3 +1,2 @@
/SRB2.res # DON'T REMOVE
/depend.dep # This keeps the folder from disappearing
/*.o

View file

@ -1,3 +1,2 @@
/SRB2.res # DON'T REMOVE
/depend.dep # This keeps the folder from disappearing
/*.o

View file

@ -1,3 +1,2 @@
/SRB2.res # DON'T REMOVE
/depend.dep # This keeps the folder from disappearing
/*.o

View file

@ -1,3 +1,2 @@
/SRB2.res # DON'T REMOVE
/depend.dep # This keeps the folder from disappearing
/*.o

View file

@ -1,3 +1,2 @@
/SRB2.res # DON'T REMOVE
/depend.dep # This keeps the folder from disappearing
/*.o

View file

@ -1,3 +1,2 @@
/SRB2.res # DON'T REMOVE
/depend.dep # This keeps the folder from disappearing
/*.o

View file

@ -1,3 +1,2 @@
/SRB2.res # DON'T REMOVE
/depend.dep # This keeps the folder from disappearing
/*.o

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

View file

@ -1 +1,2 @@
/depend.dep # DON'T REMOVE
# This keeps the folder from disappearing

View file

@ -1 +1,2 @@
/depend.ped # DON'T REMOVE
# This keeps the folder from disappearing

2
objs/VC/.gitignore vendored
View file

@ -0,0 +1,2 @@
# DON'T REMOVE
# This keeps the folder from disappearing

4
objs/VC9/.gitignore vendored
View file

@ -1,2 +1,2 @@
/Win32 # DON'T REMOVE
/x64 # This keeps the folder from disappearing

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

View file

@ -1 +1,2 @@
/depend.dep # DON'T REMOVE
# This keeps the folder from disappearing

View file

@ -1 +1,2 @@
/depend.dep # DON'T REMOVE
# This keeps the folder from disappearing

View file

@ -1 +1,2 @@
/depend.dep # DON'T REMOVE
# This keeps the folder from disappearing

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

View file

@ -1,2 +1,2 @@
/depend.dep # DON'T REMOVE
/*.o # This keeps the folder from disappearing

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
@ -493,13 +511,11 @@ OBJS:=$(i_main_o) \
# For reference, this is the command I use to build a srb2.pot file from the source code. # For reference, this is the command I use to build a srb2.pot file from the source code.
# (The listed source files are the ones containing translated strings). # (The listed source files are the ones containing translated strings).
# FILES=""; for file in `find ./ | grep "\.c" | grep -v svn`; do [ "`grep "M_GetText(" $file`" ] && FILES="$FILES $file"; done; xgettext -d srb2 -o locale/srb2.pot -kM_GetText -F --no-wrap $FILES # FILES=""; for file in `find ./ | grep "\.c" | grep -v svn`; do [ "`grep "M_GetText(" $file`" ] && FILES="$FILES $file"; done; xgettext -d srb2 -o locale/srb2.pot -kM_GetText -F --no-wrap $FILES
ifndef NOGETTEXT
ifdef GETTEXT ifdef GETTEXT
POS:=$(BIN)/en.mo POS:=$(BIN)/en.mo
OPTS+=-DGETTEXT OPTS+=-DGETTEXT
endif endif
endif
ifdef DJGPPDOS ifdef DJGPPDOS
all: pre-build $(BIN)/$(EXENAME) all: pre-build $(BIN)/$(EXENAME)

View file

@ -7,6 +7,23 @@
# and other things # and other things
# #
ifdef GCC63
GCC62=1
endif
ifdef GCC62
GCC61=1
endif
ifdef GCC61
GCC54=1
endif
ifdef GCC54
GCC53=1
endif
ifdef GCC53 ifdef GCC53
GCC52=1 GCC52=1
endif endif
@ -164,19 +181,29 @@ ifdef GCC45
WFLAGS+=-Wunsuffixed-float-constants WFLAGS+=-Wunsuffixed-float-constants
endif endif
endif endif
ifdef NOLDWARNING ifdef NOLDWARNING
LDFLAGS+=-Wl,--as-needed LDFLAGS+=-Wl,--as-needed
endif endif
ifdef ERRORMODE ifdef ERRORMODE
WFLAGS+=-Werror WFLAGS+=-Werror
endif endif
WFLAGS+=$(OLDWFLAGS)
ifdef GCC43 ifdef GCC43
#WFLAGS+=-Wno-error=clobbered #WFLAGS+=-Wno-error=clobbered
endif endif
ifdef GCC46 ifdef GCC46
WFLAGS+=-Wno-error=suggest-attribute=noreturn WFLAGS+=-Wno-error=suggest-attribute=noreturn
endif endif
WFLAGS+=$(OLDWFLAGS) ifdef GCC54
WFLAGS+=-Wno-logical-op -Wno-error=logical-op
endif
ifdef GCC61
WFLAGS+=-Wno-tautological-compare -Wno-error=tautological-compare
endif
#indicate platform and what interface use with #indicate platform and what interface use with
@ -256,9 +283,6 @@ else
ifdef LINUX ifdef LINUX
NASMFORMAT=elf -DLINUX NASMFORMAT=elf -DLINUX
SDL=1 SDL=1
ifndef NOGETTEXT
GETTEXT=1
endif
ifdef LINUX64 ifdef LINUX64
OBJDIR:=$(OBJDIR)/Linux64 OBJDIR:=$(OBJDIR)/Linux64
BIN:=$(BIN)/Linux64 BIN:=$(BIN)/Linux64
@ -294,9 +318,6 @@ else
ifdef MINGW64 ifdef MINGW64
INTERFACE=win32 INTERFACE=win32
#NASMFORMAT=win64 #NASMFORMAT=win64
ifndef NOGETTEXT
#GETTEXT=1
endif
OBJDIR:=$(OBJDIR)/Mingw64 OBJDIR:=$(OBJDIR)/Mingw64
BIN:=$(BIN)/Mingw64 BIN:=$(BIN)/Mingw64
else else
@ -327,9 +348,6 @@ else
ifdef MINGW ifdef MINGW
INTERFACE=win32 INTERFACE=win32
NASMFORMAT=win32 NASMFORMAT=win32
ifndef NOGETTEXT
GETTEXT=1
endif
OBJDIR:=$(OBJDIR)/Mingw OBJDIR:=$(OBJDIR)/Mingw
BIN:=$(BIN)/Mingw BIN:=$(BIN)/Mingw
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

@ -212,7 +212,7 @@ boolean B_CheckRespawn(player_t *player)
// Check if Sonic is busy first. // Check if Sonic is busy first.
// If he's doing any of these things, he probably doesn't want to see us. // If he's doing any of these things, he probably doesn't want to see us.
if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_NIGHTSMODE) if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_BOUNCING)
|| (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK) || (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK)
|| (sonic->player->powers[pw_carry])) || (sonic->player->powers[pw_carry]))
return false; return false;

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

@ -1165,7 +1165,7 @@ found:
if (var == &cv_forceskin) if (var == &cv_forceskin)
{ {
var->value = R_SkinAvailable(var->string); var->value = R_SkinAvailable(var->string);
if (!R_SkinUnlock(var->value)) if (!R_SkinUsable(-1, var->value))
var->value = -1; var->value = -1;
} }
else else
@ -1361,6 +1361,16 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
return; return;
} }
if (var == &cv_forceskin)
{
INT32 skin = R_SkinAvailable(value);
if ((stricmp(value, "None")) && ((skin == -1) || !R_SkinUsable(-1, skin)))
{
CONS_Printf("Please provide a valid skin name (\"None\" disables).\n");
return;
}
}
// Only add to netcmd buffer if in a netgame, otherwise, just change it. // Only add to netcmd buffer if in a netgame, otherwise, just change it.
if (netgame || multiplayer) if (netgame || multiplayer)
{ {
@ -1478,7 +1488,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
else if (newvalue >= numskins) else if (newvalue >= numskins)
newvalue = -1; newvalue = -1;
} while ((oldvalue != newvalue) } while ((oldvalue != newvalue)
&& !(R_SkinUnlock(newvalue))); && !(R_SkinUsable(-1, newvalue)));
} }
else else
newvalue = var->value + increment; newvalue = var->value + increment;
@ -1551,34 +1561,27 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if (var == &cv_chooseskin) if (var == &cv_chooseskin)
{ {
// Special case for the chooseskin variable, used only directly from the menu // Special case for the chooseskin variable, used only directly from the menu
if (increment > 0) // Going up! newvalue = var->value - 1;
do
{ {
newvalue = var->value - 1; if (increment > 0) // Going up!
do
{ {
newvalue++; newvalue++;
if (newvalue == MAXSKINS) if (newvalue == MAXSKINS)
newvalue = 0; newvalue = 0;
} while (var->PossibleValue[newvalue].strvalue == NULL); }
var->value = newvalue + 1; else if (increment < 0) // Going down!
var->string = var->PossibleValue[newvalue].strvalue;
var->func();
return;
}
else if (increment < 0) // Going down!
{
newvalue = var->value - 1;
do
{ {
newvalue--; newvalue--;
if (newvalue == -1) if (newvalue == -1)
newvalue = MAXSKINS-1; newvalue = MAXSKINS-1;
} while (var->PossibleValue[newvalue].strvalue == NULL); }
var->value = newvalue + 1; } while (var->PossibleValue[newvalue].strvalue == NULL);
var->string = var->PossibleValue[newvalue].strvalue;
var->func(); var->value = newvalue + 1;
return; var->string = var->PossibleValue[newvalue].strvalue;
} var->func();
return;
} }
#ifdef PARANOIA #ifdef PARANOIA
if (currentindice == -1) if (currentindice == -1)

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, true);
}
else
V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
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, true);
}
else
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, true);
if (c == input_cur && con_tick >= 4)
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
}
if (cend == input_cur && con_tick >= 4)
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
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, true);
}
} }
// 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
@ -1306,11 +1465,11 @@ static void CON_DrawHudlines(void)
else else
{ {
//charwidth = SHORT(hu_font['A'-HU_FONTSTART]->width) * con_scalefactor; //charwidth = SHORT(hu_font['A'-HU_FONTSTART]->width) * con_scalefactor;
V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
} }
} }
//V_DrawCharacter(x, y, (p[c]&0xff) | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); //V_DrawCharacter(x, y, (p[c]&0xff) | cv_constextsize.value | V_NOSCALESTART, true);
y += charheight; y += charheight;
} }
@ -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
@ -1448,7 +1607,7 @@ static void CON_DrawConsole(void)
charflags = (*p & 0x7f) << V_CHARCOLORSHIFT; charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
p++; p++;
} }
V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
} }
} }

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;
@ -164,6 +172,7 @@ typedef struct
UINT8 skincolor; UINT8 skincolor;
INT32 skin; INT32 skin;
UINT32 availabilities;
// Just in case Lua does something like // Just in case Lua does something like
// modify these at runtime // modify these at runtime
fixed_t camerascale; fixed_t camerascale;
@ -176,9 +185,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;
@ -187,7 +196,6 @@ typedef struct
fixed_t playerspinheight; fixed_t playerspinheight;
fixed_t speed; fixed_t speed;
UINT8 jumping;
UINT8 secondjump; UINT8 secondjump;
UINT8 fly1; UINT8 fly1;
tic_t glidetime; tic_t glidetime;
@ -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.
@ -275,21 +284,22 @@ typedef struct
// 0xFF == not in game; else player skin num // 0xFF == not in game; else player skin num
UINT8 playerskins[MAXPLAYERS]; UINT8 playerskins[MAXPLAYERS];
UINT8 playercolor[MAXPLAYERS]; UINT8 playercolor[MAXPLAYERS];
UINT32 playeravailabilities[MAXPLAYERS];
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 +308,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 +381,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 +430,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 +451,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 +490,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"
@ -106,8 +107,6 @@ UINT8 window_notinfocus = false;
// //
// DEMO LOOP // DEMO LOOP
// //
//static INT32 demosequence;
static const char *pagename = "MAP1PIC";
static char *startupwadfiles[MAX_WADFILES]; static char *startupwadfiles[MAX_WADFILES];
boolean devparm = false; // started game with -devparm boolean devparm = false; // started game with -devparm
@ -176,6 +175,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 || ev->type == ev_console) 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 +219,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
@ -684,9 +718,7 @@ void D_StartTitle(void)
maptol = 0; maptol = 0;
gameaction = ga_nothing; gameaction = ga_nothing;
playerdeadview = false;
displayplayer = consoleplayer = 0; displayplayer = consoleplayer = 0;
//demosequence = -1;
gametype = GT_COOP; gametype = GT_COOP;
paused = false; paused = false;
advancedemo = false; advancedemo = false;
@ -837,29 +869,24 @@ static void IdentifyVersion(void)
I_Error("File %s has been modified with non-music lumps",musicfile); I_Error("File %s has been modified with non-music lumps",musicfile);
} }
#endif #endif
}
/* ======================================================================== */ #if 1 // This section can be deleted when music_new is merged with music.dta
// Just print the nice red titlebar like the original SRB2 for DOS. {
/* ======================================================================== */ const char *musicfile = "music_new.dta";
#ifdef PC_DOS const char *musicpath = va(pandf,srb2waddir,musicfile);
static inline void D_Titlebar(char *title1, char *title2) int ms = W_VerifyNMUSlumps(musicpath); // Don't forget the music!
{ if (ms == 1)
// SRB2 banner D_AddFile(musicpath);
clrscr(); else if (ms == 0)
textattr((BLUE<<4)+WHITE); I_Error("File %s has been modified with non-music lumps",musicfile);
clreol(); }
cputs(title1);
// standard srb2 banner
textattr((RED<<4)+WHITE);
clreol();
gotoxy((80-strlen(title2))/2, 2);
cputs(title2);
normvideo();
gotoxy(1,3);
}
#endif #endif
}
#ifdef PC_DOS
/* ======================================================================== */
// Code for printing SRB2's title bar in DOS
/* ======================================================================== */
// //
// Center the title string, then add the date and time of compilation. // Center the title string, then add the date and time of compilation.
@ -888,6 +915,31 @@ static inline void D_MakeTitleString(char *s)
strcpy(s, temp); strcpy(s, temp);
} }
static inline void D_Titlebar(void)
{
char title1[82]; // srb2 title banner
char title2[82];
strcpy(title1, "Sonic Robo Blast 2");
strcpy(title2, "Sonic Robo Blast 2");
D_MakeTitleString(title1);
// SRB2 banner
clrscr();
textattr((BLUE<<4)+WHITE);
clreol();
cputs(title1);
// standard srb2 banner
textattr((RED<<4)+WHITE);
clreol();
gotoxy((80-strlen(title2))/2, 2);
cputs(title2);
normvideo();
gotoxy(1,3);
}
#endif
// //
// D_SRB2Main // D_SRB2Main
@ -895,8 +947,6 @@ static inline void D_MakeTitleString(char *s)
void D_SRB2Main(void) void D_SRB2Main(void)
{ {
INT32 p; INT32 p;
char srb2[82]; // srb2 title banner
char title[82];
INT32 pstartmap = 1; INT32 pstartmap = 1;
boolean autostart = false; boolean autostart = false;
@ -939,20 +989,8 @@ void D_SRB2Main(void)
dedicated = M_CheckParm("-dedicated") != 0; dedicated = M_CheckParm("-dedicated") != 0;
#endif #endif
strcpy(title, "Sonic Robo Blast 2");
strcpy(srb2, "Sonic Robo Blast 2");
D_MakeTitleString(srb2);
#ifdef PC_DOS #ifdef PC_DOS
D_Titlebar(srb2, title); D_Titlebar();
#endif
#if defined (__OS2__) && !defined (HAVE_SDL)
// set PM window title
snprintf(pmData->title, sizeof (pmData->title),
"Sonic Robo Blast 2" VERSIONSTRING ": %s",
title);
pmData->title[sizeof (pmData->title) - 1] = '\0';
#endif #endif
if (devparm) if (devparm)
@ -1352,7 +1390,6 @@ void D_SRB2Main(void)
if (dedicated && server) if (dedicated && server)
{ {
pagename = "TITLESKY";
levelstarttic = gametic; levelstarttic = gametic;
G_SetGamestate(GS_LEVEL); G_SetGamestate(GS_LEVEL);
if (!P_SetupLevel(false)) if (!P_SetupLevel(false))

View file

@ -34,7 +34,7 @@ void D_SRB2Loop(void) FUNCNORETURN;
// D_SRB2Main() // D_SRB2Main()
// Not a globally visible function, just included for source reference, // Not a globally visible function, just included for source reference,
// calls all startup code, parses command line options. // calls all startup code, parses command line options.
// If not overrided by user input, calls N_AdvanceDemo. // If not overrided by user input, calls D_AdvanceDemo.
// //
void D_SRB2Main(void); void D_SRB2Main(void);
@ -51,9 +51,6 @@ const char *D_Home(void);
// //
// BASE LEVEL // BASE LEVEL
// //
void D_PageTicker(void);
// pagename is lumpname of a 320x200 patch to fill the screen
void D_PageDrawer(const char *pagename);
void D_AdvanceDemo(void); void D_AdvanceDemo(void);
void D_StartTitle(void); void D_StartTitle(void);

View file

@ -31,25 +31,27 @@
// //
// 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;
/// \brief network packet data, points inside doomcom /// \brief network packet data, points inside doomcom
doomdata_t *netbuffer = NULL; doomdata_t *netbuffer = NULL;
#ifdef DEBUGFILE
FILE *debugfile = NULL; // put some net info in a file during the game FILE *debugfile = NULL; // put some net info in a file during the game
#endif
#define MAXREBOUND 8 #define MAXREBOUND 8
static doomdata_t reboundstore[MAXREBOUND]; static doomdata_t reboundstore[MAXREBOUND];
@ -62,7 +64,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 +131,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 +141,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 +154,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 +215,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 +235,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 +252,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 +265,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 +277,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 +333,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 +366,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 +426,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 +437,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 +473,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 +503,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 +524,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 +544,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 +552,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 +570,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 +611,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 +643,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 +654,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 +678,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
@ -652,12 +713,25 @@ void Net_CloseConnection(INT32 node)
#else #else
INT32 i; INT32 i;
boolean forceclose = (node & FORCECLOSE) != 0; boolean forceclose = (node & FORCECLOSE) != 0;
if (node == -1)
{
DEBFILE(M_GetText("Net_CloseConnection: node -1 detected!\n"));
return; // nope, just ignore it
}
node &= ~FORCECLOSE; node &= ~FORCECLOSE;
if (!node) if (!node)
return; return;
nodes[node].flags |= CLOSE; if (node < 0 || node >= MAXNETNODES) // prevent invalid nodes from crashing the game
{
DEBFILE(va(M_GetText("Net_CloseConnection: invalid node %d detected!\n"), node));
return;
}
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 +750,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 +803,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 +829,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 +857,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 +895,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 +912,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 +926,102 @@ 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;
}
#ifndef NONET
static boolean ShouldDropPacket(void)
{
return (packetdropquantity[netbuffer->packettype])
|| (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100;
}
#endif
#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 +1052,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 +1065,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 +1086,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 +1124,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 +1151,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 +1170,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 +1180,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 +1212,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 +1300,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 +1441,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);
@ -188,14 +189,13 @@ static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2
static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}}; static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}};
static CV_PossibleValue_t sleeping_cons_t[] = {{-1, "MIN"}, {1000/TICRATE, "MAX"}, {0, NULL}}; static CV_PossibleValue_t sleeping_cons_t[] = {{-1, "MIN"}, {1000/TICRATE, "MAX"}, {0, NULL}};
static CV_PossibleValue_t competitionboxes_cons_t[] = {{0, "Normal"}, {1, "Random"}, {2, "Teleports"}, static CV_PossibleValue_t competitionboxes_cons_t[] = {{0, "Normal"}, {1, "Random"}, //{2, "Teleports"},
{3, "None"}, {0, NULL}}; {3, "None"}, {0, NULL}};
static CV_PossibleValue_t matchboxes_cons_t[] = {{0, "Normal"}, {1, "Random"}, {2, "Non-Random"}, static CV_PossibleValue_t matchboxes_cons_t[] = {{0, "Normal"}, {1, "Random"}, {2, "Non-Random"},
{3, "None"}, {0, NULL}}; {3, "None"}, {0, NULL}};
static CV_PossibleValue_t chances_cons_t[] = {{0, "MIN"}, {9, "MAX"}, {0, NULL}}; static CV_PossibleValue_t chances_cons_t[] = {{0, "MIN"}, {9, "MAX"}, {0, NULL}};
static CV_PossibleValue_t match_scoring_cons_t[] = {{0, "Normal"}, {1, "Classic"}, {0, NULL}};
static CV_PossibleValue_t pause_cons_t[] = {{0, "Server"}, {1, "All"}, {0, NULL}}; static CV_PossibleValue_t pause_cons_t[] = {{0, "Server"}, {1, "All"}, {0, NULL}};
static CV_PossibleValue_t timetic_cons_t[] = {{0, "Normal"}, {1, "Tics"}, {2, "Centiseconds"}, {0, NULL}}; static CV_PossibleValue_t timetic_cons_t[] = {{0, "Normal"}, {1, "Tics"}, {2, "Centiseconds"}, {0, NULL}};
@ -310,7 +310,6 @@ consvar_t cv_friendlyfire = {"friendlyfire", "Off", CV_NETVAR, CV_OnOff, NULL, 0
consvar_t cv_itemfinder = {"itemfinder", "Off", CV_CALL, CV_OnOff, ItemFinder_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_itemfinder = {"itemfinder", "Off", CV_CALL, CV_OnOff, ItemFinder_OnChange, 0, NULL, NULL, 0, 0, NULL};
// Scoring type options // Scoring type options
consvar_t cv_match_scoring = {"matchscoring", "Normal", CV_NETVAR|CV_CHEAT, match_scoring_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_rollingdemos = {"rollingdemos", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_rollingdemos = {"rollingdemos", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
@ -340,7 +339,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 +366,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
// ========================================================================= // =========================================================================
@ -453,7 +483,6 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_itemrespawntime); CV_RegisterVar(&cv_itemrespawntime);
CV_RegisterVar(&cv_itemrespawn); CV_RegisterVar(&cv_itemrespawn);
CV_RegisterVar(&cv_flagtime); CV_RegisterVar(&cv_flagtime);
CV_RegisterVar(&cv_suddendeath);
// misc // misc
CV_RegisterVar(&cv_friendlyfire); CV_RegisterVar(&cv_friendlyfire);
@ -501,7 +530,6 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_startinglives); CV_RegisterVar(&cv_startinglives);
CV_RegisterVar(&cv_countdowntime); CV_RegisterVar(&cv_countdowntime);
CV_RegisterVar(&cv_runscripts); CV_RegisterVar(&cv_runscripts);
CV_RegisterVar(&cv_match_scoring);
CV_RegisterVar(&cv_overtime); CV_RegisterVar(&cv_overtime);
CV_RegisterVar(&cv_pause); CV_RegisterVar(&cv_pause);
CV_RegisterVar(&cv_mute); CV_RegisterVar(&cv_mute);
@ -517,9 +545,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);
@ -581,6 +612,7 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_screenshot_option); CV_RegisterVar(&cv_screenshot_option);
CV_RegisterVar(&cv_screenshot_folder); CV_RegisterVar(&cv_screenshot_folder);
CV_RegisterVar(&cv_screenshot_colorprofile);
CV_RegisterVar(&cv_moviemode); CV_RegisterVar(&cv_moviemode);
// PNG variables // PNG variables
CV_RegisterVar(&cv_zlib_level); CV_RegisterVar(&cv_zlib_level);
@ -638,7 +670,29 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_resetmusic); CV_RegisterVar(&cv_resetmusic);
// FIXME: not to be here.. but needs be done for config loading // FIXME: not to be here.. but needs be done for config loading
CV_RegisterVar(&cv_usegamma); CV_RegisterVar(&cv_globalgamma);
CV_RegisterVar(&cv_globalsaturation);
CV_RegisterVar(&cv_rhue);
CV_RegisterVar(&cv_yhue);
CV_RegisterVar(&cv_ghue);
CV_RegisterVar(&cv_chue);
CV_RegisterVar(&cv_bhue);
CV_RegisterVar(&cv_mhue);
CV_RegisterVar(&cv_rgamma);
CV_RegisterVar(&cv_ygamma);
CV_RegisterVar(&cv_ggamma);
CV_RegisterVar(&cv_cgamma);
CV_RegisterVar(&cv_bgamma);
CV_RegisterVar(&cv_mgamma);
CV_RegisterVar(&cv_rsaturation);
CV_RegisterVar(&cv_ysaturation);
CV_RegisterVar(&cv_gsaturation);
CV_RegisterVar(&cv_csaturation);
CV_RegisterVar(&cv_bsaturation);
CV_RegisterVar(&cv_msaturation);
// m_menu.c // m_menu.c
CV_RegisterVar(&cv_crosshair); CV_RegisterVar(&cv_crosshair);
@ -696,6 +750,7 @@ void D_RegisterClientCommands(void)
// s_sound.c // s_sound.c
CV_RegisterVar(&cv_soundvolume); CV_RegisterVar(&cv_soundvolume);
CV_RegisterVar(&cv_closedcaptioning);
CV_RegisterVar(&cv_digmusicvolume); CV_RegisterVar(&cv_digmusicvolume);
CV_RegisterVar(&cv_midimusicvolume); CV_RegisterVar(&cv_midimusicvolume);
CV_RegisterVar(&cv_numChannels); CV_RegisterVar(&cv_numChannels);
@ -976,7 +1031,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 ((cv_forceskin.value != -1) || (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0'))
return false; return false;
// Can change skin in intermission and whatnot. // Can change skin in intermission and whatnot.
@ -1074,6 +1129,8 @@ static void SendNameAndColor(void)
if (!Playing()) if (!Playing())
return; return;
players[consoleplayer].availabilities = R_GetSkinAvailabilities();
// If you're not in a netgame, merely update the skin, color, and name. // If you're not in a netgame, merely update the skin, color, and name.
if (!netgame) if (!netgame)
{ {
@ -1092,7 +1149,7 @@ static void SendNameAndColor(void)
SetPlayerSkinByNum(consoleplayer, 0); SetPlayerSkinByNum(consoleplayer, 0);
CV_StealthSet(&cv_skin, skins[0].name); CV_StealthSet(&cv_skin, skins[0].name);
} }
else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUnlock(foundskin)) else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin))
{ {
boolean notsame; boolean notsame;
@ -1139,7 +1196,7 @@ static void SendNameAndColor(void)
// check if player has the skin loaded (cv_skin may have // check if player has the skin loaded (cv_skin may have
// the name of a skin that was available in the previous game) // the name of a skin that was available in the previous game)
cv_skin.value = R_SkinAvailable(cv_skin.string); cv_skin.value = R_SkinAvailable(cv_skin.string);
if ((cv_skin.value < 0) || !R_SkinUnlock(cv_skin.value)) if ((cv_skin.value < 0) || !R_SkinUsable(consoleplayer, cv_skin.value))
{ {
CV_StealthSet(&cv_skin, DEFAULTSKIN); CV_StealthSet(&cv_skin, DEFAULTSKIN);
cv_skin.value = 0; cv_skin.value = 0;
@ -1147,6 +1204,7 @@ static void SendNameAndColor(void)
// Finally write out the complete packet and send it off. // Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME); WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME);
WRITEUINT32(p, (UINT32)players[consoleplayer].availabilities);
WRITEUINT8(p, (UINT8)cv_playercolor.value); WRITEUINT8(p, (UINT8)cv_playercolor.value);
WRITEUINT8(p, (UINT8)cv_skin.value); WRITEUINT8(p, (UINT8)cv_skin.value);
SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf); SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf);
@ -1189,6 +1247,8 @@ static void SendNameAndColor2(void)
if (!Playing()) if (!Playing())
return; return;
players[secondplaya].availabilities = R_GetSkinAvailabilities();
// If you're not in a netgame, merely update the skin, color, and name. // If you're not in a netgame, merely update the skin, color, and name.
if (botingame) if (botingame)
{ {
@ -1217,7 +1277,7 @@ static void SendNameAndColor2(void)
SetPlayerSkinByNum(secondplaya, forcedskin); SetPlayerSkinByNum(secondplaya, forcedskin);
CV_StealthSet(&cv_skin2, skins[forcedskin].name); CV_StealthSet(&cv_skin2, skins[forcedskin].name);
} }
else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUnlock(foundskin)) else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUsable(secondplaya, foundskin))
{ {
boolean notsame; boolean notsame;
@ -1272,6 +1332,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
#endif #endif
READSTRINGN(*cp, name, MAXPLAYERNAME); READSTRINGN(*cp, name, MAXPLAYERNAME);
p->availabilities = READUINT32(*cp);
color = READUINT8(*cp); color = READUINT8(*cp);
skin = READUINT8(*cp); skin = READUINT8(*cp);
@ -1288,6 +1349,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
if (server && (p != &players[consoleplayer] && p != &players[secondarydisplayplayer])) if (server && (p != &players[consoleplayer] && p != &players[secondarydisplayplayer]))
{ {
boolean kick = false; boolean kick = false;
INT32 s;
// team colors // team colors
if (G_GametypeHasTeams()) if (G_GametypeHasTeams())
@ -1302,6 +1364,16 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
if (!p->skincolor) if (!p->skincolor)
kick = true; kick = true;
// availabilities
for (s = 0; s < MAXSKINS; s++)
{
if (!skins[s].availability && (p->availabilities & (1 << s)))
{
kick = true;
break;
}
}
if (kick) if (kick)
{ {
XBOXSTATIC UINT8 buf[2]; XBOXSTATIC UINT8 buf[2];
@ -1497,10 +1569,13 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
// The supplied data are assumed to be good. // The supplied data are assumed to be good.
I_Assert(delay >= 0 && delay <= 2); I_Assert(delay >= 0 && delay <= 2);
if (mapnum != -1)
CV_SetValue(&cv_nextmap, mapnum);
CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d ultmode=%d resetplayers=%d delay=%d skipprecutscene=%d\n", CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d ultmode=%d resetplayers=%d delay=%d skipprecutscene=%d\n",
mapnum, newgametype, pultmode, resetplayers, delay, skipprecutscene); mapnum, newgametype, pultmode, resetplayers, delay, skipprecutscene);
if (netgame || multiplayer) if ((netgame || multiplayer) && !((gametype == newgametype) && (newgametype == GT_COOP)))
FLS = false; FLS = false;
if (delay != 2) if (delay != 2)
@ -1534,8 +1609,13 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
mapchangepending = 0; mapchangepending = 0;
// spawn the server if needed // spawn the server if needed
// reset players if there is a new one // reset players if there is a new one
if (!(adminplayer == consoleplayer) && SV_SpawnServer()) if (!(adminplayer == consoleplayer))
buf[0] &= ~(1<<1); {
if (SV_SpawnServer())
buf[0] &= ~(1<<1);
if (!Playing()) // you failed to start a server somehow, so cancel the map change
return;
}
// Kick bot from special stages // Kick bot from special stages
if (botskin) if (botskin)
@ -1587,7 +1667,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;
@ -1666,9 +1746,19 @@ static void Command_Map_f(void)
} }
} }
// Prevent warping to locked levels
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
// lives hell.
if (!dedicated && M_MapLocked(newmapnum))
{
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
return;
}
// don't use a gametype the map doesn't support // don't use a gametype the map doesn't support
if (cv_debug || COM_CheckParm("-force") || cv_skipmapcheck.value) if (cv_debug || COM_CheckParm("-force") || cv_skipmapcheck.value)
; // The player wants us to trek on anyway. Do so. fromlevelselect = false; // The player wants us to trek on anyway. Do so.
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
// Alternatively, bail if the map header is completely missing anyway. // Alternatively, bail if the map header is completely missing anyway.
else if (!mapheaderinfo[newmapnum-1] else if (!mapheaderinfo[newmapnum-1]
@ -1687,19 +1777,10 @@ static void Command_Map_f(void)
CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring); CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring);
return; return;
} }
else
fromlevelselect = ((netgame || multiplayer) && ((gametype == newgametype) && (newgametype == GT_COOP)));
// Prevent warping to locked levels D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect);
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
// lives hell.
if (!dedicated && M_MapLocked(newmapnum))
{
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
return;
}
fromlevelselect = false;
D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, false);
} }
/** Receives a map command and changes the map. /** Receives a map command and changes the map.
@ -1765,17 +1846,14 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
if (demoplayback && !timingdemo) if (demoplayback && !timingdemo)
precache = false; precache = false;
if (resetplayer) if (resetplayer && !FLS)
{ emeralds = 0;
if (!FLS || (netgame || multiplayer))
emeralds = 0;
}
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
LUAh_MapChange(); LUAh_MapChange();
#endif #endif
G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene); G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene, FLS);
if (demoplayback && !timingdemo) if (demoplayback && !timingdemo)
precache = true; precache = true;
CON_ToggleOff(); CON_ToggleOff();
@ -1914,7 +1992,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];
@ -2081,7 +2159,7 @@ static void Command_Teamchange_f(void)
return; return;
} }
if (!cv_allowteamchange.value && !NetPacket.packet.newteam) // allow swapping to spectator even in locked teams. if (!cv_allowteamchange.value && NetPacket.packet.newteam) // allow swapping to spectator even in locked teams.
{ {
CONS_Alert(CONS_NOTICE, M_GetText("The server is not allowing team changes at the moment.\n")); CONS_Alert(CONS_NOTICE, M_GetText("The server is not allowing team changes at the moment.\n"));
return; return;
@ -2178,7 +2256,7 @@ static void Command_Teamchange2_f(void)
return; return;
} }
if (!cv_allowteamchange.value && !NetPacket.packet.newteam) // allow swapping to spectator even in locked teams. if (!cv_allowteamchange.value && NetPacket.packet.newteam) // allow swapping to spectator even in locked teams.
{ {
CONS_Alert(CONS_NOTICE, M_GetText("The server is not allowing team changes at the moment.\n")); CONS_Alert(CONS_NOTICE, M_GetText("The server is not allowing team changes at the moment.\n"));
return; return;
@ -2574,7 +2652,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 +2707,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 +2766,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 +2788,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 +2872,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
{ {
@ -2932,6 +3010,7 @@ static void Command_Addfile(void)
XBOXSTATIC char buf[256]; XBOXSTATIC char buf[256];
char *buf_p = buf; char *buf_p = buf;
INT32 i; INT32 i;
int musiconly; // W_VerifyNMUSlumps isn't boolean
if (COM_Argc() != 2) if (COM_Argc() != 2)
{ {
@ -2946,7 +3025,9 @@ static void Command_Addfile(void)
if (!isprint(fn[i]) || fn[i] == ';') if (!isprint(fn[i]) || fn[i] == ';')
return; return;
if (!W_VerifyNMUSlumps(fn)) musiconly = W_VerifyNMUSlumps(fn);
if (!musiconly)
{ {
// ... But only so long as they contain nothing more then music and sprites. // ... But only so long as they contain nothing more then music and sprites.
if (netgame && !(server || adminplayer == consoleplayer)) if (netgame && !(server || adminplayer == consoleplayer))
@ -2958,7 +3039,7 @@ static void Command_Addfile(void)
} }
// Add file on your client directly if it is trivial, or you aren't in a netgame. // Add file on your client directly if it is trivial, or you aren't in a netgame.
if (!(netgame || multiplayer) || W_VerifyNMUSlumps(fn)) if (!(netgame || multiplayer) || musiconly)
{ {
P_AddWadFile(fn, NULL); P_AddWadFile(fn, NULL);
return; return;
@ -2978,9 +3059,7 @@ static void Command_Addfile(void)
#else #else
FILE *fhandle; FILE *fhandle;
fhandle = fopen(fn, "rb"); if ((fhandle = W_OpenWadFile(&fn, true)) != NULL)
if (fhandle)
{ {
tic_t t = I_GetTime(); tic_t t = I_GetTime();
CONS_Debug(DBG_SETUP, "Making MD5 for %s\n",fn); CONS_Debug(DBG_SETUP, "Making MD5 for %s\n",fn);
@ -2988,11 +3067,8 @@ static void Command_Addfile(void)
CONS_Debug(DBG_SETUP, "MD5 calc for %s took %f second\n", fn, (float)(I_GetTime() - t)/TICRATE); CONS_Debug(DBG_SETUP, "MD5 calc for %s took %f second\n", fn, (float)(I_GetTime() - t)/TICRATE);
fclose(fhandle); fclose(fhandle);
} }
else else // file not found
{
CONS_Printf(M_GetText("File %s not found.\n"), fn);
return; return;
}
#endif #endif
WRITEMEM(buf_p, md5sum, 16); WRITEMEM(buf_p, md5sum, 16);
} }
@ -3045,13 +3121,19 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
filestatus_t ncs = FS_NOTFOUND; filestatus_t ncs = FS_NOTFOUND;
UINT8 md5sum[16]; UINT8 md5sum[16];
boolean kick = false; boolean kick = false;
boolean toomany = false;
INT32 i; INT32 i;
size_t packetsize = 0;
serverinfo_pak *dummycheck = NULL;
// Shut the compiler up.
(void)dummycheck;
READSTRINGN(*cp, filename, 240); READSTRINGN(*cp, filename, 240);
READMEM(*cp, md5sum, 16); READMEM(*cp, md5sum, 16);
// 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.
@ -3071,13 +3153,25 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
return; return;
} }
ncs = findfile(filename,md5sum,true); // See W_LoadWadFile in w_wad.c
for (i = 0; i < numwadfiles; i++)
packetsize += nameonlylength(wadfiles[i]->filename) + 22;
if (ncs != FS_FOUND) packetsize += nameonlylength(filename) + 22;
if ((numwadfiles >= MAX_WADFILES)
|| (packetsize > sizeof(dummycheck->fileneeded)))
toomany = true;
else
ncs = findfile(filename,md5sum,true);
if (ncs != FS_FOUND || toomany)
{ {
char message[256]; char message[256];
if (ncs == FS_NOTFOUND) if (toomany)
sprintf(message, M_GetText("Too many files loaded to add %s\n"), filename);
else if (ncs == FS_NOTFOUND)
sprintf(message, M_GetText("The server doesn't have %s\n"), filename); sprintf(message, M_GetText("The server doesn't have %s\n"), filename);
else if (ncs == FS_MD5SUMBAD) else if (ncs == FS_MD5SUMBAD)
sprintf(message, M_GetText("Checksum mismatch on %s\n"), filename); sprintf(message, M_GetText("Checksum mismatch on %s\n"), filename);
@ -3147,10 +3241,15 @@ static void Got_Addfilecmd(UINT8 **cp, INT32 playernum)
ncs = findfile(filename,md5sum,true); ncs = findfile(filename,md5sum,true);
if (ncs != FS_FOUND) if (ncs != FS_FOUND || !P_AddWadFile(filename, NULL))
{ {
Command_ExitGame_f(); Command_ExitGame_f();
if (ncs == FS_NOTFOUND) if (ncs == FS_FOUND)
{
CONS_Printf(M_GetText("The server tried to add %s,\nbut you have too many files added.\nRestart the game to clear loaded files\nand play on this server."), filename);
M_StartMessage(va("The server added a file \n(%s)\nbut you have too many files added.\nRestart the game to clear loaded files.\n\nPress ESC\n",filename), NULL, MM_NOTHING);
}
else if (ncs == FS_NOTFOUND)
{ {
CONS_Printf(M_GetText("The server tried to add %s,\nbut you don't have this file.\nYou need to find it in order\nto play on this server."), filename); CONS_Printf(M_GetText("The server tried to add %s,\nbut you don't have this file.\nYou need to find it in order\nto play on this server."), filename);
M_StartMessage(va("The server added a file \n(%s)\nthat you do not have.\n\nPress ESC\n",filename), NULL, MM_NOTHING); M_StartMessage(va("The server added a file \n(%s)\nthat you do not have.\n\nPress ESC\n",filename), NULL, MM_NOTHING);
@ -3168,7 +3267,6 @@ static void Got_Addfilecmd(UINT8 **cp, INT32 playernum)
return; return;
} }
P_AddWadFile(filename, NULL);
G_SetGameModified(true); G_SetGameModified(true);
} }
@ -3318,6 +3416,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 +4063,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++;
@ -4002,13 +4105,6 @@ static void Command_Archivetest_f(void)
*/ */
static void ForceSkin_OnChange(void) static void ForceSkin_OnChange(void)
{ {
if ((server || adminplayer == consoleplayer) && ((cv_forceskin.value == -1 && stricmp(cv_forceskin.string, "None")) || !(R_SkinUnlock(cv_forceskin.value))))
{
CONS_Printf("Please provide a valid skin name (\"None\" disables).\n");
CV_SetValue(&cv_forceskin, -1);
return;
}
// NOT in SP, silly! // NOT in SP, silly!
if (!(netgame || multiplayer)) if (!(netgame || multiplayer))
return; return;
@ -4017,7 +4113,7 @@ static void ForceSkin_OnChange(void)
CONS_Printf("The server has lifted the forced skin restrictions.\n"); CONS_Printf("The server has lifted the forced skin restrictions.\n");
else else
{ {
CONS_Printf("The server is restricting all players to skin \"%s\".\n",skins[cv_forceskin.value].realname); CONS_Printf("The server is restricting all players to skin \"%s\".\n",skins[cv_forceskin.value].name);
ForceAllSkins(cv_forceskin.value); ForceAllSkins(cv_forceskin.value);
} }
} }
@ -4056,7 +4152,7 @@ static void Skin_OnChange(void)
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)) && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y
{ {
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name); CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return; return;
@ -4099,8 +4195,7 @@ static void Color_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

@ -20,6 +20,12 @@
// console vars // console vars
extern consvar_t cv_playername; extern consvar_t cv_playername;
extern consvar_t cv_playercolor; extern consvar_t cv_playercolor;
extern consvar_t cv_skin;
// secondary splitscreen player
extern consvar_t cv_playername2;
extern consvar_t cv_playercolor2;
extern consvar_t cv_skin2;
#ifdef SEENAMES #ifdef SEENAMES
extern consvar_t cv_seenames, cv_allowseenames; extern consvar_t cv_seenames, cv_allowseenames;
#endif #endif
@ -32,7 +38,6 @@ extern consvar_t cv_joyport2;
#endif #endif
extern consvar_t cv_joyscale; extern consvar_t cv_joyscale;
extern consvar_t cv_joyscale2; extern consvar_t cv_joyscale2;
extern consvar_t cv_controlperkey;
// splitscreen with second mouse // splitscreen with second mouse
extern consvar_t cv_mouse2port; extern consvar_t cv_mouse2port;
@ -40,25 +45,12 @@ extern consvar_t cv_usemouse2;
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON)
extern consvar_t cv_mouse2opt; extern consvar_t cv_mouse2opt;
#endif #endif
extern consvar_t cv_invertmouse2;
extern consvar_t cv_alwaysfreelook2;
extern consvar_t cv_mousemove2;
extern consvar_t cv_mousesens2;
extern consvar_t cv_mouseysens2;
// normally in p_mobj but the .h is not read // normally in p_mobj but the .h is not read
extern consvar_t cv_itemrespawntime; extern consvar_t cv_itemrespawntime;
extern consvar_t cv_itemrespawn; extern consvar_t cv_itemrespawn;
extern consvar_t cv_flagtime; extern consvar_t cv_flagtime;
extern consvar_t cv_suddendeath;
extern consvar_t cv_skin;
// secondary splitscreen player
extern consvar_t cv_playername2;
extern consvar_t cv_playercolor2;
extern consvar_t cv_skin2;
extern consvar_t cv_touchtag; extern consvar_t cv_touchtag;
extern consvar_t cv_hidetime; extern consvar_t cv_hidetime;
@ -77,9 +69,6 @@ extern consvar_t cv_autobalance;
extern consvar_t cv_teamscramble; extern consvar_t cv_teamscramble;
extern consvar_t cv_scrambleonchange; extern consvar_t cv_scrambleonchange;
extern consvar_t cv_useranalog, cv_useranalog2;
extern consvar_t cv_analog, cv_analog2;
extern consvar_t cv_netstat; extern consvar_t cv_netstat;
#ifdef WALLSPLATS #ifdef WALLSPLATS
extern consvar_t cv_splats; extern consvar_t cv_splats;
@ -101,8 +90,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_overtime; extern consvar_t cv_overtime;
extern consvar_t cv_startinglives; extern consvar_t cv_startinglives;
@ -121,17 +108,7 @@ extern consvar_t cv_maxping;
extern consvar_t cv_skipmapcheck; extern consvar_t cv_skipmapcheck;
extern consvar_t cv_sleep, cv_screenshot_option, cv_screenshot_folder; extern consvar_t cv_sleep;
extern consvar_t cv_moviemode;
extern consvar_t cv_zlib_level, cv_zlib_memory, cv_zlib_strategy;
extern consvar_t cv_zlib_window_bits, cv_zlib_levela, cv_zlib_memorya;
extern consvar_t cv_zlib_strategya, cv_zlib_window_bitsa;
extern consvar_t cv_apng_delay;
typedef enum typedef enum
{ {
@ -162,6 +139,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
@ -210,7 +189,6 @@ void Command_ExitGame_f(void);
void Command_Retry_f(void); void Command_Retry_f(void);
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pultmode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect); void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pultmode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect);
void ObjectPlace_OnChange(void);
void ItemFinder_OnChange(void); void ItemFinder_OnChange(void);
void D_SetPassword(const char *pw); void D_SetPassword(const char *pw);

View file

@ -62,44 +62,49 @@
#include <errno.h> #include <errno.h>
static void SendFile(INT32 node, const char *filename, UINT8 fileid); // Prototypes
static boolean 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 +116,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 +149,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 +165,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 +181,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 +252,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)
{ {
@ -287,7 +304,8 @@ boolean CL_SendRequestFile(void)
} }
// get request filepak and put it on the send queue // get request filepak and put it on the send queue
void Got_RequestFilePak(INT32 node) // returns false if a requested file was not found or cannot be sent
boolean Got_RequestFilePak(INT32 node)
{ {
char wad[MAX_WADPATH+1]; char wad[MAX_WADPATH+1];
UINT8 *p = netbuffer->u.textcmd; UINT8 *p = netbuffer->u.textcmd;
@ -298,16 +316,33 @@ 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); if (!SV_SendFile(node, wad, id))
{
SV_AbortSendFiles(node);
return false; // don't read the rest of the files
}
} }
return true; // no problems with any files
} }
// 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;
char wadfilename[MAX_WADPATH]; char wadfilename[MAX_WADPATH];
INT32 ret = 1; INT32 ret = 1;
size_t packetsize = 0;
size_t filestoget = 0;
serverinfo_pak *dummycheck = NULL;
// Shut the compiler up.
(void)dummycheck;
// if (M_CheckParm("-nofiles")) // if (M_CheckParm("-nofiles"))
// return 1; // return 1;
@ -333,7 +368,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 +378,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;
@ -356,11 +391,15 @@ INT32 CL_CheckFiles(void)
return 1; return 1;
} }
// See W_LoadWadFile in w_wad.c
for (i = 0; i < numwadfiles; i++)
packetsize += nameonlylength(wadfiles[i]->filename) + 22;
for (i = 1; i < fileneedednum; i++) for (i = 1; i < fileneedednum; i++)
{ {
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));
@ -375,6 +414,14 @@ INT32 CL_CheckFiles(void)
if (fileneeded[i].status != FS_NOTFOUND || !fileneeded[i].important) if (fileneeded[i].status != FS_NOTFOUND || !fileneeded[i].important)
continue; continue;
packetsize += nameonlylength(fileneeded[i].filename) + 22;
if ((numwadfiles+filestoget >= MAX_WADFILES)
|| (packetsize > sizeof(dummycheck->fileneeded)))
return 3;
filestoget++;
fileneeded[i].status = findfile(fileneeded[i].filename, fileneeded[i].md5sum, true); fileneeded[i].status = findfile(fileneeded[i].filename, fileneeded[i].md5sum, true);
CONS_Debug(DBG_NETPLAY, "found %d\n", fileneeded[i].status); CONS_Debug(DBG_NETPLAY, "found %d\n", fileneeded[i].status);
if (fileneeded[i].status != FS_FOUND) if (fileneeded[i].status != FS_FOUND)
@ -383,7 +430,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 +441,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 +470,270 @@ 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 boolean 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 (%s)\n", filename, node, I_GetNodeAddress(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 false; // cancel the rest of the requests
} }
// 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 false; // cancel the rest of the requests
} }
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++;
return true;
} }
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 +741,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 +789,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 +884,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); boolean 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

@ -32,18 +32,21 @@
// Extra abilities/settings for skins (combinable stuff) // Extra abilities/settings for skins (combinable stuff)
typedef enum typedef enum
{ {
SF_SUPER = 1, // Can turn super in singleplayer/co-op mode. SF_SUPER = 1, // Can turn super in singleplayer/co-op mode?
SF_SUPERANIMS = 1<<1, // If super, use the super sonic animations SF_NOSUPERSPIN = 1<<1, // Should spin frames be played while super?
SF_SUPERSPIN = 1<<2, // Should spin frames be played while super? SF_NOSPINDASHDUST = 1<<2, // Spawn dust particles when charging a spindash?
SF_HIRES = 1<<3, // Draw the sprite 2x as small? SF_HIRES = 1<<3, // Draw the sprite at different size?
SF_NOSKID = 1<<4, // No skid particles etc SF_NOSKID = 1<<4, // No skid particles etc
SF_NOSPEEDADJUST = 1<<5, // Skin-specific version of disablespeedadjust SF_NOSPEEDADJUST = 1<<5, // Skin-specific version of disablespeedadjust
SF_RUNONWATER = 1<<6, // Run on top of water FOFs? SF_RUNONWATER = 1<<6, // Run on top of water FOFs?
SF_NOJUMPSPIN = 1<<7, // SPR2_JUMP defaults to SPR2_SPRG instead of SPR2_SPIN, falling states used, and player height is full when jumping? SF_NOJUMPSPIN = 1<<7, // SPR2_JUMP defaults to SPR2_SPRG instead of SPR2_ROLL, falling states used, and player height is full when jumping?
SF_NOJUMPDAMAGE = 1<<8, // Don't damage enemies, etc whilst jumping? SF_NOJUMPDAMAGE = 1<<8, // Don't damage enemies, etc whilst jumping?
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_DASHMODE = 1<<11, // Sonic Advance 2 style top speed increase?
SF_FASTEDGE = 1<<12, // Faster edge teeter?
SF_MULTIABILITY = 1<<13, // Revenge of Final Demo.
// free up to and including 1<<31 // free up to and including 1<<31
} skinflags_t; } skinflags_t;
@ -64,7 +67,7 @@ typedef enum
CA_JUMPBOOST, CA_JUMPBOOST,
CA_AIRDRILL, CA_AIRDRILL,
CA_JUMPTHOK, CA_JUMPTHOK,
CA_DASHMODE, CA_BOUNCE,
CA_TWINSPIN CA_TWINSPIN
} charability_t; } charability_t;
@ -73,7 +76,7 @@ typedef enum
{ {
CA2_NONE=0, CA2_NONE=0,
CA2_SPINDASH, CA2_SPINDASH,
CA2_MULTIABILITY, CA2_GUNSLINGER,
CA2_MELEE CA2_MELEE
} charability2_t; } charability2_t;
@ -117,10 +120,8 @@ typedef enum
// Did you get a time-over? // Did you get a time-over?
PF_TIMEOVER = 1<<10, PF_TIMEOVER = 1<<10,
// Ready for Super?
PF_SUPERREADY = 1<<11,
// Character action status // Character action status
PF_STARTJUMP = 1<<11,
PF_JUMPED = 1<<12, PF_JUMPED = 1<<12,
PF_SPINNING = 1<<13, PF_SPINNING = 1<<13,
PF_STARTDASH = 1<<14, PF_STARTDASH = 1<<14,
@ -132,12 +133,11 @@ typedef enum
// Sliding (usually in water) like Labyrinth/Oil Ocean // Sliding (usually in water) like Labyrinth/Oil Ocean
PF_SLIDING = 1<<17, PF_SLIDING = 1<<17,
/*** NIGHTS STUFF ***/ // Bouncing
// Is the player in NiGHTS mode? PF_BOUNCING = 1<<18,
PF_NIGHTSMODE = 1<<18,
PF_TRANSFERTOCLOSEST = 1<<19,
// Spill rings after falling /*** NIGHTS STUFF ***/
PF_TRANSFERTOCLOSEST = 1<<19,
PF_NIGHTSFALL = 1<<20, PF_NIGHTSFALL = 1<<20,
PF_DRILLING = 1<<21, PF_DRILLING = 1<<21,
PF_SKIDDOWN = 1<<22, PF_SKIDDOWN = 1<<22,
@ -151,9 +151,15 @@ 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,
// free up to and including 1<<31 // Used shield ability
PF_SHIELDABILITY = 1<<28,
// Jump damage?
PF_NOJUMPDAMAGE = 1<<29,
// up to 1<<31 is free
} pflags_t; } pflags_t;
typedef enum typedef enum
@ -164,7 +170,7 @@ typedef enum
PA_EDGE, PA_EDGE,
PA_WALK, PA_WALK,
PA_RUN, PA_RUN,
PA_PEEL, PA_DASH,
PA_PAIN, PA_PAIN,
PA_ROLL, PA_ROLL,
PA_JUMP, PA_JUMP,
@ -178,23 +184,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
@ -205,6 +222,10 @@ typedef enum
CR_GENERIC, CR_GENERIC,
// Tails carry. // Tails carry.
CR_PLAYER, CR_PLAYER,
// NiGHTS mode. Not technically a CARRYING, but doesn't stack with any of the others, so might as well go here.
CR_NIGHTSMODE,
// Old Brak sucks hard, but this gimmick could be used for something better, so we might as well continue supporting it.
CR_BRAKGOOP,
// Specific level gimmicks. // Specific level gimmicks.
CR_ZOOMTUBE, CR_ZOOMTUBE,
CR_ROPEHANG, CR_ROPEHANG,
@ -244,9 +265,7 @@ typedef enum
pw_nights_helper, pw_nights_helper,
pw_nights_linkfreeze, pw_nights_linkfreeze,
//for linedef exec 427 pw_nocontrol, //for linedef exec 427
pw_nocontrol,
pw_ingoop, // In goop
NUMPOWERS NUMPOWERS
} powertype_t; } powertype_t;
@ -297,10 +316,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.
@ -324,6 +341,7 @@ typedef struct player_s
UINT8 skincolor; UINT8 skincolor;
INT32 skin; INT32 skin;
UINT32 availabilities;
UINT32 score; // player score UINT32 score; // player score
fixed_t dashspeed; // dashing speed fixed_t dashspeed; // dashing speed
@ -361,8 +379,7 @@ 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 secondjump; // Jump counter
UINT8 secondjump;
UINT8 fly1; // Tails flying UINT8 fly1; // Tails flying
UINT8 scoreadd; // Used for multiple enemy attack bonus UINT8 scoreadd; // Used for multiple enemy attack bonus

File diff suppressed because it is too large Load diff

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

@ -90,6 +90,10 @@ static unsigned long nombre = NEWTICRATE*10;
static void I_BlitScreenVesa1(void); //see later static void I_BlitScreenVesa1(void); //see later
void I_FinishUpdate (void) void I_FinishUpdate (void)
{ {
// draw captions if enabled
if (cv_closedcaptioning.value)
SCR_ClosedCaptions();
// draw FPS if enabled // draw FPS if enabled
if (cv_ticrate.value) if (cv_ticrate.value)
SCR_DisplayTicRate(); SCR_DisplayTicRate();

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 24
// ========================================================================= // =========================================================================
@ -222,7 +222,7 @@ extern FILE *logstream;
// NOTE: it needs more than this to increase the number of players... // NOTE: it needs more than this to increase the number of players...
#define MAXPLAYERS 32 #define MAXPLAYERS 32
#define MAXSKINS MAXPLAYERS #define MAXSKINS 32
#define PLAYERSMASK (MAXPLAYERS-1) #define PLAYERSMASK (MAXPLAYERS-1)
#define MAXPLAYERNAME 21 #define MAXPLAYERNAME 21
@ -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)
@ -536,4 +540,14 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
/// \note You should leave this enabled unless you're working with a future SRB2 version. /// \note You should leave this enabled unless you're working with a future SRB2 version.
#define MUSICSLOT_COMPATIBILITY #define MUSICSLOT_COMPATIBILITY
/// Experimental attempts at preventing MF_PAPERCOLLISION objects from getting stuck in walls.
//#define PAPER_COLLISIONCORRECTION
/// Hudname padding.
#define SKINNAMEPADDING
/// Handle touching sector specials in P_PlayerAfterThink instead of P_PlayerThink.
/// \note Required for proper collision with moving sloped surfaces that have sector specials on them.
//#define SECTORSPECIALSAFTERTHINK
#endif // __DOOMDEF__ #endif // __DOOMDEF__

View file

@ -241,6 +241,12 @@ 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
char selectheading[22]; ///< Level select heading. Allows for controllable grouping.
// 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.
@ -263,6 +269,7 @@ typedef struct
#define LF2_RECORDATTACK 4 ///< Show this map in Time Attack #define LF2_RECORDATTACK 4 ///< Show this map in Time Attack
#define LF2_NIGHTSATTACK 8 ///< Show this map in NiGHTS mode menu #define LF2_NIGHTSATTACK 8 ///< Show this map in NiGHTS mode menu
#define LF2_NOVISITNEEDED 16 ///< Available in time attack/nights mode without visiting the level #define LF2_NOVISITNEEDED 16 ///< Available in time attack/nights mode without visiting the level
#define LF2_WIDEICON 32 ///< If you're in a circumstance where it fits, use a wide map icon
extern mapheader_t* mapheaderinfo[NUMMAPS]; extern mapheader_t* mapheaderinfo[NUMMAPS];
@ -307,7 +314,7 @@ enum GameType
NUMGAMETYPES NUMGAMETYPES
}; };
// If you alter this list, update gametype_cons_t in m_menu.c // If you alter this list, update dehacked.c, and gametype_cons_t and MISC_ChangeGameTypeMenu in m_menu.c
extern tic_t totalplaytime; extern tic_t totalplaytime;
@ -372,6 +379,7 @@ nightsdata_t ntemprecords;
extern UINT32 token; ///< Number of tokens collected in a level extern UINT32 token; ///< Number of tokens collected in a level
extern UINT32 tokenlist; ///< List of tokens collected extern UINT32 tokenlist; ///< List of tokens collected
extern boolean gottoken; ///< Did you get a token? Used for end of act
extern INT32 tokenbits; ///< Used for setting token bits extern INT32 tokenbits; ///< Used for setting token bits
extern INT32 sstimer; ///< Time allotted in the special stage extern INT32 sstimer; ///< Time allotted in the special stage
extern UINT32 bluescore; ///< Blue Team Scores extern UINT32 bluescore; ///< Blue Team Scores
@ -445,19 +453,17 @@ extern mapthing_t *redctfstarts[MAXPLAYERS]; // CTF
#if defined (macintosh) #if defined (macintosh)
#define DEBFILE(msg) I_OutputMsg(msg) #define DEBFILE(msg) I_OutputMsg(msg)
extern FILE *debugfile;
#else #else
#define DEBUGFILE #define DEBUGFILE
#ifdef DEBUGFILE #ifdef DEBUGFILE
#define DEBFILE(msg) { if (debugfile) { fputs(msg, debugfile); fflush(debugfile); } } #define DEBFILE(msg) { if (debugfile) { fputs(msg, debugfile); fflush(debugfile); } }
extern FILE *debugfile;
#else #else
#define DEBFILE(msg) {} #define DEBFILE(msg) {}
extern FILE *debugfile;
#endif #endif
#endif #endif
#ifdef DEBUGFILE #ifdef DEBUGFILE
extern FILE *debugfile;
extern INT32 debugload; extern INT32 debugload;
#endif #endif

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

@ -434,7 +434,6 @@ void F_StartIntro(void)
G_SetGamestate(GS_INTRO); G_SetGamestate(GS_INTRO);
gameaction = ga_nothing; gameaction = ga_nothing;
playerdeadview = false;
paused = false; paused = false;
CON_ToggleOff(); CON_ToggleOff();
CON_ClearHUD(); CON_ClearHUD();
@ -559,7 +558,7 @@ static void F_IntroDrawScene(void)
if (finalecount < 4) if (finalecount < 4)
S_StopMusic(); S_StopMusic();
if (finalecount == 4) if (finalecount == 4)
S_ChangeMusicInternal("stjr", false); S_ChangeMusicInternal("_stjr", false);
x = (BASEVIDWIDTH<<FRACBITS)/2 - FixedMul(334<<FRACBITS, aspect)/2; x = (BASEVIDWIDTH<<FRACBITS)/2 - FixedMul(334<<FRACBITS, aspect)/2;
y = (BASEVIDHEIGHT<<FRACBITS)/2 - FixedMul(358<<FRACBITS, aspect)/2; y = (BASEVIDHEIGHT<<FRACBITS)/2 - FixedMul(358<<FRACBITS, aspect)/2;
V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH1", PU_CACHE)), aspect); V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH1", PU_CACHE)), aspect);
@ -771,7 +770,7 @@ void F_IntroDrawer(void)
F_RunWipe(99,true); F_RunWipe(99,true);
} }
S_ChangeMusicInternal("read_m", false); S_ChangeMusicInternal("_intro", false);
} }
else if (intro_scenenum == 3) else if (intro_scenenum == 3)
roidtics = BASEVIDWIDTH - 64; roidtics = BASEVIDWIDTH - 64;
@ -974,7 +973,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 +985,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 +1021,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",
@ -1124,13 +1124,12 @@ void F_StartCredits(void)
} }
gameaction = ga_nothing; gameaction = ga_nothing;
playerdeadview = false;
paused = false; paused = false;
CON_ToggleOff(); CON_ToggleOff();
CON_ClearHUD(); CON_ClearHUD();
S_StopMusic(); S_StopMusic();
S_ChangeMusicInternal("credit", false); S_ChangeMusicInternal("_creds", false);
finalecount = 0; finalecount = 0;
animtimer = 0; animtimer = 0;
@ -1271,7 +1270,6 @@ void F_StartGameEvaluation(void)
G_SaveGame((UINT32)cursaveslot); G_SaveGame((UINT32)cursaveslot);
gameaction = ga_nothing; gameaction = ga_nothing;
playerdeadview = false;
paused = false; paused = false;
CON_ToggleOff(); CON_ToggleOff();
CON_ClearHUD(); CON_ClearHUD();
@ -1382,7 +1380,6 @@ void F_StartGameEnd(void)
G_SetGamestate(GS_GAMEEND); G_SetGamestate(GS_GAMEEND);
gameaction = ga_nothing; gameaction = ga_nothing;
playerdeadview = false;
paused = false; paused = false;
CON_ToggleOff(); CON_ToggleOff();
CON_ClearHUD(); CON_ClearHUD();
@ -1427,7 +1424,7 @@ void F_StartTitleScreen(void)
// IWAD dependent stuff. // IWAD dependent stuff.
S_ChangeMusicInternal("titles", looptitle); S_ChangeMusicInternal("_title", looptitle);
animtimer = 0; animtimer = 0;
@ -1585,7 +1582,6 @@ void F_StartContinue(void)
gameaction = ga_nothing; gameaction = ga_nothing;
keypressed = false; keypressed = false;
playerdeadview = false;
paused = false; paused = false;
CON_ToggleOff(); CON_ToggleOff();
CON_ClearHUD(); CON_ClearHUD();
@ -1593,7 +1589,7 @@ void F_StartContinue(void)
// In case menus are still up?!! // In case menus are still up?!!
M_ClearMenus(true); M_ClearMenus(true);
S_ChangeMusicInternal("contsc", false); S_ChangeMusicInternal("_conti", false);
S_StopSounds(); S_StopSounds();
timetonext = TICRATE*11; timetonext = TICRATE*11;
@ -1727,7 +1723,7 @@ static void F_AdvanceToNextScene(void)
void F_EndCutScene(void) void F_EndCutScene(void)
{ {
cutsceneover = true; // do this first, just in case Y_EndGame or something wants to turn it back false later cutsceneover = true; // do this first, just in case G_EndGame or something wants to turn it back false later
if (runningprecutscene) if (runningprecutscene)
{ {
if (server) if (server)
@ -1742,7 +1738,7 @@ void F_EndCutScene(void)
else if (nextmap < 1100-1) else if (nextmap < 1100-1)
G_NextLevel(); G_NextLevel();
else else
Y_EndGame(); G_EndGame();
} }
} }
@ -1754,7 +1750,6 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
G_SetGamestate(GS_CUTSCENE); G_SetGamestate(GS_CUTSCENE);
gameaction = ga_nothing; gameaction = ga_nothing;
playerdeadview = false;
paused = false; paused = false;
CON_ToggleOff(); CON_ToggleOff();

View file

@ -86,7 +86,7 @@ INT32 lastwipetic = 0;
static UINT8 *wipe_scr_start; //screen 3 static UINT8 *wipe_scr_start; //screen 3
static UINT8 *wipe_scr_end; //screen 4 static UINT8 *wipe_scr_end; //screen 4
static UINT8 *wipe_scr; //screen 0 (main drawing) static UINT8 *wipe_scr; //screen 0 (main drawing)
static fixed_t paldiv; static fixed_t paldiv = 0;
/** Create fademask_t from lump /** Create fademask_t from lump
* *
@ -145,7 +145,7 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
while (lsize--) while (lsize--)
{ {
// Determine pixel to use from fademask // Determine pixel to use from fademask
pcolor = &pLocalPalette[*lump++]; pcolor = &pMasterPalette[*lump++];
*mask++ = FixedDiv((pcolor->s.red+1)<<FRACBITS, paldiv)>>FRACBITS; *mask++ = FixedDiv((pcolor->s.red+1)<<FRACBITS, paldiv)>>FRACBITS;
} }
@ -337,7 +337,8 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
UINT8 wipeframe = 0; UINT8 wipeframe = 0;
fademask_t *fmask; fademask_t *fmask;
paldiv = FixedDiv(257<<FRACBITS, 11<<FRACBITS); if (!paldiv)
paldiv = FixedDiv(257<<FRACBITS, 11<<FRACBITS);
// Init the wipe // Init the wipe
WipeInAction = true; WipeInAction = true;

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;
@ -155,6 +156,7 @@ UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage.
UINT16 emeralds; UINT16 emeralds;
UINT32 token; // Number of tokens collected in a level UINT32 token; // Number of tokens collected in a level
UINT32 tokenlist; // List of tokens collected UINT32 tokenlist; // List of tokens collected
boolean gottoken; // Did you get a token? Used for end of act
INT32 tokenbits; // Used for setting token bits INT32 tokenbits; // Used for setting token bits
// Old Special Stage // Old Special Stage
@ -696,8 +698,7 @@ void G_SetNightsRecords(void)
free(gpath); free(gpath);
// If the mare count changed, this will update the score display // If the mare count changed, this will update the score display
CV_AddValue(&cv_nextmap, 1); Nextmap_OnChange();
CV_AddValue(&cv_nextmap, -1);
} }
// for consistency among messages: this modifies the game and removes savemoddata. // for consistency among messages: this modifies the game and removes savemoddata.
@ -1011,14 +1012,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
if (turnleft) if (turnleft)
cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]); cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]);
} }
if (cv_analog.value || twodlevel if (twodlevel
|| (player->mo && (player->mo->flags2 & MF2_TWOD)) || (player->mo && (player->mo->flags2 & MF2_TWOD))
|| (!demoplayback && (player->climbing || (!demoplayback && (player->climbing
|| (player->pflags & PF_NIGHTSMODE) || (player->powers[pw_carry] == CR_NIGHTSMODE)
|| (player->pflags & PF_SLIDING) || (player->pflags & (PF_SLIDING|PF_FORCESTRAFE))))) // Analog
|| (player->pflags & PF_FORCESTRAFE)))) // Analog
forcestrafe = true; forcestrafe = true;
if (forcestrafe) // Analog if (forcestrafe)
{ {
if (turnright) if (turnright)
side += sidemove[speed]; side += sidemove[speed];
@ -1031,6 +1031,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
side += ((axis * sidemove[1]) >> 10); side += ((axis * sidemove[1]) >> 10);
} }
} }
else if (cv_analog.value) // Analog
{
if (turnright)
cmd->buttons |= BT_CAMRIGHT;
if (turnleft)
cmd->buttons |= BT_CAMLEFT;
}
else else
{ {
if (turnright) if (turnright)
@ -1118,15 +1125,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
if (PLAYER1INPUTDOWN(gc_use)) if (PLAYER1INPUTDOWN(gc_use))
cmd->buttons |= BT_USE; cmd->buttons |= BT_USE;
// Camera Controls
if (cv_debug || cv_analog.value || demoplayback || objectplacing || player->pflags & PF_NIGHTSMODE)
{
if (PLAYER1INPUTDOWN(gc_camleft))
cmd->buttons |= BT_CAMLEFT;
if (PLAYER1INPUTDOWN(gc_camright))
cmd->buttons |= BT_CAMRIGHT;
}
if (PLAYER1INPUTDOWN(gc_camreset)) if (PLAYER1INPUTDOWN(gc_camreset))
{ {
if (camera.chase && !resetdown) if (camera.chase && !resetdown)
@ -1188,10 +1186,19 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
if (!mouseaiming && cv_mousemove.value) if (!mouseaiming && cv_mousemove.value)
forward += mousey; forward += mousey;
if (cv_analog.value || if ((!demoplayback && (player->climbing
(!demoplayback && (player->climbing
|| (player->pflags & PF_SLIDING)))) // Analog for mouse || (player->pflags & PF_SLIDING)))) // Analog for mouse
side += mousex*2; side += mousex*2;
else if (cv_analog.value)
{
if (mousex)
{
if (mousex > 0)
cmd->buttons |= BT_CAMRIGHT;
else
cmd->buttons |= BT_CAMLEFT;
}
}
else else
cmd->angleturn = (INT16)(cmd->angleturn - (mousex*8)); cmd->angleturn = (INT16)(cmd->angleturn - (mousex*8));
@ -1226,9 +1233,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
cmd->sidemove = (SINT8)(cmd->sidemove + side); cmd->sidemove = (SINT8)(cmd->sidemove + side);
if (cv_analog.value) { if (cv_analog.value) {
cmd->angleturn = (INT16)(thiscam->angle >> 16);
if (player->awayviewtics) if (player->awayviewtics)
cmd->angleturn = (INT16)(player->awayviewmobj->angle >> 16); cmd->angleturn = (INT16)(player->awayviewmobj->angle >> 16);
else
cmd->angleturn = (INT16)(thiscam->angle >> 16);
} }
else else
{ {
@ -1302,12 +1310,11 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
if (turnleft) if (turnleft)
cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]); cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]);
} }
if (cv_analog2.value || twodlevel if (twodlevel
|| (player->mo && (player->mo->flags2 & MF2_TWOD)) || (player->mo && (player->mo->flags2 & MF2_TWOD))
|| player->climbing || player->climbing
|| (player->pflags & PF_NIGHTSMODE) || (player->powers[pw_carry] == CR_NIGHTSMODE)
|| (player->pflags & PF_SLIDING) || (player->pflags & (PF_SLIDING|PF_FORCESTRAFE))) // Analog
|| (player->pflags & PF_FORCESTRAFE)) // Analog
forcestrafe = true; forcestrafe = true;
if (forcestrafe) // Analog if (forcestrafe) // Analog
{ {
@ -1322,6 +1329,13 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
side += ((axis * sidemove[1]) >> 10); side += ((axis * sidemove[1]) >> 10);
} }
} }
else if (cv_analog2.value) // Analog
{
if (turnright)
cmd->buttons |= BT_CAMRIGHT;
if (turnleft)
cmd->buttons |= BT_CAMLEFT;
}
else else
{ {
if (turnright) if (turnright)
@ -1406,15 +1420,6 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
if (PLAYER2INPUTDOWN(gc_use)) if (PLAYER2INPUTDOWN(gc_use))
cmd->buttons |= BT_USE; cmd->buttons |= BT_USE;
// Camera Controls
if (cv_debug || cv_analog2.value || player->pflags & PF_NIGHTSMODE)
{
if (PLAYER2INPUTDOWN(gc_camleft))
cmd->buttons |= BT_CAMLEFT;
if (PLAYER2INPUTDOWN(gc_camright))
cmd->buttons |= BT_CAMRIGHT;
}
if (PLAYER2INPUTDOWN(gc_camreset)) if (PLAYER2INPUTDOWN(gc_camreset))
{ {
if (camera2.chase && !resetdown) if (camera2.chase && !resetdown)
@ -1476,9 +1481,19 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
if (!mouseaiming && cv_mousemove2.value) if (!mouseaiming && cv_mousemove2.value)
forward += mouse2y; forward += mouse2y;
if (cv_analog2.value || player->climbing if (player->climbing
|| (player->pflags & PF_SLIDING)) // Analog for mouse || (player->pflags & PF_SLIDING)) // Analog for mouse
side += mouse2x*2; side += mouse2x*2;
else if (cv_analog2.value)
{
if (mouse2x)
{
if (mouse2x > 0)
cmd->buttons |= BT_CAMRIGHT;
else
cmd->buttons |= BT_CAMLEFT;
}
}
else else
cmd->angleturn = (INT16)(cmd->angleturn - (mouse2x*8)); cmd->angleturn = (INT16)(cmd->angleturn - (mouse2x*8));
@ -1526,9 +1541,10 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
} }
if (cv_analog2.value) { if (cv_analog2.value) {
cmd->angleturn = (INT16)(thiscam->angle >> 16);
if (player->awayviewtics) if (player->awayviewtics)
cmd->angleturn = (INT16)(player->awayviewmobj->angle >> 16); cmd->angleturn = (INT16)(player->awayviewmobj->angle >> 16);
else
cmd->angleturn = (INT16)(thiscam->angle >> 16);
} }
else else
{ {
@ -1565,11 +1581,6 @@ static void Analog_OnChange(void)
// cameras are not initialized at this point // cameras are not initialized at this point
if (leveltime > 1)
CV_SetValue(&cv_cam_dist, 128);
if (cv_analog.value || demoplayback)
CV_SetValue(&cv_cam_dist, 192);
if (!cv_chasecam.value && cv_analog.value) { if (!cv_chasecam.value && cv_analog.value) {
CV_SetValue(&cv_analog, 0); CV_SetValue(&cv_analog, 0);
return; return;
@ -1590,11 +1601,6 @@ static void Analog2_OnChange(void)
// cameras are not initialized at this point // cameras are not initialized at this point
if (leveltime > 1)
CV_SetValue(&cv_cam2_dist, 128);
if (cv_analog2.value)
CV_SetValue(&cv_cam2_dist, 192);
if (!cv_chasecam2.value && cv_analog2.value) { if (!cv_chasecam2.value && cv_analog2.value) {
CV_SetValue(&cv_analog2, 0); CV_SetValue(&cv_analog2, 0);
return; return;
@ -2081,6 +2087,7 @@ void G_PlayerReborn(INT32 player)
UINT8 mare; UINT8 mare;
UINT8 skincolor; UINT8 skincolor;
INT32 skin; INT32 skin;
UINT32 availabilities;
tic_t jointime; tic_t jointime;
boolean spectator; boolean spectator;
INT16 bot; INT16 bot;
@ -2105,6 +2112,7 @@ void G_PlayerReborn(INT32 player)
skincolor = players[player].skincolor; skincolor = players[player].skincolor;
skin = players[player].skin; skin = players[player].skin;
availabilities = players[player].availabilities;
camerascale = players[player].camerascale; camerascale = players[player].camerascale;
shieldscale = players[player].shieldscale; shieldscale = players[player].shieldscale;
charability = players[player].charability; charability = players[player].charability;
@ -2150,6 +2158,7 @@ void G_PlayerReborn(INT32 player)
// save player config truth reborn // save player config truth reborn
p->skincolor = skincolor; p->skincolor = skincolor;
p->skin = skin; p->skin = skin;
p->availabilities = availabilities;
p->camerascale = camerascale; p->camerascale = camerascale;
p->shieldscale = shieldscale; p->shieldscale = shieldscale;
p->charability = charability; p->charability = charability;
@ -2193,7 +2202,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)
@ -2300,6 +2309,9 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost)
if (starpost) //Don't even bother with looking for a place to spawn. if (starpost) //Don't even bother with looking for a place to spawn.
{ {
P_MovePlayerToStarpost(playernum); P_MovePlayerToStarpost(playernum);
#ifdef HAVE_BLUA
LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :)
#endif
return; return;
} }
@ -2624,7 +2636,7 @@ void G_ExitLevel(void)
CONS_Printf(M_GetText("The round has ended.\n")); CONS_Printf(M_GetText("The round has ended.\n"));
// Remove CEcho text on round end. // Remove CEcho text on round end.
HU_DoCEcho(""); HU_ClearCEcho();
} }
} }
@ -2772,7 +2784,6 @@ static INT16 RandMap(INT16 tolflags, INT16 pprevmap)
static void G_DoCompleted(void) static void G_DoCompleted(void)
{ {
INT32 i; INT32 i;
boolean gottoken = false;
tokenlist = 0; // Reset the list tokenlist = 0; // Reset the list
@ -2858,10 +2869,9 @@ static void G_DoCompleted(void)
if (nextmap >= 1100-1 && nextmap <= 1102-1 && (gametype == GT_RACE || gametype == GT_COMPETITION)) if (nextmap >= 1100-1 && nextmap <= 1102-1 && (gametype == GT_RACE || gametype == GT_COMPETITION))
nextmap = (INT16)(spstage_start-1); nextmap = (INT16)(spstage_start-1);
if (gametype == GT_COOP && token) if ((gottoken = (gametype == GT_COOP && token)))
{ {
token--; token--;
gottoken = true;
if (!(emeralds & EMERALD1)) if (!(emeralds & EMERALD1))
nextmap = (INT16)(sstage_start - 1); // Special Stage 1 nextmap = (INT16)(sstage_start - 1); // Special Stage 1
@ -2900,7 +2910,7 @@ static void G_DoCompleted(void)
if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) if (nextmap < NUMMAPS && !mapheaderinfo[nextmap])
P_AllocMapHeader(nextmap); P_AllocMapHeader(nextmap);
if (skipstats) if (skipstats && !modeattacking) // Don't skip stats if we're in record attack
G_AfterIntermission(); G_AfterIntermission();
else else
{ {
@ -2920,7 +2930,7 @@ void G_AfterIntermission(void)
if (nextmap < 1100-1) if (nextmap < 1100-1)
G_NextLevel(); G_NextLevel();
else else
Y_EndGame(); G_EndGame();
} }
} }
@ -3006,6 +3016,38 @@ static void G_DoContinued(void)
gameaction = ga_nothing; gameaction = ga_nothing;
} }
//
// G_EndGame (formerly Y_EndGame)
// Frankly this function fits better in g_game.c than it does in y_inter.c
//
// ...Gee, (why) end the game?
// Because G_AfterIntermission and F_EndCutscene would
// both do this exact same thing *in different ways* otherwise,
// which made it so that you could only unlock Ultimate mode
// if you had a cutscene after the final level and crap like that.
// This function simplifies it so only one place has to be updated
// when something new is added.
void G_EndGame(void)
{
// Only do evaluation and credits in coop games.
if (gametype == GT_COOP)
{
if (nextmap == 1102-1) // end game with credits
{
F_StartCredits();
return;
}
if (nextmap == 1101-1) // end game with evaluation
{
F_StartGameEvaluation();
return;
}
}
// 1100 or competitive multiplayer, so go back to title screen.
D_StartTitle();
}
// //
// G_LoadGameSettings // G_LoadGameSettings
// //
@ -3540,7 +3582,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b
// This is the map command interpretation something like Command_Map_f // This is the map command interpretation something like Command_Map_f
// //
// called at: map cmd execution, doloadgame, doplaydemo // called at: map cmd execution, doloadgame, doplaydemo
void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean skipprecutscene) void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS)
{ {
INT32 i; INT32 i;
@ -3570,7 +3612,8 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
if (netgame || multiplayer) if (netgame || multiplayer)
{ {
players[i].lives = cv_startinglives.value; if (!FLS || (players[i].lives < cv_startinglives.value))
players[i].lives = cv_startinglives.value;
players[i].continues = 0; players[i].continues = 0;
} }
else if (pultmode) else if (pultmode)
@ -3584,13 +3627,16 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
players[i].continues = 1; players[i].continues = 1;
} }
if (!((netgame || multiplayer) && (FLS)))
players[i].score = 0;
// The latter two should clear by themselves, but just in case // The latter two should clear by themselves, but just in case
players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS); players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS);
// Clear cheatcodes too, just in case. // Clear cheatcodes too, just in case.
players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS); players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS);
players[i].score = players[i].xtralife = 0; players[i].xtralife = 0;
} }
// Reset unlockable triggers // Reset unlockable triggers
@ -3624,7 +3670,6 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
mapmusflags |= MUSIC_RELOADRESET; mapmusflags |= MUSIC_RELOADRESET;
ultimatemode = pultmode; ultimatemode = pultmode;
playerdeadview = false;
automapactive = false; automapactive = false;
imcontinuing = false; imcontinuing = false;
@ -3652,6 +3697,9 @@ char *G_BuildMapTitle(INT32 mapnum)
{ {
char *title = NULL; char *title = NULL;
if (!mapheaderinfo[mapnum-1])
P_AllocMapHeader(mapnum-1);
if (strcmp(mapheaderinfo[mapnum-1]->lvlttl, "")) if (strcmp(mapheaderinfo[mapnum-1]->lvlttl, ""))
{ {
size_t len = 1; size_t len = 1;
@ -3885,7 +3933,7 @@ void G_GhostAddColor(ghostcolor_t color)
ghostext.color = (UINT8)color; ghostext.color = (UINT8)color;
} }
void G_GhostAddScale(UINT16 scale) void G_GhostAddScale(fixed_t scale)
{ {
if (!demorecording || !(demoflags & DF_GHOST)) if (!demorecording || !(demoflags & DF_GHOST))
return; return;
@ -3919,12 +3967,8 @@ void G_WriteGhostTic(mobj_t *ghost)
if (!(demoflags & DF_GHOST)) if (!(demoflags & DF_GHOST))
return; // No ghost data to write. return; // No ghost data to write.
if (ghost->player && ghost->player->pflags & PF_NIGHTSMODE && ghost->tracer) if (ghost->player && ghost->player->powers[pw_carry] == CR_NIGHTSMODE) // We're talking about the NiGHTS thing, not the normal platforming thing!
{
// We're talking about the NiGHTS thing, not the normal platforming thing!
ziptic |= GZT_NIGHTS; ziptic |= GZT_NIGHTS;
ghost = ghost->tracer;
}
ziptic_p = demo_p++; // the ziptic, written at the end of this function ziptic_p = demo_p++; // the ziptic, written at the end of this function
@ -4106,11 +4150,9 @@ void G_ConsGhostTic(void)
demo_p++; demo_p++;
if (ziptic & GZT_SPR2) if (ziptic & GZT_SPR2)
demo_p++; demo_p++;
if(ziptic & GZT_NIGHTS) { if (ziptic & GZT_NIGHTS) {
if (!testmo->player || !(testmo->player->pflags & PF_NIGHTSMODE) || !testmo->tracer) if (!testmo->player || !(testmo->player->powers[pw_carry] == CR_NIGHTSMODE))
nightsfail = true; nightsfail = true;
else
testmo = testmo->tracer;
} }
if (ziptic & GZT_EXTRA) if (ziptic & GZT_EXTRA)
@ -4727,7 +4769,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));
@ -5141,7 +5183,7 @@ void G_DoPlayDemo(char *defdemoname)
memset(playeringame,0,sizeof(playeringame)); memset(playeringame,0,sizeof(playeringame));
playeringame[0] = true; playeringame[0] = true;
P_SetRandSeed(randseed); P_SetRandSeed(randseed);
G_InitNew(false, G_BuildMapName(gamemap), true, true); G_InitNew(false, G_BuildMapName(gamemap), true, true, false);
// Set skin // Set skin
SetPlayerSkin(0, skin); SetPlayerSkin(0, skin);
@ -5621,7 +5663,7 @@ boolean G_CheckDemoStatus(void)
WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file. md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file.
#endif #endif
saved = FIL_WriteFile(demoname, demobuffer, demo_p - demobuffer); // finally output the file. saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
free(demobuffer); free(demobuffer);
demorecording = false; demorecording = false;

View file

@ -56,6 +56,9 @@ extern INT16 rw_maximums[NUM_WEAPONS];
// used in game menu // used in game menu
extern consvar_t cv_crosshair, cv_crosshair2; extern consvar_t cv_crosshair, cv_crosshair2;
extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_mousemove; extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_mousemove;
extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_mousemove2;
extern consvar_t cv_useranalog, cv_useranalog2;
extern consvar_t cv_analog, cv_analog2;
extern consvar_t cv_sideaxis,cv_turnaxis,cv_moveaxis,cv_lookaxis,cv_fireaxis,cv_firenaxis; extern consvar_t cv_sideaxis,cv_turnaxis,cv_moveaxis,cv_lookaxis,cv_fireaxis,cv_firenaxis;
extern consvar_t cv_sideaxis2,cv_turnaxis2,cv_moveaxis2,cv_lookaxis2,cv_fireaxis2,cv_firenaxis2; extern consvar_t cv_sideaxis2,cv_turnaxis2,cv_moveaxis2,cv_lookaxis2,cv_fireaxis2,cv_firenaxis2;
extern consvar_t cv_ghost_bestscore, cv_ghost_besttime, cv_ghost_bestrings, cv_ghost_last, cv_ghost_guest; extern consvar_t cv_ghost_bestscore, cv_ghost_besttime, cv_ghost_bestrings, cv_ghost_last, cv_ghost_guest;
@ -89,7 +92,7 @@ void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo);
void G_DoReborn(INT32 playernum); void G_DoReborn(INT32 playernum);
void G_PlayerReborn(INT32 player); void G_PlayerReborn(INT32 player);
void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer,
boolean skipprecutscene); boolean skipprecutscene, boolean FLS);
char *G_BuildMapTitle(INT32 mapnum); char *G_BuildMapTitle(INT32 mapnum);
// XMOD spawning // XMOD spawning
@ -139,7 +142,7 @@ void G_GhostAddSpin(void);
void G_GhostAddRev(void); void G_GhostAddRev(void);
void G_GhostAddColor(ghostcolor_t color); void G_GhostAddColor(ghostcolor_t color);
void G_GhostAddFlip(void); void G_GhostAddFlip(void);
void G_GhostAddScale(UINT16 scale); void G_GhostAddScale(fixed_t scale);
void G_GhostAddHit(mobj_t *victim); void G_GhostAddHit(mobj_t *victim);
void G_WriteGhostTic(mobj_t *ghost); void G_WriteGhostTic(mobj_t *ghost);
void G_ConsGhostTic(void); void G_ConsGhostTic(void);
@ -171,6 +174,7 @@ void G_NextLevel(void);
void G_Continue(void); void G_Continue(void);
void G_UseContinue(void); void G_UseContinue(void);
void G_AfterIntermission(void); void G_AfterIntermission(void);
void G_EndGame(void); // moved from y_inter.c/h and renamed
void G_Ticker(boolean run); void G_Ticker(boolean run);
boolean G_Responder(event_t *ev); boolean G_Responder(event_t *ev);

View file

@ -25,10 +25,10 @@ static CV_PossibleValue_t mousesens_cons_t[] = {{1, "MIN"}, {MAXMOUSESENSITIVITY
static CV_PossibleValue_t onecontrolperkey_cons_t[] = {{1, "One"}, {2, "Several"}, {0, NULL}}; static CV_PossibleValue_t onecontrolperkey_cons_t[] = {{1, "One"}, {2, "Several"}, {0, NULL}};
// mouse values are used once // mouse values are used once
consvar_t cv_mousesens = {"mousesens", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_mousesens = {"mousesens", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mousesens2 = {"mousesens2", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_mousesens2 = {"mousesens2", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mouseysens = {"mouseysens", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_mouseysens = {"mouseysens", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mouseysens2 = {"mouseysens2", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_mouseysens2 = {"mouseysens2", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_controlperkey = {"controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_controlperkey = {"controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
INT32 mousex, mousey; INT32 mousex, mousey;
@ -977,8 +977,6 @@ static const char *gamecontrolname[num_gamecontrols] =
"tossflag", "tossflag",
"use", "use",
"camtoggle", "camtoggle",
"camleft",
"camright",
"camreset", "camreset",
"lookup", "lookup",
"lookdown", "lookdown",
@ -1074,8 +1072,6 @@ void G_Controldefault(void)
gamecontrol[gc_use ][0] = KEY_JOY1+1; //B gamecontrol[gc_use ][0] = KEY_JOY1+1; //B
gamecontrol[gc_use ][1] = '.'; gamecontrol[gc_use ][1] = '.';
gamecontrol[gc_camtoggle ][1] = ','; gamecontrol[gc_camtoggle ][1] = ',';
gamecontrol[gc_camleft ][0] = 'o';
gamecontrol[gc_camright ][0] = 'p';
gamecontrol[gc_camreset ][0] = 'c'; gamecontrol[gc_camreset ][0] = 'c';
gamecontrol[gc_lookup ][0] = KEY_PGUP; gamecontrol[gc_lookup ][0] = KEY_PGUP;
gamecontrol[gc_lookdown ][0] = KEY_PGDN; gamecontrol[gc_lookdown ][0] = KEY_PGDN;
@ -1154,10 +1150,8 @@ void G_Controldefault(void)
#else #else
void G_Controldefault(void) void G_Controldefault(void)
{ {
gamecontrol[gc_forward ][0] = KEY_UPARROW; gamecontrol[gc_forward ][0] = 'w';
gamecontrol[gc_forward ][1] = 'w'; gamecontrol[gc_backward ][0] = 's';
gamecontrol[gc_backward ][0] = KEY_DOWNARROW;
gamecontrol[gc_backward ][1] = 's';
gamecontrol[gc_strafeleft ][0] = 'a'; gamecontrol[gc_strafeleft ][0] = 'a';
gamecontrol[gc_straferight][0] = 'd'; gamecontrol[gc_straferight][0] = 'd';
gamecontrol[gc_turnleft ][0] = KEY_LEFTARROW; gamecontrol[gc_turnleft ][0] = KEY_LEFTARROW;
@ -1178,19 +1172,16 @@ void G_Controldefault(void)
gamecontrol[gc_fire ][1] = KEY_MOUSE1+0; gamecontrol[gc_fire ][1] = KEY_MOUSE1+0;
gamecontrol[gc_firenormal ][0] = 'c'; gamecontrol[gc_firenormal ][0] = 'c';
gamecontrol[gc_tossflag ][0] = '\''; gamecontrol[gc_tossflag ][0] = '\'';
gamecontrol[gc_use ][0] = 'x'; gamecontrol[gc_use ][0] = KEY_LSHIFT;
gamecontrol[gc_camtoggle ][0] = 'v'; gamecontrol[gc_camtoggle ][0] = 'v';
gamecontrol[gc_camleft ][0] = '[';
gamecontrol[gc_camright ][0] = ']';
gamecontrol[gc_camreset ][0] = 'r'; gamecontrol[gc_camreset ][0] = 'r';
gamecontrol[gc_lookup ][0] = KEY_PGUP; gamecontrol[gc_lookup ][0] = KEY_UPARROW;
gamecontrol[gc_lookdown ][0] = KEY_PGDN; gamecontrol[gc_lookdown ][0] = KEY_DOWNARROW;
gamecontrol[gc_centerview ][0] = KEY_END; gamecontrol[gc_centerview ][0] = KEY_END;
gamecontrol[gc_talkkey ][0] = 't'; gamecontrol[gc_talkkey ][0] = 't';
gamecontrol[gc_teamkey ][0] = 'y'; gamecontrol[gc_teamkey ][0] = 'y';
gamecontrol[gc_scores ][0] = KEY_TAB; gamecontrol[gc_scores ][0] = KEY_TAB;
gamecontrol[gc_jump ][0] = 'z'; gamecontrol[gc_jump ][0] = KEY_SPACE;
gamecontrol[gc_jump ][1] = KEY_MOUSE1+1;
gamecontrol[gc_console ][0] = KEY_CONSOLE; gamecontrol[gc_console ][0] = KEY_CONSOLE;
gamecontrol[gc_pause ][0] = KEY_PAUSE; gamecontrol[gc_pause ][0] = KEY_PAUSE;
#ifdef WMINPUT #ifdef WMINPUT

View file

@ -105,8 +105,6 @@ typedef enum
gc_tossflag, gc_tossflag,
gc_use, gc_use,
gc_camtoggle, gc_camtoggle,
gc_camleft,
gc_camright,
gc_camreset, gc_camreset,
gc_lookup, gc_lookup,
gc_lookdown, gc_lookdown,
@ -126,6 +124,8 @@ typedef enum
// mouse values are used once // mouse values are used once
extern consvar_t cv_mousesens, cv_mouseysens; extern consvar_t cv_mousesens, cv_mouseysens;
extern consvar_t cv_mousesens2, cv_mouseysens2;
extern consvar_t cv_controlperkey;
extern INT32 mousex, mousey; extern INT32 mousex, mousey;
extern INT32 mlooky; //mousey with mlookSensitivity extern INT32 mlooky; //mousey with mlookSensitivity

View file

@ -564,8 +564,6 @@ static inline void HWR_SubsecPoly(INT32 num, poly_t *poly)
subsector_t *sub; subsector_t *sub;
seg_t *lseg; seg_t *lseg;
sscount++;
sub = &subsectors[num]; sub = &subsectors[num];
count = sub->numlines; count = sub->numlines;
lseg = &segs[sub->firstline]; lseg = &segs[sub->firstline];

View file

@ -152,7 +152,9 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale); float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale);
float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale); float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale);
if (alphalevel >= 10 && alphalevel < 13) if (alphalevel == 12)
alphalevel = 0;
else if (alphalevel >= 10 && alphalevel < 13)
return; return;
// make patch ready in hardware cache // make patch ready in hardware cache
@ -252,7 +254,9 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale); float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale);
float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale); float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale);
if (alphalevel >= 10 && alphalevel < 13) if (alphalevel == 12)
alphalevel = 0;
else if (alphalevel >= 10 && alphalevel < 13)
return; return;
// make patch ready in hardware cache // make patch ready in hardware cache
@ -656,6 +660,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 +669,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; //;
@ -778,7 +789,7 @@ boolean HWR_Screenshot(const char *lbmname)
HWD.pfnReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf); HWD.pfnReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf);
#ifdef USE_PNG #ifdef USE_PNG
ret = M_SavePNG(lbmname, buf, vid.width, vid.height, NULL); ret = M_SavePNG(lbmname, buf, vid.width, vid.height, false);
#else #else
ret = saveTGA(lbmname, buf, vid.width, vid.height); ret = saveTGA(lbmname, buf, vid.width, vid.height);
#endif #endif

View file

@ -78,6 +78,7 @@ typedef struct gr_vissprite_s
//Hurdler: 25/04/2000: now support colormap in hardware mode //Hurdler: 25/04/2000: now support colormap in hardware mode
UINT8 *colormap; UINT8 *colormap;
INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing
float z1, z2;
} gr_vissprite_t; } gr_vissprite_t;
// -------- // --------

View file

@ -247,27 +247,33 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_BMNE &lspr[NOLIGHT], // SPR_BMNE
// Monitor Boxes // Monitor Boxes
&lspr[NOLIGHT], // SPR_SRBX &lspr[NOLIGHT], // SPR_MSTV
&lspr[NOLIGHT], // SPR_RRBX &lspr[NOLIGHT], // SPR_XLTV
&lspr[NOLIGHT], // SPR_BRBX
&lspr[NOLIGHT], // SPR_SHTV
&lspr[NOLIGHT], // SPR_PINV
&lspr[NOLIGHT], // SPR_YLTV
&lspr[NOLIGHT], // SPR_BLTV
&lspr[NOLIGHT], // SPR_BKTV
&lspr[NOLIGHT], // SPR_WHTV
&lspr[NOLIGHT], // SPR_GRTV
&lspr[NOLIGHT], // SPR_ELTV
&lspr[NOLIGHT], // SPR_EGGB
&lspr[NOLIGHT], // SPR_MIXU
&lspr[NOLIGHT], // SPR_RECY
&lspr[NOLIGHT], // SPR_QUES
&lspr[NOLIGHT], // SPR_GBTV
&lspr[NOLIGHT], // SPR_PRUP
&lspr[NOLIGHT], // SPR_PTTV
// Monitor Miscellany &lspr[NOLIGHT], // SPR_TRRI
&lspr[NOLIGHT], // SPR_MTEX &lspr[NOLIGHT], // SPR_TBRI
&lspr[NOLIGHT], // SPR_TVRI
&lspr[NOLIGHT], // SPR_TVPI
&lspr[NOLIGHT], // SPR_TVAT
&lspr[NOLIGHT], // SPR_TVFO
&lspr[NOLIGHT], // SPR_TVAR
&lspr[NOLIGHT], // SPR_TVWW
&lspr[NOLIGHT], // SPR_TVEL
&lspr[NOLIGHT], // SPR_TVSS
&lspr[NOLIGHT], // SPR_TVIV
&lspr[NOLIGHT], // SPR_TV1U
&lspr[NOLIGHT], // SPR_TV1P
&lspr[NOLIGHT], // SPR_TVEG
&lspr[NOLIGHT], // SPR_TVMX
&lspr[NOLIGHT], // SPR_TVMY
&lspr[NOLIGHT], // SPR_TVGV
&lspr[NOLIGHT], // SPR_TVRC
&lspr[NOLIGHT], // SPR_TV1K
&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
@ -290,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
@ -356,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
@ -383,7 +404,7 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_SPLA &lspr[NOLIGHT], // SPR_SPLA
&lspr[NOLIGHT], // SPR_SMOK &lspr[NOLIGHT], // SPR_SMOK
&lspr[NOLIGHT], // SPR_BUBL &lspr[NOLIGHT], // SPR_BUBL
&lspr[SUPERSPARK_L], // SPR_WZAP &lspr[RINGLIGHT_L], // SPR_WZAP
&lspr[SUPERSPARK_L], // SPR_TFOG &lspr[SUPERSPARK_L], // SPR_TFOG
&lspr[NIGHTSLIGHT_L], // SPR_SEED // Sonic CD flower seed &lspr[NIGHTSLIGHT_L], // SPR_SEED // Sonic CD flower seed
&lspr[NOLIGHT], // SPR_PRTL &lspr[NOLIGHT], // SPR_PRTL
@ -391,9 +412,12 @@ light_t *t_lspr[NUMSPRITES] =
// Game Indicators // Game Indicators
&lspr[NOLIGHT], // SPR_SCOR &lspr[NOLIGHT], // SPR_SCOR
&lspr[NOLIGHT], // SPR_DRWN &lspr[NOLIGHT], // SPR_DRWN
&lspr[NOLIGHT], // SPR_LCKN
&lspr[NOLIGHT], // SPR_TTAG &lspr[NOLIGHT], // SPR_TTAG
&lspr[NOLIGHT], // SPR_GFLG &lspr[NOLIGHT], // SPR_GFLG
&lspr[NOLIGHT], // SPR_CORK
// Ring Weapons // Ring Weapons
&lspr[RINGLIGHT_L], // SPR_RRNG &lspr[RINGLIGHT_L], // SPR_RRNG
&lspr[RINGLIGHT_L], // SPR_RNGB &lspr[RINGLIGHT_L], // SPR_RNGB
@ -473,23 +497,6 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_GWLG &lspr[NOLIGHT], // SPR_GWLG
&lspr[NOLIGHT], // SPR_GWLR &lspr[NOLIGHT], // SPR_GWLR
// SRB1 Sprites
&lspr[NOLIGHT], // SPR_SRBA
&lspr[NOLIGHT], // SPR_SRBB
&lspr[NOLIGHT], // SPR_SRBC
&lspr[NOLIGHT], // SPR_SRBD
&lspr[NOLIGHT], // SPR_SRBE
&lspr[NOLIGHT], // SPR_SRBF
&lspr[NOLIGHT], // SPR_SRBG
&lspr[NOLIGHT], // SPR_SRBH
&lspr[NOLIGHT], // SPR_SRBI
&lspr[NOLIGHT], // SPR_SRBJ
&lspr[NOLIGHT], // SPR_SRBK
&lspr[NOLIGHT], // SPR_SRBL
&lspr[NOLIGHT], // SPR_SRBM
&lspr[NOLIGHT], // SPR_SRBN
&lspr[NOLIGHT], // SPR_SRBO
// Free slots // Free slots
&lspr[NOLIGHT], &lspr[NOLIGHT],
&lspr[NOLIGHT], &lspr[NOLIGHT],

View file

@ -45,7 +45,7 @@
#include "hw_md2.h" #include "hw_md2.h"
#define R_FAKEFLOORS #define R_FAKEFLOORS
//#define HWPRECIP #define HWPRECIP
#define SORTING #define SORTING
//#define POLYSKY //#define POLYSKY
@ -1084,9 +1084,9 @@ static void HWR_SplitWall(sector_t *sector, wallVert3D *wallVerts, INT32 texnum,
float endheight = 0.0f, endbheight = 0.0f; float endheight = 0.0f, endbheight = 0.0f;
fixed_t v1x = FLOAT_TO_FIXED(wallVerts[0].x); fixed_t v1x = FLOAT_TO_FIXED(wallVerts[0].x);
fixed_t v1y = FLOAT_TO_FIXED(wallVerts[0].y); fixed_t v1y = FLOAT_TO_FIXED(wallVerts[0].z); // not a typo
fixed_t v2x = FLOAT_TO_FIXED(wallVerts[1].x); fixed_t v2x = FLOAT_TO_FIXED(wallVerts[1].x);
fixed_t v2y = FLOAT_TO_FIXED(wallVerts[1].y); fixed_t v2y = FLOAT_TO_FIXED(wallVerts[1].z); // not a typo
// compiler complains when P_GetZAt is used in FLOAT_TO_FIXED directly // compiler complains when P_GetZAt is used in FLOAT_TO_FIXED directly
// use this as a temp var to store P_GetZAt's return value each time // use this as a temp var to store P_GetZAt's return value each time
fixed_t temp; fixed_t temp;
@ -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;
@ -3363,7 +3367,6 @@ static void HWR_Subsector(size_t num)
if (num < numsubsectors) if (num < numsubsectors)
{ {
sscount++;
// subsector // subsector
sub = &subsectors[num]; sub = &subsectors[num];
// sector // sector
@ -3718,6 +3721,9 @@ static void HWR_Subsector(size_t num)
while (count--) while (count--)
{ {
#ifdef POLYOBJECTS
if (!line->polyseg) // ignore segs that belong to polyobjects
#endif
HWR_AddLine(line); HWR_AddLine(line);
line++; line++;
} }
@ -4223,6 +4229,7 @@ static void HWR_DrawSprite(gr_vissprite_t *spr)
GLPatch_t *gpatch; // sprite patch converted to hardware GLPatch_t *gpatch; // sprite patch converted to hardware
FSurfaceInfo Surf; FSurfaceInfo Surf;
const boolean hires = (spr->mobj && spr->mobj->skin && ((skin_t *)spr->mobj->skin)->flags & SF_HIRES); const boolean hires = (spr->mobj && spr->mobj->skin && ((skin_t *)spr->mobj->skin)->flags & SF_HIRES);
//const boolean papersprite = (spr->mobj && (spr->mobj->frame & FF_PAPERSPRITE));
if (spr->mobj) if (spr->mobj)
this_scale = FIXED_TO_FLOAT(spr->mobj->scale); this_scale = FIXED_TO_FLOAT(spr->mobj->scale);
if (hires) if (hires)
@ -4266,7 +4273,8 @@ static void HWR_DrawSprite(gr_vissprite_t *spr)
// make a wall polygon (with 2 triangles), using the floor/ceiling heights, // make a wall polygon (with 2 triangles), using the floor/ceiling heights,
// and the 2d map coords of start/end vertices // and the 2d map coords of start/end vertices
wallVerts[0].z = wallVerts[1].z = wallVerts[2].z = wallVerts[3].z = spr->tz; wallVerts[0].z = wallVerts[3].z = spr->z1;
wallVerts[2].z = wallVerts[1].z = spr->z2;
// transform // transform
wv = wallVerts; wv = wallVerts;
@ -4401,7 +4409,6 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr)
FOutVector *wv; FOutVector *wv;
GLPatch_t *gpatch; // sprite patch converted to hardware GLPatch_t *gpatch; // sprite patch converted to hardware
FSurfaceInfo Surf; FSurfaceInfo Surf;
sector_t *sector;
if (!spr->mobj) if (!spr->mobj)
return; return;
@ -4455,19 +4462,38 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr)
//Hurdler: 25/04/2000: now support colormap in hardware mode //Hurdler: 25/04/2000: now support colormap in hardware mode
HWR_GetMappedPatch(gpatch, spr->colormap); HWR_GetMappedPatch(gpatch, spr->colormap);
sector = spr->mobj->subsector->sector; // colormap test
if (sector->ffloors)
{ {
ffloor_t *caster = sector->lightlist[R_GetPlaneLight(sector, spr->mobj->z, false)].caster; sector_t *sector = spr->mobj->subsector->sector;
sector = caster ? &sectors[caster->secnum] : sector; UINT8 lightlevel = 255;
} extracolormap_t *colormap = sector->extra_colormap;
// sprite lighting by modulating the RGB components if (sector->numlights)
if (sector->extra_colormap) {
Surf.FlatColor.rgba = HWR_Lighting(spr->sectorlight,sector->extra_colormap->rgba,sector->extra_colormap->fadergba, false, false); INT32 light;
light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before
if (!(spr->mobj->frame & FF_FULLBRIGHT))
lightlevel = *sector->lightlist[light].lightlevel;
if (sector->lightlist[light].extra_colormap)
colormap = sector->lightlist[light].extra_colormap;
}
else else
Surf.FlatColor.rgba = HWR_Lighting(spr->sectorlight,NORMALFOG,FADEFOG, false, false); {
if (!(spr->mobj->frame & FF_FULLBRIGHT))
lightlevel = sector->lightlevel;
if (sector->extra_colormap)
colormap = sector->extra_colormap;
}
if (colormap)
Surf.FlatColor.rgba = HWR_Lighting(lightlevel, colormap->rgba, colormap->fadergba, false, false);
else
Surf.FlatColor.rgba = HWR_Lighting(lightlevel, NORMALFOG, FADEFOG, false, false);
}
if (spr->mobj->flags2 & MF2_SHADOW) if (spr->mobj->flags2 & MF2_SHADOW)
{ {
@ -4501,8 +4527,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;
@ -5040,8 +5066,14 @@ static void HWR_ProjectSprite(mobj_t *thing)
size_t lumpoff; size_t lumpoff;
unsigned rot; unsigned rot;
UINT8 flip; UINT8 flip;
boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
angle_t ang; angle_t ang;
INT32 heightsec, phs; INT32 heightsec, phs;
const boolean papersprite = (thing->frame & FF_PAPERSPRITE);
float offset;
float ang_scale = 1.0f, ang_scalez = 0.0f;
float z1, z2;
if (!thing) if (!thing)
return; return;
@ -5056,7 +5088,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
tz = (tr_x * gr_viewcos) + (tr_y * gr_viewsin); tz = (tr_x * gr_viewcos) + (tr_y * gr_viewsin);
// thing is behind view plane? // thing is behind view plane?
if (tz < ZCLIP_PLANE && (!cv_grmd2.value || md2_models[thing->sprite].notfound == true)) //Yellow: Only MD2's dont disappear if (tz < ZCLIP_PLANE && !papersprite && (!cv_grmd2.value || md2_models[thing->sprite].notfound == true)) //Yellow: Only MD2's dont disappear
return; return;
tx = (tr_x * gr_viewsin) - (tr_y * gr_viewcos); tx = (tr_x * gr_viewsin) - (tr_y * gr_viewcos);
@ -5094,6 +5126,27 @@ static void HWR_ProjectSprite(mobj_t *thing)
I_Error("sprframes NULL for sprite %d\n", thing->sprite); I_Error("sprframes NULL for sprite %d\n", thing->sprite);
#endif #endif
if (papersprite)
{
// Use the actual view angle, rather than the angle formed
// between the view point and the thing
// this makes sure paper sprites always appear at the right angle!
// Note: DO NOT do this in software mode version, it actually
// makes papersprites look WORSE there (I know, I've tried)
// Monster Iestyn - 13/05/17
ang = dup_viewangle - thing->angle;
ang_scale = FIXED_TO_FLOAT(FINESINE(ang>>ANGLETOFINESHIFT));
ang_scalez = FIXED_TO_FLOAT(FINECOSINE(ang>>ANGLETOFINESHIFT));
if (ang_scale < 0)
{
ang_scale = -ang_scale;
ang_scalez = -ang_scalez;
}
}
else if (sprframe->rotate != SRF_SINGLE)
ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
if (sprframe->rotate == SRF_SINGLE) if (sprframe->rotate == SRF_SINGLE)
{ {
// use single rotation for all views // use single rotation for all views
@ -5104,8 +5157,6 @@ static void HWR_ProjectSprite(mobj_t *thing)
else else
{ {
// choose a different rotation based on player view // choose a different rotation based on player view
ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right
rot = 6; // F7 slot rot = 6; // F7 slot
else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left
@ -5123,9 +5174,12 @@ static void HWR_ProjectSprite(mobj_t *thing)
// calculate edges of the shape // calculate edges of the shape
if (flip) if (flip)
tx -= FIXED_TO_FLOAT(spritecachedinfo[lumpoff].width - spritecachedinfo[lumpoff].offset) * this_scale; offset = FIXED_TO_FLOAT(spritecachedinfo[lumpoff].width - spritecachedinfo[lumpoff].offset) * this_scale;
else else
tx -= FIXED_TO_FLOAT(spritecachedinfo[lumpoff].offset) * this_scale; offset = FIXED_TO_FLOAT(spritecachedinfo[lumpoff].offset) * this_scale;
z1 = tz - (offset * ang_scalez);
tx -= offset * ang_scale;
// project x // project x
x1 = gr_windowcenterx + (tx * gr_centerx / tz); x1 = gr_windowcenterx + (tx * gr_centerx / tz);
@ -5136,10 +5190,17 @@ static void HWR_ProjectSprite(mobj_t *thing)
x1 = tx; x1 = tx;
tx += FIXED_TO_FLOAT(spritecachedinfo[lumpoff].width) * this_scale; offset = FIXED_TO_FLOAT(spritecachedinfo[lumpoff].width) * this_scale;
z2 = z1 + (offset * ang_scalez);
tx += offset * ang_scale;
if (papersprite && max(z1, z2) < ZCLIP_PLANE)
return;
x2 = gr_windowcenterx + (tx * gr_centerx / tz); x2 = gr_windowcenterx + (tx * gr_centerx / tz);
if (thing->eflags & MFE_VERTICALFLIP) if (vflip)
{ {
gz = FIXED_TO_FLOAT(thing->z+thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; gz = FIXED_TO_FLOAT(thing->z+thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale;
gzt = gz + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale; gzt = gz + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale;
@ -5185,6 +5246,8 @@ static void HWR_ProjectSprite(mobj_t *thing)
vis->patchlumpnum = sprframe->lumppat[rot]; vis->patchlumpnum = sprframe->lumppat[rot];
vis->flip = flip; vis->flip = flip;
vis->mobj = thing; vis->mobj = thing;
vis->z1 = z1;
vis->z2 = z2;
//Hurdler: 25/04/2000: now support colormap in hardware mode //Hurdler: 25/04/2000: now support colormap in hardware mode
if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash" if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash"
@ -5216,10 +5279,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
//CONS_Debug(DBG_RENDER, "------------------\nH: sprite : %d\nH: frame : %x\nH: type : %d\nH: sname : %s\n\n", //CONS_Debug(DBG_RENDER, "------------------\nH: sprite : %d\nH: frame : %x\nH: type : %d\nH: sname : %s\n\n",
// thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]); // thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]);
if (thing->eflags & MFE_VERTICALFLIP) vis->vflip = vflip;
vis->vflip = true;
else
vis->vflip = false;
vis->precip = false; vis->precip = false;
} }
@ -5296,6 +5356,11 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
// //
vis = HWR_NewVisSprite(); vis = HWR_NewVisSprite();
vis->x1 = x1; vis->x1 = x1;
#if 0
vis->x2 = x2;
#else
(void)x2;
#endif
vis->x2 = tx; vis->x2 = tx;
vis->tz = tz; vis->tz = tz;
vis->dispoffset = 0; // Monster Iestyn: 23/11/15: HARDWARE SUPPORT AT LAST vis->dispoffset = 0; // Monster Iestyn: 23/11/15: HARDWARE SUPPORT AT LAST
@ -5308,7 +5373,6 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
// set top/bottom coords // set top/bottom coords
vis->ty = FIXED_TO_FLOAT(thing->z + spritecachedinfo[lumpoff].topoffset) - gr_viewz; vis->ty = FIXED_TO_FLOAT(thing->z + spritecachedinfo[lumpoff].topoffset) - gr_viewz;
vis->sectorlight = 0xff;
vis->precip = true; vis->precip = true;
} }
#endif #endif
@ -5330,7 +5394,7 @@ static void HWR_DrawSkyBackground(player_t *player)
// 0--1 // 0--1
(void)player; (void)player;
HWR_GetTexture(skytexture); HWR_GetTexture(texturetranslation[skytexture]);
//Hurdler: the sky is the only texture who need 4.0f instead of 1.0 //Hurdler: the sky is the only texture who need 4.0f instead of 1.0
// because it's called just after clearing the screen // because it's called just after clearing the screen
@ -5350,7 +5414,7 @@ static void HWR_DrawSkyBackground(player_t *player)
angle = (dup_viewangle + gr_xtoviewangle[0]); angle = (dup_viewangle + gr_xtoviewangle[0]);
dimensionmultiply = ((float)textures[skytexture]->width/256.0f); dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f);
v[0].sow = v[3].sow = ((float) angle / ((ANGLE_90-1)*dimensionmultiply)); v[0].sow = v[3].sow = ((float) angle / ((ANGLE_90-1)*dimensionmultiply));
v[2].sow = v[1].sow = (-1.0f/dimensionmultiply)+((float) angle / ((ANGLE_90-1)*dimensionmultiply)); v[2].sow = v[1].sow = (-1.0f/dimensionmultiply)+((float) angle / ((ANGLE_90-1)*dimensionmultiply));
@ -5359,7 +5423,7 @@ static void HWR_DrawSkyBackground(player_t *player)
angle = aimingangle; angle = aimingangle;
aspectratio = (float)vid.width/(float)vid.height; aspectratio = (float)vid.width/(float)vid.height;
dimensionmultiply = ((float)textures[skytexture]->height/(128.0f*aspectratio)); dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio));
angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply; angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply;
// Middle of the sky should always be at angle 0 // Middle of the sky should always be at angle 0

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
@ -1349,7 +1364,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
UINT32 durs = spr->mobj->state->tics; UINT32 durs = spr->mobj->state->tics;
UINT32 tics = spr->mobj->tics; UINT32 tics = spr->mobj->tics;
md2_frame_t *curr, *next = NULL; md2_frame_t *curr, *next = NULL;
const UINT8 flip = (UINT8)((spr->mobj->eflags & MFE_VERTICALFLIP) == MFE_VERTICALFLIP); const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !(spr->mobj->frame & FF_VERTICALFLIP));
spritedef_t *sprdef; spritedef_t *sprdef;
spriteframe_t *sprframe; spriteframe_t *sprframe;
float finalscale; float finalscale;
@ -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;
} }
} }
@ -1464,7 +1482,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
p.x = FIXED_TO_FLOAT(spr->mobj->x); p.x = FIXED_TO_FLOAT(spr->mobj->x);
p.y = FIXED_TO_FLOAT(spr->mobj->y)+md2->offset; p.y = FIXED_TO_FLOAT(spr->mobj->y)+md2->offset;
if (spr->mobj->eflags & MFE_VERTICALFLIP) if (flip)
p.z = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height); p.z = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height);
else else
p.z = FIXED_TO_FLOAT(spr->mobj->z); p.z = FIXED_TO_FLOAT(spr->mobj->z);

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

@ -244,6 +244,7 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...)
#define pglMaterialfv glMaterialfv #define pglMaterialfv glMaterialfv
/* Raster functions */ /* Raster functions */
#define pglPixelStorei glPixelStorei
#define pglReadPixels glReadPixels #define pglReadPixels glReadPixels
/* Texture mapping */ /* Texture mapping */
@ -262,15 +263,8 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...)
/* texture mapping */ //GL_EXT_copy_texture /* texture mapping */ //GL_EXT_copy_texture
#ifndef KOS_GL_COMPATIBILITY #ifndef KOS_GL_COMPATIBILITY
#define pglCopyTexImage2D glCopyTexImage2D #define pglCopyTexImage2D glCopyTexImage2D
#endif
/* GLU functions */
#define pgluBuild2DMipmaps gluBuild2DMipmaps
#endif
#ifndef MINI_GL_COMPATIBILITY
/* 1.3 functions for multitexturing */
#define pglActiveTexture glActiveTexture
#define pglMultiTexCoord2f glMultiTexCoord2f
#endif
#else //!STATIC_OPENGL #else //!STATIC_OPENGL
/* 1.0 functions */ /* 1.0 functions */
@ -365,6 +359,8 @@ typedef void (APIENTRY * PFNglMaterialfv) (GLint face, GLenum pname, GLfloat *pa
static PFNglMaterialfv pglMaterialfv; static PFNglMaterialfv pglMaterialfv;
/* Raster functions */ /* Raster functions */
typedef void (APIENTRY * PFNglPixelStorei) (GLenum pname, GLint param);
static PFNglPixelStorei pglPixelStorei;
typedef void (APIENTRY * PFNglReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); typedef void (APIENTRY * PFNglReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
static PFNglReadPixels pglReadPixels; static PFNglReadPixels pglReadPixels;
@ -391,7 +387,7 @@ static PFNglBindTexture pglBindTexture;
/* texture mapping */ //GL_EXT_copy_texture /* texture mapping */ //GL_EXT_copy_texture
typedef void (APIENTRY * PFNglCopyTexImage2D) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); typedef void (APIENTRY * PFNglCopyTexImage2D) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
static PFNglCopyTexImage2D pglCopyTexImage2D; static PFNglCopyTexImage2D pglCopyTexImage2D;
#endif
/* GLU functions */ /* GLU functions */
typedef GLint (APIENTRY * PFNgluBuild2DMipmaps) (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data); typedef GLint (APIENTRY * PFNgluBuild2DMipmaps) (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data);
static PFNgluBuild2DMipmaps pgluBuild2DMipmaps; static PFNgluBuild2DMipmaps pgluBuild2DMipmaps;
@ -403,7 +399,6 @@ static PFNglActiveTexture pglActiveTexture;
typedef void (APIENTRY *PFNglMultiTexCoord2f) (GLenum, GLfloat, GLfloat); typedef void (APIENTRY *PFNglMultiTexCoord2f) (GLenum, GLfloat, GLfloat);
static PFNglMultiTexCoord2f pglMultiTexCoord2f; static PFNglMultiTexCoord2f pglMultiTexCoord2f;
#endif #endif
#endif
#ifndef MINI_GL_COMPATIBILITY #ifndef MINI_GL_COMPATIBILITY
/* 1.2 Parms */ /* 1.2 Parms */
@ -494,6 +489,7 @@ boolean SetupGLfunc(void)
GETOPENGLFUNC(pglLightModelfv , glLightModelfv) GETOPENGLFUNC(pglLightModelfv , glLightModelfv)
GETOPENGLFUNC(pglMaterialfv , glMaterialfv) GETOPENGLFUNC(pglMaterialfv , glMaterialfv)
GETOPENGLFUNC(pglPixelStorei , glPixelStorei)
GETOPENGLFUNC(pglReadPixels , glReadPixels) GETOPENGLFUNC(pglReadPixels , glReadPixels)
GETOPENGLFUNC(pglTexEnvi , glTexEnvi) GETOPENGLFUNC(pglTexEnvi , glTexEnvi)
@ -519,35 +515,23 @@ boolean SetupGLfunc(void)
// This has to be done after the context is created so the version number can be obtained // This has to be done after the context is created so the version number can be obtained
boolean SetupGLFunc13(void) boolean SetupGLFunc13(void)
{ {
#ifdef MINI_GL_COMPATIBILITY
return false;
#else
const GLubyte *version = pglGetString(GL_VERSION); const GLubyte *version = pglGetString(GL_VERSION);
int glmajor, glminor; int glmajor, glminor;
gl13 = false; gl13 = false;
#ifdef MINI_GL_COMPATIBILITY
return false;
#else
#ifdef STATIC_OPENGL
gl13 = true;
#else
// Parse the GL version // Parse the GL version
if (version != NULL) if (version != NULL)
{ {
if (sscanf((const char*)version, "%d.%d", &glmajor, &glminor) == 2) if (sscanf((const char*)version, "%d.%d", &glmajor, &glminor) == 2)
{ {
// Look, we gotta prepare for the inevitable arrival of GL 2.0 code... // Look, we gotta prepare for the inevitable arrival of GL 2.0 code...
switch (glmajor) if (glmajor == 1 && glminor >= 3)
{ gl13 = true;
case 1: else if (glmajor > 1)
if (glminor == 3) gl13 = true; gl13 = true;
break;
case 2:
case 3:
case 4:
gl13 = true;
default:
break;
}
} }
} }
@ -568,9 +552,6 @@ boolean SetupGLFunc13(void)
} }
else else
DBG_Printf("GL_ARB_multitexture support: disabled\n"); DBG_Printf("GL_ARB_multitexture support: disabled\n");
#undef GETOPENGLFUNC
#endif
return true; return true;
#endif #endif
} }
@ -897,7 +878,9 @@ EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height,
GLubyte*top = (GLvoid*)dst_data, *bottom = top + dst_stride * (height - 1); GLubyte*top = (GLvoid*)dst_data, *bottom = top + dst_stride * (height - 1);
GLubyte *row = malloc(dst_stride); GLubyte *row = malloc(dst_stride);
if (!row) return; if (!row) return;
pglPixelStorei(GL_PACK_ALIGNMENT, 1);
pglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, dst_data); pglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, dst_data);
pglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for(i = 0; i < height/2; i++) for(i = 0; i < height/2; i++)
{ {
memcpy(row, top, dst_stride); memcpy(row, top, dst_stride);
@ -913,7 +896,9 @@ EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height,
INT32 j; INT32 j;
GLubyte *image = malloc(width*height*3*sizeof (*image)); GLubyte *image = malloc(width*height*3*sizeof (*image));
if (!image) return; if (!image) return;
pglPixelStorei(GL_PACK_ALIGNMENT, 1);
pglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, image); pglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, image);
pglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (i = height-1; i >= 0; i--) for (i = height-1; i >= 0; i--)
{ {
for (j = 0; j < width; j++) for (j = 0; j < width; j++)
@ -1815,13 +1800,11 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value)
min_filter = GL_NEAREST; min_filter = GL_NEAREST;
#endif #endif
} }
#ifndef STATIC_OPENGL
if (!pgluBuild2DMipmaps) if (!pgluBuild2DMipmaps)
{ {
MipMap = GL_FALSE; MipMap = GL_FALSE;
min_filter = GL_LINEAR; min_filter = GL_LINEAR;
} }
#endif
Flush(); //??? if we want to change filter mode by texture, remove this Flush(); //??? if we want to change filter mode by texture, remove this
break; break;
@ -1836,7 +1819,7 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value)
} }
} }
static inline void DrawMD2Ex(INT32 *gl_cmd_buffer, md2_frame_t *frame, UINT32 duration, UINT32 tics, md2_frame_t *nextframe, FTransform *pos, float scale, UINT8 flipped, UINT8 *color) static void DrawMD2Ex(INT32 *gl_cmd_buffer, md2_frame_t *frame, UINT32 duration, UINT32 tics, md2_frame_t *nextframe, FTransform *pos, float scale, UINT8 flipped, UINT8 *color)
{ {
INT32 val, count, pindex; INT32 val, count, pindex;
GLfloat s, t; GLfloat s, t;

View file

@ -91,7 +91,7 @@ patch_t *tallminus;
patch_t *emeraldpics[7]; patch_t *emeraldpics[7];
patch_t *tinyemeraldpics[7]; patch_t *tinyemeraldpics[7];
static patch_t *emblemicon; static patch_t *emblemicon;
static patch_t *tokenicon; patch_t *tokenicon;
//------------------------------------------- //-------------------------------------------
// misc vars // misc vars
@ -198,21 +198,6 @@ void HU_LoadGraphics(void)
tny_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); tny_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
} }
// cache the level title font for entire game execution
lt_font[0] = (patch_t *)W_CachePatchName("LTFNT039", PU_HUDGFX); /// \note fake start hack
// Number support
lt_font[9] = (patch_t *)W_CachePatchName("LTFNT048", PU_HUDGFX);
lt_font[10] = (patch_t *)W_CachePatchName("LTFNT049", PU_HUDGFX);
lt_font[11] = (patch_t *)W_CachePatchName("LTFNT050", PU_HUDGFX);
lt_font[12] = (patch_t *)W_CachePatchName("LTFNT051", PU_HUDGFX);
lt_font[13] = (patch_t *)W_CachePatchName("LTFNT052", PU_HUDGFX);
lt_font[14] = (patch_t *)W_CachePatchName("LTFNT053", PU_HUDGFX);
lt_font[15] = (patch_t *)W_CachePatchName("LTFNT054", PU_HUDGFX);
lt_font[16] = (patch_t *)W_CachePatchName("LTFNT055", PU_HUDGFX);
lt_font[17] = (patch_t *)W_CachePatchName("LTFNT056", PU_HUDGFX);
lt_font[18] = (patch_t *)W_CachePatchName("LTFNT057", PU_HUDGFX);
j = LT_FONTSTART; j = LT_FONTSTART;
for (i = 0; i < LT_FONTSIZE; i++) for (i = 0; i < LT_FONTSIZE; i++)
{ {
@ -470,7 +455,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 +742,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 +775,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
@ -854,7 +840,7 @@ static void HU_DrawChat(void)
else else
{ {
//charwidth = SHORT(hu_font[talk[i]-HU_FONTSTART]->width) * con_scalefactor; //charwidth = SHORT(hu_font[talk[i]-HU_FONTSTART]->width) * con_scalefactor;
V_DrawCharacter(HU_INPUTX + c, y, talk[i++] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); V_DrawCharacter(HU_INPUTX + c, y, talk[i++] | cv_constextsize.value | V_NOSCALESTART, true);
} }
c += charwidth; c += charwidth;
} }
@ -871,7 +857,7 @@ static void HU_DrawChat(void)
else else
{ {
//charwidth = SHORT(hu_font[w_chat[i]-HU_FONTSTART]->width) * con_scalefactor; //charwidth = SHORT(hu_font[w_chat[i]-HU_FONTSTART]->width) * con_scalefactor;
V_DrawCharacter(HU_INPUTX + c, y, w_chat[i++] | cv_constextsize.value | V_NOSCALESTART | t, !cv_allcaps.value); V_DrawCharacter(HU_INPUTX + c, y, w_chat[i++] | cv_constextsize.value | V_NOSCALESTART | t, true);
} }
c += charwidth; c += charwidth;
@ -883,7 +869,7 @@ static void HU_DrawChat(void)
} }
if (hu_tick < 4) if (hu_tick < 4)
V_DrawCharacter(HU_INPUTX + c, y, '_' | cv_constextsize.value |V_NOSCALESTART|t, !cv_allcaps.value); V_DrawCharacter(HU_INPUTX + c, y, '_' | cv_constextsize.value |V_NOSCALESTART|t, true);
} }
@ -1101,7 +1087,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 +1196,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 +1206,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 +1218,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]);
@ -1228,15 +1226,15 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
} }
else else
{ {
if (players[tab[i].num].powers[pw_super]) if (players[tab[i].num].powers[pw_super] && players[tab[i].num].mo && (players[tab[i].num].mo->state < &states[S_PLAY_SUPER_TRANS] || players[tab[i].num].mo->state > &states[S_PLAY_SUPER_TRANS9]))
{ {
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->color, GTC_CACHE);
V_DrawSmallMappedPatch (x, y-4, 0, superprefix[players[tab[i].num].skin], colormap); V_DrawSmallMappedPatch (x, y-4, 0, superprefix[players[tab[i].num].skin], colormap);
} }
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 +1242,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 +1258,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 +1309,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 +1335,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 +1365,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 +1388,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 +1404,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 +1419,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

@ -21,7 +21,7 @@
//------------------------------------ //------------------------------------
// heads up font // heads up font
//------------------------------------ //------------------------------------
#define HU_FONTSTART '\x1F' // the first font character #define HU_FONTSTART '\x19' // the first font character
#define HU_FONTEND '~' #define HU_FONTEND '~'
#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) #define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1)
@ -71,6 +71,7 @@ extern patch_t *rmatcico;
extern patch_t *bmatcico; extern patch_t *bmatcico;
extern patch_t *tagico; extern patch_t *tagico;
extern patch_t *tallminus; extern patch_t *tallminus;
extern patch_t *tokenicon;
// set true when entering a chat message // set true when entering a chat message
extern boolean chat_on; extern boolean chat_on;
@ -78,9 +79,6 @@ extern boolean chat_on;
// set true whenever the tab rankings are being shown for any reason // set true whenever the tab rankings are being shown for any reason
extern boolean hu_showscores; extern boolean hu_showscores;
// P_DeathThink sets this true to show scores while dead, in multiplayer
extern boolean playerdeadview;
// init heads up data at game startup. // init heads up data at game startup.
void HU_Init(void); void HU_Init(void);

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)

5084
src/info.c

File diff suppressed because it is too large Load diff

1096
src/info.h

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

269
src/lua_blockmaplib.c Normal file
View file

@ -0,0 +1,269 @@
// 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;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
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

@ -22,7 +22,10 @@
#include "lua_libs.h" #include "lua_libs.h"
#include "lua_hud.h" // hud_running errors #include "lua_hud.h" // hud_running errors
#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!");
#define INLEVEL if (gamestate != GS_LEVEL)\
return luaL_error(L, "This function can only be used in a level!");
static const char *cvname = NULL; static const char *cvname = NULL;
@ -412,6 +415,7 @@ static int lib_consPrintf(lua_State *L)
if (n < 2) if (n < 2)
return luaL_error(L, "CONS_Printf requires at least two arguments: player and text."); return luaL_error(L, "CONS_Printf requires at least two arguments: player and text.");
//HUDSAFE //HUDSAFE
INLEVEL
plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
if (!plr) if (!plr)
return LUA_ErrInvalid(L, "player_t"); return LUA_ErrInvalid(L, "player_t");

View file

@ -43,6 +43,10 @@ enum hook {
hook_PlayerMsg, hook_PlayerMsg,
hook_HurtMsg, hook_HurtMsg,
hook_PlayerSpawn, hook_PlayerSpawn,
hook_ShieldSpawn,
hook_ShieldSpecial,
hook_MobjMoveBlocked,
hook_MapThingSpawn,
hook_MAX // last hook hook_MAX // last hook
}; };
@ -60,11 +64,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 +79,11 @@ 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
#define LUAh_MobjMoveBlocked(mo) LUAh_MobjHook(mo, hook_MobjMoveBlocked) // Hook for P_XYMovement (when movement is blocked)
boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type
#endif #endif

View file

@ -54,6 +54,10 @@ const char *const hookNames[hook_MAX+1] = {
"PlayerMsg", "PlayerMsg",
"HurtMsg", "HurtMsg",
"PlayerSpawn", "PlayerSpawn",
"ShieldSpawn",
"ShieldSpecial",
"MobjMoveBlocked",
"MapThingSpawn",
NULL NULL
}; };
@ -74,12 +78,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);
@ -106,9 +128,12 @@ static int lib_addHook(lua_State *L)
case hook_BossDeath: case hook_BossDeath:
case hook_MobjRemoved: case hook_MobjRemoved:
case hook_HurtMsg: case hook_HurtMsg:
case hook_MobjMoveBlocked:
case hook_MapThingSpawn:
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 +166,51 @@ 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:
case hook_MobjMoveBlocked:
case hook_MapThingSpawn:
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 +241,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 +295,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 +416,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 +479,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 +542,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 +596,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 +605,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 +639,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 +688,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 +717,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 +761,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 +788,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 +929,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 +1005,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 +1023,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);
@ -798,4 +1076,66 @@ void LUAh_NetArchiveHook(lua_CFunction archFunc)
// stack: tables // stack: tables
} }
boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing)
{
hook_p hookp;
boolean hooked = false;
if (!gL || !(hooksAvailable[hook_MapThingSpawn/8] & (1<<(hook_MapThingSpawn%8))))
return false;
lua_settop(gL, 0);
// Look for all generic mobj map thing spawn hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == hook_MapThingSpawn)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, mo, META_MOBJ);
LUA_PushUserdata(gL, mthing, META_MAPTHING);
}
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[mo->type]; hookp; hookp = hookp->next)
if (hookp->type == hook_MapThingSpawn)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, mo, META_MOBJ);
LUA_PushUserdata(gL, mthing, META_MAPTHING);
}
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);
}
lua_settop(gL, 0);
return hooked;
}
#endif #endif

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)
@ -369,6 +374,8 @@ static int libd_drawScaled(lua_State *L)
x = luaL_checkinteger(L, 1); x = luaL_checkinteger(L, 1);
y = luaL_checkinteger(L, 2); y = luaL_checkinteger(L, 2);
scale = luaL_checkinteger(L, 3); scale = luaL_checkinteger(L, 3);
if (scale < 0)
return luaL_error(L, "negative scale");
patch = *((patch_t **)luaL_checkudata(L, 4, META_PATCH)); patch = *((patch_t **)luaL_checkudata(L, 4, META_PATCH));
flags = luaL_optinteger(L, 5, 0); flags = luaL_optinteger(L, 5, 0);
if (!lua_isnoneornil(L, 6)) if (!lua_isnoneornil(L, 6))

View file

@ -31,6 +31,7 @@ enum sfxinfo_read {
sfxinfor_singular, sfxinfor_singular,
sfxinfor_priority, sfxinfor_priority,
sfxinfor_flags, // "pitch" sfxinfor_flags, // "pitch"
sfxinfor_caption,
sfxinfor_skinsound sfxinfor_skinsound
}; };
const char *const sfxinfo_ropt[] = { const char *const sfxinfo_ropt[] = {
@ -38,18 +39,21 @@ const char *const sfxinfo_ropt[] = {
"singular", "singular",
"priority", "priority",
"flags", "flags",
"caption",
"skinsound", "skinsound",
NULL}; NULL};
enum sfxinfo_write { enum sfxinfo_write {
sfxinfow_singular = 0, sfxinfow_singular = 0,
sfxinfow_priority, sfxinfow_priority,
sfxinfow_flags // "pitch" sfxinfow_flags, // "pitch"
sfxinfow_caption
}; };
const char *const sfxinfo_wopt[] = { const char *const sfxinfo_wopt[] = {
"singular", "singular",
"priority", "priority",
"flags", "flags",
"caption",
NULL}; NULL};
// //
@ -769,8 +773,8 @@ static int lib_getSfxInfo(lua_State *L)
lua_remove(L, 1); lua_remove(L, 1);
i = luaL_checkinteger(L, 1); i = luaL_checkinteger(L, 1);
if (i >= NUMSFX) if (i == 0 || i >= NUMSFX)
return luaL_error(L, "sfxinfo[] index %d out of range (0 - %d)", i, NUMSFX-1); return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, NUMSFX-1);
LUA_PushUserdata(L, &S_sfx[i], META_SFXINFO); LUA_PushUserdata(L, &S_sfx[i], META_SFXINFO);
return 1; return 1;
} }
@ -783,9 +787,9 @@ static int lib_setSfxInfo(lua_State *L)
lua_remove(L, 1); lua_remove(L, 1);
{ {
UINT32 i = luaL_checkinteger(L, 1); UINT32 i = luaL_checkinteger(L, 1);
if (i >= NUMSFX) if (i == 0 || i >= NUMSFX)
return luaL_error(L, "sfxinfo[] index %d out of range (0 - %d)", i, NUMSFX-1); return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, NUMSFX-1);
info = &S_sfx[i]; // get the mobjinfo to assign to. info = &S_sfx[i]; // get the sfxinfo to assign to.
} }
luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table. luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
lua_remove(L, 1); // pop mobjtype num, don't need it any more. lua_remove(L, 1); // pop mobjtype num, don't need it any more.
@ -814,6 +818,9 @@ static int lib_setSfxInfo(lua_State *L)
case sfxinfow_flags: case sfxinfow_flags:
info->pitch = (INT32)luaL_checkinteger(L, 3); info->pitch = (INT32)luaL_checkinteger(L, 3);
break; break;
case sfxinfow_caption:
strlcpy(info->caption, luaL_checkstring(L, 3), sizeof(info->caption));
break;
default: default:
break; break;
} }
@ -851,11 +858,14 @@ static int sfxinfo_get(lua_State *L)
case sfxinfor_flags: case sfxinfor_flags:
lua_pushinteger(L, sfx->pitch); lua_pushinteger(L, sfx->pitch);
return 1; return 1;
case sfxinfor_caption:
lua_pushstring(L, sfx->caption);
return 1;
case sfxinfor_skinsound: case sfxinfor_skinsound:
lua_pushinteger(L, sfx->skinsound); lua_pushinteger(L, sfx->skinsound);
return 1; return 1;
default: default:
return luaL_error(L, "impossible error"); return luaL_error(L, "Field does not exist in sfxinfo_t");
} }
return 0; return 0;
} }
@ -886,8 +896,11 @@ static int sfxinfo_set(lua_State *L)
case sfxinfow_flags: case sfxinfow_flags:
sfx->pitch = luaL_checkinteger(L, 1); sfx->pitch = luaL_checkinteger(L, 1);
break; break;
case sfxinfow_caption:
strlcpy(sfx->caption, luaL_checkstring(L, 1), sizeof(sfx->caption));
break;
default: default:
return luaL_error(L, "impossible error"); return luaL_error(L, "Field does not exist in sfxinfo_t");
} }
return 0; return 0;
} }

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,15 +185,98 @@ 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};
///////////////////////////////////
// sector list iterate functions //
///////////////////////////////////
// iterates through a sector's thinglist! // iterates through a sector's thinglist!
static int lib_iterateSectorThinglist(lua_State *L) static int lib_iterateSectorThinglist(lua_State *L)
{ {
mobj_t *state = NULL; mobj_t *state = NULL;
mobj_t *thing = NULL; mobj_t *thing = NULL;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
if (lua_gettop(L) < 2) if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call sector.thinglist() directly, use it as 'for rover in sector.thinglist do <block> end'."); return luaL_error(L, "Don't call sector.thinglist() directly, use it as 'for rover in sector.thinglist do <block> end'.");
@ -227,6 +310,9 @@ static int lib_iterateSectorFFloors(lua_State *L)
ffloor_t *state = NULL; ffloor_t *state = NULL;
ffloor_t *ffloor = NULL; ffloor_t *ffloor = NULL;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
if (lua_gettop(L) < 2) if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call sector.ffloors() directly, use it as 'for rover in sector.ffloors do <block> end'."); return luaL_error(L, "Don't call sector.ffloors() directly, use it as 'for rover in sector.ffloors do <block> end'.");
@ -262,6 +348,10 @@ static int sector_iterate(lua_State *L)
return 3; return 3;
} }
////////////////////
// sector.lines[] //
////////////////////
// sector.lines, i -> sector.lines[i] // sector.lines, i -> sector.lines[i]
// sector.lines.valid, for validity checking // sector.lines.valid, for validity checking
static int sectorlines_get(lua_State *L) static int sectorlines_get(lua_State *L)
@ -323,6 +413,10 @@ static int sectorlines_num(lua_State *L)
return 1; return 1;
} }
//////////////
// sector_t //
//////////////
static int sector_get(lua_State *L) static int sector_get(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));
@ -348,22 +442,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 +484,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));
@ -514,6 +558,10 @@ static int sector_num(lua_State *L)
return 1; return 1;
} }
/////////////////
// subsector_t //
/////////////////
static int subsector_get(lua_State *L) static int subsector_get(lua_State *L)
{ {
subsector_t *subsector = *((subsector_t **)luaL_checkudata(L, 1, META_SUBSECTOR)); subsector_t *subsector = *((subsector_t **)luaL_checkudata(L, 1, META_SUBSECTOR));
@ -553,6 +601,10 @@ static int subsector_num(lua_State *L)
return 1; return 1;
} }
////////////
// line_t //
////////////
static int line_get(lua_State *L) static int line_get(lua_State *L)
{ {
line_t *line = *((line_t **)luaL_checkudata(L, 1, META_LINE)); line_t *line = *((line_t **)luaL_checkudata(L, 1, META_LINE));
@ -650,6 +702,10 @@ static int line_num(lua_State *L)
return 1; return 1;
} }
////////////////////
// line.sidenum[] //
////////////////////
static int sidenum_get(lua_State *L) static int sidenum_get(lua_State *L)
{ {
UINT16 *sidenum = *((UINT16 **)luaL_checkudata(L, 1, META_SIDENUM)); UINT16 *sidenum = *((UINT16 **)luaL_checkudata(L, 1, META_SIDENUM));
@ -678,6 +734,10 @@ static int sidenum_get(lua_State *L)
return 1; return 1;
} }
////////////
// side_t //
////////////
static int side_get(lua_State *L) static int side_get(lua_State *L)
{ {
side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE)); side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE));
@ -779,6 +839,10 @@ static int side_num(lua_State *L)
return 1; return 1;
} }
//////////////
// vertex_t //
//////////////
static int vertex_get(lua_State *L) static int vertex_get(lua_State *L)
{ {
vertex_t *vertex = *((vertex_t **)luaL_checkudata(L, 1, META_VERTEX)); vertex_t *vertex = *((vertex_t **)luaL_checkudata(L, 1, META_VERTEX));
@ -818,9 +882,293 @@ static int vertex_num(lua_State *L)
return 1; return 1;
} }
#ifdef HAVE_LUA_SEGS
///////////
// seg_t //
///////////
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;
}
////////////
// node_t //
////////////
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 //
///////////////
/*
// 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[] //
/////////////////////
// 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
//////////
// bbox //
//////////
// 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;
}
///////////////
// sectors[] //
///////////////
static int lib_iterateSectors(lua_State *L) static int lib_iterateSectors(lua_State *L)
{ {
size_t i = 0; size_t i = 0;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
if (lua_gettop(L) < 2) if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call sectors.iterate() directly, use it as 'for sector in sectors.iterate do <block> end'."); return luaL_error(L, "Don't call sectors.iterate() directly, use it as 'for sector in sectors.iterate do <block> end'.");
lua_settop(L, 2); lua_settop(L, 2);
@ -838,6 +1186,8 @@ static int lib_iterateSectors(lua_State *L)
static int lib_getSector(lua_State *L) static int lib_getSector(lua_State *L)
{ {
int field; int field;
if (gamestate != GS_LEVEL)
return luaL_error(L, "You cannot access this outside of a level!");
lua_settop(L, 2); lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused. lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1)) if (lua_isnumber(L, 1))
@ -864,9 +1214,15 @@ static int lib_numsectors(lua_State *L)
return 1; return 1;
} }
//////////////////
// subsectors[] //
//////////////////
static int lib_iterateSubsectors(lua_State *L) static int lib_iterateSubsectors(lua_State *L)
{ {
size_t i = 0; size_t i = 0;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
if (lua_gettop(L) < 2) if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call subsectors.iterate() directly, use it as 'for subsector in subsectors.iterate do <block> end'."); return luaL_error(L, "Don't call subsectors.iterate() directly, use it as 'for subsector in subsectors.iterate do <block> end'.");
lua_settop(L, 2); lua_settop(L, 2);
@ -884,6 +1240,8 @@ static int lib_iterateSubsectors(lua_State *L)
static int lib_getSubsector(lua_State *L) static int lib_getSubsector(lua_State *L)
{ {
int field; int field;
if (gamestate != GS_LEVEL)
return luaL_error(L, "You cannot access this outside of a level!");
lua_settop(L, 2); lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused. lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1)) if (lua_isnumber(L, 1))
@ -910,9 +1268,15 @@ static int lib_numsubsectors(lua_State *L)
return 1; return 1;
} }
/////////////
// lines[] //
/////////////
static int lib_iterateLines(lua_State *L) static int lib_iterateLines(lua_State *L)
{ {
size_t i = 0; size_t i = 0;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
if (lua_gettop(L) < 2) if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call lines.iterate() directly, use it as 'for line in lines.iterate do <block> end'."); return luaL_error(L, "Don't call lines.iterate() directly, use it as 'for line in lines.iterate do <block> end'.");
lua_settop(L, 2); lua_settop(L, 2);
@ -930,6 +1294,8 @@ static int lib_iterateLines(lua_State *L)
static int lib_getLine(lua_State *L) static int lib_getLine(lua_State *L)
{ {
int field; int field;
if (gamestate != GS_LEVEL)
return luaL_error(L, "You cannot access this outside of a level!");
lua_settop(L, 2); lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused. lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1)) if (lua_isnumber(L, 1))
@ -956,9 +1322,15 @@ static int lib_numlines(lua_State *L)
return 1; return 1;
} }
/////////////
// sides[] //
/////////////
static int lib_iterateSides(lua_State *L) static int lib_iterateSides(lua_State *L)
{ {
size_t i = 0; size_t i = 0;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
if (lua_gettop(L) < 2) if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call sides.iterate() directly, use it as 'for side in sides.iterate do <block> end'."); return luaL_error(L, "Don't call sides.iterate() directly, use it as 'for side in sides.iterate do <block> end'.");
lua_settop(L, 2); lua_settop(L, 2);
@ -976,6 +1348,8 @@ static int lib_iterateSides(lua_State *L)
static int lib_getSide(lua_State *L) static int lib_getSide(lua_State *L)
{ {
int field; int field;
if (gamestate != GS_LEVEL)
return luaL_error(L, "You cannot access this outside of a level!");
lua_settop(L, 2); lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused. lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1)) if (lua_isnumber(L, 1))
@ -1002,9 +1376,15 @@ static int lib_numsides(lua_State *L)
return 1; return 1;
} }
////////////////
// vertexes[] //
////////////////
static int lib_iterateVertexes(lua_State *L) static int lib_iterateVertexes(lua_State *L)
{ {
size_t i = 0; size_t i = 0;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
if (lua_gettop(L) < 2) if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call vertexes.iterate() directly, use it as 'for vertex in vertexes.iterate do <block> end'."); return luaL_error(L, "Don't call vertexes.iterate() directly, use it as 'for vertex in vertexes.iterate do <block> end'.");
lua_settop(L, 2); lua_settop(L, 2);
@ -1022,6 +1402,8 @@ static int lib_iterateVertexes(lua_State *L)
static int lib_getVertex(lua_State *L) static int lib_getVertex(lua_State *L)
{ {
int field; int field;
if (gamestate != GS_LEVEL)
return luaL_error(L, "You cannot access this outside of a level!");
lua_settop(L, 2); lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused. lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1)) if (lua_isnumber(L, 1))
@ -1048,6 +1430,121 @@ static int lib_numvertexes(lua_State *L)
return 1; return 1;
} }
#ifdef HAVE_LUA_SEGS
////////////
// segs[] //
////////////
static int lib_iterateSegs(lua_State *L)
{
size_t i = 0;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
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;
if (gamestate != GS_LEVEL)
return luaL_error(L, "You cannot access this outside of a level!");
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;
}
/////////////
// nodes[] //
/////////////
static int lib_iterateNodes(lua_State *L)
{
size_t i = 0;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
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;
if (gamestate != GS_LEVEL)
return luaL_error(L, "You cannot access this outside of a level!");
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
//////////////
// ffloor_t //
//////////////
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));
@ -1191,6 +1688,10 @@ static int ffloor_set(lua_State *L)
return 0; return 0;
} }
/////////////////////
// mapheaderinfo[] //
/////////////////////
static int lib_getMapheaderinfo(lua_State *L) static int lib_getMapheaderinfo(lua_State *L)
{ {
// i -> mapheaderinfo[i-1] // i -> mapheaderinfo[i-1]
@ -1223,6 +1724,10 @@ static int lib_nummapheaders(lua_State *L)
return 1; return 1;
} }
/////////////////
// mapheader_t //
/////////////////
static int mapheaderinfo_get(lua_State *L) static int mapheaderinfo_get(lua_State *L)
{ {
mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER)); mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER));
@ -1367,6 +1872,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 +1965,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;
@ -503,7 +511,8 @@ static int mobj_set(lua_State *L)
for (i = 0; i < numskins; i++) for (i = 0; i < numskins; i++)
if (fastcmp(skins[i].name, skin)) if (fastcmp(skins[i].name, skin))
{ {
mo->skin = &skins[i]; if (!mo->player || R_SkinUsable(mo->player-players, i))
mo->skin = &skins[i];
return 0; return 0;
} }
return luaL_error(L, "mobj.skin '%s' not found!", skin); return luaL_error(L, "mobj.skin '%s' not found!", skin);
@ -743,6 +752,8 @@ static int mapthing_set(lua_State *L)
static int lib_iterateMapthings(lua_State *L) static int lib_iterateMapthings(lua_State *L)
{ {
size_t i = 0; size_t i = 0;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
if (lua_gettop(L) < 2) if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call mapthings.iterate() directly, use it as 'for mapthing in mapthings.iterate do <block> end'."); return luaL_error(L, "Don't call mapthings.iterate() directly, use it as 'for mapthing in mapthings.iterate do <block> end'.");
lua_settop(L, 2); lua_settop(L, 2);
@ -760,6 +771,8 @@ static int lib_iterateMapthings(lua_State *L)
static int lib_getMapthing(lua_State *L) static int lib_getMapthing(lua_State *L)
{ {
int field; int field;
if (gamestate != GS_LEVEL)
return luaL_error(L, "You cannot access this outside of a level!");
lua_settop(L, 2); lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused. lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1)) if (lua_isnumber(L, 1))

View file

@ -25,6 +25,8 @@
static int lib_iteratePlayers(lua_State *L) static int lib_iteratePlayers(lua_State *L)
{ {
INT32 i = -1; INT32 i = -1;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
if (lua_gettop(L) < 2) if (lua_gettop(L) < 2)
{ {
//return luaL_error(L, "Don't call players.iterate() directly, use it as 'for player in players.iterate do <block> end'."); //return luaL_error(L, "Don't call players.iterate() directly, use it as 'for player in players.iterate do <block> end'.");
@ -51,6 +53,8 @@ static int lib_getPlayer(lua_State *L)
{ {
const char *field; const char *field;
// i -> players[i] // i -> players[i]
if (gamestate != GS_LEVEL)
return luaL_error(L, "You cannot access this outside of a level!");
if (lua_type(L, 2) == LUA_TNUMBER) if (lua_type(L, 2) == LUA_TNUMBER)
{ {
lua_Integer i = luaL_checkinteger(L, 2); lua_Integer i = luaL_checkinteger(L, 2);
@ -122,8 +126,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"))
@ -190,8 +194,6 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->gotcontinue); lua_pushinteger(L, plr->gotcontinue);
else if (fastcmp(field,"speed")) else if (fastcmp(field,"speed"))
lua_pushfixed(L, plr->speed); lua_pushfixed(L, plr->speed);
else if (fastcmp(field,"jumping"))
lua_pushboolean(L, plr->jumping);
else if (fastcmp(field,"secondjump")) else if (fastcmp(field,"secondjump"))
lua_pushinteger(L, plr->secondjump); lua_pushinteger(L, plr->secondjump);
else if (fastcmp(field,"fly1")) else if (fastcmp(field,"fly1"))
@ -382,8 +384,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"))
@ -455,8 +457,6 @@ static int player_set(lua_State *L)
plr->gotcontinue = (UINT8)luaL_checkinteger(L, 3); plr->gotcontinue = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"speed")) else if (fastcmp(field,"speed"))
plr->speed = luaL_checkfixed(L, 3); plr->speed = luaL_checkfixed(L, 3);
else if (fastcmp(field,"jumping"))
plr->jumping = luaL_checkboolean(L, 3);
else if (fastcmp(field,"secondjump")) else if (fastcmp(field,"secondjump"))
plr->secondjump = (UINT8)luaL_checkinteger(L, 3); plr->secondjump = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"fly1")) else if (fastcmp(field,"fly1"))

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}
}; };
@ -566,14 +596,14 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
{ {
mobjinfo_t *info = *((mobjinfo_t **)lua_touserdata(gL, myindex)); mobjinfo_t *info = *((mobjinfo_t **)lua_touserdata(gL, myindex));
WRITEUINT8(save_p, ARCH_MOBJINFO); WRITEUINT8(save_p, ARCH_MOBJINFO);
WRITEUINT8(save_p, info - mobjinfo); WRITEUINT16(save_p, info - mobjinfo);
break; break;
} }
case ARCH_STATE: case ARCH_STATE:
{ {
state_t *state = *((state_t **)lua_touserdata(gL, myindex)); state_t *state = *((state_t **)lua_touserdata(gL, myindex));
WRITEUINT8(save_p, ARCH_STATE); WRITEUINT8(save_p, ARCH_STATE);
WRITEUINT8(save_p, state - states); WRITEUINT16(save_p, state - states);
break; break;
} }
case ARCH_MOBJ: case ARCH_MOBJ:
@ -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

@ -16,84 +16,127 @@
#include "lua_script.h" #include "lua_script.h"
#include "lua_libs.h" #include "lua_libs.h"
#define META_ITERATIONSTATE "iteration state"
static const char *const iter_opt[] = { static const char *const iter_opt[] = {
"all", "all",
"mobj", "mobj",
NULL}; NULL};
static const actionf_p1 iter_funcs[] = {
NULL,
(actionf_p1)P_MobjThinker
};
struct iterationState {
actionf_p1 filter;
int next;
};
static int iterationState_gc(lua_State *L)
{
struct iterationState *it = luaL_checkudata(L, -1, META_ITERATIONSTATE);
if (it->next != LUA_REFNIL)
{
luaL_unref(L, LUA_REGISTRYINDEX, it->next);
it->next = LUA_REFNIL;
}
return 0;
}
#define push_thinker(th) {\
if ((th)->function.acp1 == (actionf_p1)P_MobjThinker) \
LUA_PushUserdata(L, (th), META_MOBJ); \
else \
lua_pushlightuserdata(L, (th)); \
}
static int lib_iterateThinkers(lua_State *L) static int lib_iterateThinkers(lua_State *L)
{ {
int state = luaL_checkoption(L, 1, "mobj", iter_opt); thinker_t *th = NULL, *next = NULL;
struct iterationState *it;
thinker_t *th = NULL; if (gamestate != GS_LEVEL)
actionf_p1 searchFunc; return luaL_error(L, "This function can only be used in a level!");
const char *searchMeta;
it = luaL_checkudata(L, 1, META_ITERATIONSTATE);
lua_settop(L, 2); lua_settop(L, 2);
lua_remove(L, 1); // remove state now.
switch(state) if (lua_isnil(L, 2))
th = &thinkercap;
else if (lua_isuserdata(L, 2))
{ {
case 0: if (lua_islightuserdata(L, 2))
searchFunc = NULL; th = lua_touserdata(L, 2);
searchMeta = NULL; else
break; {
case 1: th = *(thinker_t **)lua_touserdata(L, -1);
default: if (!th)
searchFunc = (actionf_p1)P_MobjThinker; {
searchMeta = META_MOBJ; if (it->next == LUA_REFNIL)
break; return 0;
lua_rawgeti(L, LUA_REGISTRYINDEX, it->next);
if (lua_islightuserdata(L, -1))
next = lua_touserdata(L, -1);
else
next = *(thinker_t **)lua_touserdata(L, -1);
}
}
} }
if (!lua_isnil(L, 1)) { luaL_unref(L, LUA_REGISTRYINDEX, it->next);
if (lua_islightuserdata(L, 1)) it->next = LUA_REFNIL;
th = (thinker_t *)lua_touserdata(L, 1);
else if (searchMeta)
th = *((thinker_t **)luaL_checkudata(L, 1, searchMeta));
else
th = *((thinker_t **)lua_touserdata(L, 1));
} else
th = &thinkercap;
if (!th) // something got our userdata invalidated! if (th && !next)
return 0; next = th->next;
if (!next)
return luaL_error(L, "next thinker invalidated during iteration");
if (searchFunc == NULL) for (; next != &thinkercap; next = next->next)
{ if (!it->filter || next->function.acp1 == it->filter)
if ((th = th->next) != &thinkercap)
{ {
if (th->function.acp1 == (actionf_p1)P_MobjThinker) push_thinker(next);
LUA_PushUserdata(L, th, META_MOBJ); if (next->next != &thinkercap)
else {
lua_pushlightuserdata(L, th); push_thinker(next->next);
it->next = luaL_ref(L, LUA_REGISTRYINDEX);
}
return 1; return 1;
} }
return 0;
}
for (th = th->next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != searchFunc)
continue;
LUA_PushUserdata(L, th, searchMeta);
return 1;
}
return 0; return 0;
} }
static int lib_startIterate(lua_State *L) static int lib_startIterate(lua_State *L)
{ {
luaL_checkoption(L, 1, iter_opt[0], iter_opt); struct iterationState *it;
lua_pushcfunction(L, lib_iterateThinkers);
lua_pushvalue(L, 1); if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
lua_pushvalue(L, lua_upvalueindex(1));
it = lua_newuserdata(L, sizeof(struct iterationState));
luaL_getmetatable(L, META_ITERATIONSTATE);
lua_setmetatable(L, -2);
it->filter = iter_funcs[luaL_checkoption(L, 1, "mobj", iter_opt)];
it->next = LUA_REFNIL;
return 2; return 2;
} }
#undef push_thinker
int LUA_ThinkerLib(lua_State *L) int LUA_ThinkerLib(lua_State *L)
{ {
luaL_newmetatable(L, META_ITERATIONSTATE);
lua_pushcfunction(L, iterationState_gc);
lua_setfield(L, -2, "__gc");
lua_pop(L, 1);
lua_createtable(L, 0, 1); lua_createtable(L, 0, 1);
lua_pushcfunction(L, lib_startIterate); lua_pushcfunction(L, lib_iterateThinkers);
lua_pushcclosure(L, lib_startIterate, 1);
lua_setfield(L, -2, "iterate"); lua_setfield(L, -2, "iterate");
lua_setglobal(L, "thinkers"); lua_setglobal(L, "thinkers");
return 0; return 0;

View file

@ -18,6 +18,7 @@
#include "z_zone.h" #include "z_zone.h"
#include "v_video.h" #include "v_video.h"
#include "i_video.h" #include "i_video.h"
#include "m_misc.h"
// GIFs are always little-endian // GIFs are always little-endian
#include "byteptr.h" #include "byteptr.h"
@ -396,7 +397,6 @@ static void GIF_headwrite(void)
{ {
UINT8 *gifhead = Z_Malloc(800, PU_STATIC, NULL); UINT8 *gifhead = Z_Malloc(800, PU_STATIC, NULL);
UINT8 *p = gifhead; UINT8 *p = gifhead;
RGBA_t *c;
INT32 i; INT32 i;
UINT16 rwidth, rheight; UINT16 rwidth, rheight;
@ -427,12 +427,17 @@ static void GIF_headwrite(void)
WRITEUINT8(p, 0x00); WRITEUINT8(p, 0x00);
// write color table // write color table
for (i = 0; i < 256; ++i)
{ {
c = &pLocalPalette[i]; RGBA_t *pal = ((cv_screenshot_colorprofile.value)
WRITEUINT8(p, c->s.red); ? pLocalPalette
WRITEUINT8(p, c->s.green); : pMasterPalette);
WRITEUINT8(p, c->s.blue);
for (i = 0; i < 256; i++)
{
WRITEUINT8(p, pal[i].s.red);
WRITEUINT8(p, pal[i].s.green);
WRITEUINT8(p, pal[i].s.blue);
}
} }
// write extension block // write extension block

View file

@ -452,7 +452,7 @@ void Command_RTeleport_f(void)
else else
inty = 0; inty = 0;
ss = R_PointInSubsector(p->mo->x + intx*FRACUNIT, p->mo->y + inty*FRACUNIT); ss = R_IsPointInSubsector(p->mo->x + intx*FRACUNIT, p->mo->y + inty*FRACUNIT);
if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height) if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height)
{ {
CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n")); CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n"));
@ -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
@ -942,7 +942,7 @@ boolean OP_FreezeObjectplace(void)
if (!objectplacing) if (!objectplacing)
return false; return false;
if ((maptol & TOL_NIGHTS) && (players[consoleplayer].pflags & PF_NIGHTSMODE)) if ((maptol & TOL_NIGHTS) && (players[consoleplayer].powers[pw_carry] == CR_NIGHTSMODE))
return false; return false;
return true; return true;
@ -968,7 +968,7 @@ void OP_NightsObjectplace(player_t *player)
if (player->pflags & PF_ATTACKDOWN) if (player->pflags & PF_ATTACKDOWN)
{ {
// Are ANY objectplace buttons pressed? If no, remove flag. // Are ANY objectplace buttons pressed? If no, remove flag.
if (!(cmd->buttons & (BT_ATTACK|BT_TOSSFLAG|BT_USE|BT_CAMRIGHT|BT_CAMLEFT))) if (!(cmd->buttons & (BT_ATTACK|BT_TOSSFLAG|BT_USE|BT_WEAPONNEXT|BT_WEAPONPREV)))
player->pflags &= ~PF_ATTACKDOWN; player->pflags &= ~PF_ATTACKDOWN;
// Do nothing. // Do nothing.
@ -1019,7 +1019,7 @@ void OP_NightsObjectplace(player_t *player)
} }
// This places a ring! // This places a ring!
if (cmd->buttons & BT_CAMRIGHT) if (cmd->buttons & BT_WEAPONNEXT)
{ {
player->pflags |= PF_ATTACKDOWN; player->pflags |= PF_ATTACKDOWN;
if (!OP_HeightOkay(player, false)) if (!OP_HeightOkay(player, false))
@ -1030,7 +1030,7 @@ void OP_NightsObjectplace(player_t *player)
} }
// This places a wing item! // This places a wing item!
if (cmd->buttons & BT_CAMLEFT) if (cmd->buttons & BT_WEAPONPREV)
{ {
player->pflags |= PF_ATTACKDOWN; player->pflags |= PF_ATTACKDOWN;
if (!OP_HeightOkay(player, false)) if (!OP_HeightOkay(player, false))
@ -1054,7 +1054,7 @@ void OP_NightsObjectplace(player_t *player)
if (!OP_HeightOkay(player, false)) if (!OP_HeightOkay(player, false))
return; return;
if (player->mo->target->flags & MF_AMBUSH) if (player->mo->target->flags2 & MF2_AMBUSH)
angle = (UINT16)player->anotherflyangle; angle = (UINT16)player->anotherflyangle;
else else
{ {
@ -1165,19 +1165,20 @@ void OP_ObjectplaceMovement(player_t *player)
if (player->pflags & PF_ATTACKDOWN) if (player->pflags & PF_ATTACKDOWN)
{ {
// Are ANY objectplace buttons pressed? If no, remove flag. // Are ANY objectplace buttons pressed? If no, remove flag.
if (!(cmd->buttons & (BT_ATTACK|BT_TOSSFLAG|BT_CAMRIGHT|BT_CAMLEFT))) if (!(cmd->buttons & (BT_ATTACK|BT_TOSSFLAG|BT_WEAPONNEXT|BT_WEAPONPREV)))
player->pflags &= ~PF_ATTACKDOWN; player->pflags &= ~PF_ATTACKDOWN;
// Do nothing. // Do nothing.
return; return;
} }
if (cmd->buttons & BT_CAMLEFT) if (cmd->buttons & BT_WEAPONPREV)
{ {
OP_CycleThings(-1); OP_CycleThings(-1);
player->pflags |= PF_ATTACKDOWN; player->pflags |= PF_ATTACKDOWN;
} }
else if (cmd->buttons & BT_CAMRIGHT)
if (cmd->buttons & BT_WEAPONNEXT)
{ {
OP_CycleThings(1); OP_CycleThings(1);
player->pflags |= PF_ATTACKDOWN; player->pflags |= PF_ATTACKDOWN;
@ -1254,7 +1255,7 @@ void Command_ObjectPlace_f(void)
{ {
objectplacing = true; objectplacing = true;
if ((players[0].pflags & PF_NIGHTSMODE)) if ((players[0].powers[pw_carry] == CR_NIGHTSMODE))
return; return;
if (!COM_CheckParm("-silent")) if (!COM_CheckParm("-silent"))
@ -1264,10 +1265,10 @@ void Command_ObjectPlace_f(void)
HU_DoCEcho(va(M_GetText( HU_DoCEcho(va(M_GetText(
"\\\\\\\\\\\\\\\\\\\\\\\\\x82" "\\\\\\\\\\\\\\\\\\\\\\\\\x82"
" Objectplace Controls: \x80\\\\" " Objectplace Controls: \x80\\\\"
"Camera L/R: Cycle mapthings\\" "Weapon Next/Prev: Cycle mapthings\\"
" Jump: Float up \\" " Jump: Float up \\"
" Spin: Float down \\" " Spin: Float down \\"
" Fire Ring: Place object \\"))); " Fire Ring: Place object \\")));
} }
// Save all the player's data. // Save all the player's data.
@ -1297,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;
@ -1325,7 +1326,7 @@ void Command_ObjectPlace_f(void)
// Don't touch the NiGHTS Objectplace stuff. // Don't touch the NiGHTS Objectplace stuff.
// ... or if the mo mysteriously vanished. // ... or if the mo mysteriously vanished.
if (!players[0].mo || (players[0].pflags & PF_NIGHTSMODE)) if (!players[0].mo || (players[0].powers[pw_carry] == CR_NIGHTSMODE))
return; return;
// If still in dummy state, get out of it. // If still in dummy state, get out of it.

View file

@ -565,7 +565,7 @@ extraemblem_t extraemblems[MAXEXTRAEMBLEMS] =
{"Game Complete", "Complete 1P Mode", 10, 'X', SKINCOLOR_BLUE, 0}, {"Game Complete", "Complete 1P Mode", 10, 'X', SKINCOLOR_BLUE, 0},
{"All Emeralds", "Complete 1P Mode with all Emeralds", 11, 'V', SKINCOLOR_GREY, 0}, {"All Emeralds", "Complete 1P Mode with all Emeralds", 11, 'V', SKINCOLOR_GREY, 0},
{"Perfect Bonus", "Perfect Bonus on a non-secret stage", 30, 'P', SKINCOLOR_GOLD, 0}, {"Perfect Bonus", "Perfect Bonus on a non-secret stage", 30, 'P', SKINCOLOR_GOLD, 0},
{"SRB1 Remake", "Complete SRB1 Remake", 21, 'O', SKINCOLOR_RUST, 0}, {"PLACEHOLDER", "PLACEHOLDER", 0, 'O', SKINCOLOR_RUST, 0},
{"NiGHTS Mastery", "Show your mastery of NiGHTS!", 22, 'W', SKINCOLOR_TEAL, 0}, {"NiGHTS Mastery", "Show your mastery of NiGHTS!", 22, 'W', SKINCOLOR_TEAL, 0},
}; };
@ -573,31 +573,31 @@ extraemblem_t extraemblems[MAXEXTRAEMBLEMS] =
unlockable_t unlockables[MAXUNLOCKABLES] = unlockable_t unlockables[MAXUNLOCKABLES] =
{ {
// Name, Objective, Menu Height, ConditionSet, Unlock Type, Variable, NoCecho, NoChecklist // Name, Objective, Menu Height, ConditionSet, Unlock Type, Variable, NoCecho, NoChecklist
/* 01 */ {"Record Attack", "Complete Greenflower Zone, Act 1", 0, 1, SECRET_RECORDATTACK, 0, true, true, 0}, /* 01 */ {"Record Attack", "/", 0, 1, SECRET_RECORDATTACK, 0, true, true, 0},
/* 02 */ {"NiGHTS Mode", "Complete Floral Field", 0, 2, SECRET_NIGHTSMODE, 0, true, true, 0}, /* 02 */ {"NiGHTS Mode", "/", 0, 2, SECRET_NIGHTSMODE, 0, true, true, 0},
/* 03 */ {"Play Credits", "Complete 1P Mode", 30, 10, SECRET_CREDITS, 0, true, true, 0}, /* 03 */ {"Play Credits", "/", 30, 10, SECRET_CREDITS, 0, true, true, 0},
/* 04 */ {"Sound Test", "Complete 1P Mode", 40, 10, SECRET_SOUNDTEST, 0, false, false, 0}, /* 04 */ {"Sound Test", "/", 40, 10, SECRET_SOUNDTEST, 0, false, false, 0},
/* 05 */ {"EXTRA LEVELS", "", 60, 0, SECRET_HEADER, 0, true, true, 0}, /* 05 */ {"EXTRA LEVELS", "/", 58, 0, SECRET_HEADER, 0, true, true, 0},
/* 06 */ {"Aerial Garden Zone", "Complete 1P Mode w/ all emeralds", 70, 11, SECRET_WARP, 40, false, false, 0}, /* 06 */ {"Aerial Garden Zone", "/", 70, 11, SECRET_WARP, 40, false, false, 0},
/* 07 */ {"Azure Temple Zone", "Complete Aerial Garden Zone", 80, 20, SECRET_WARP, 41, false, false, 0}, /* 07 */ {"Azure Temple Zone", "/", 80, 20, SECRET_WARP, 41, false, false, 0},
/* 08 */ {"BONUS LEVELS", "", 100, 0, SECRET_HEADER, 0, true, true, 0}, /* 08 */ {"BONUS LEVELS", "/", 98, 0, SECRET_HEADER, 0, true, true, 0},
/* 09 */ {"SRB1 Remake", "Collect 20 Emblems", 130, 40, SECRET_WARP, 101, false, false, 0}, /* 09 */ {"PLACEHOLDER", "/", 0, 0, SECRET_NONE, 0, true, true, 0},
/* 10 */ {"Mario Koopa Blast", "Collect 60 Emblems", 110, 42, SECRET_WARP, 30, false, false, 0}, /* 10 */ {"Mario Koopa Blast", "/", 110, 42, SECRET_WARP, 30, false, false, 0},
/* 11 */ {"SRB1 Level Select", "Complete SRB1 Remake", 140, 21, SECRET_LEVELSELECT, 2, false, true, 0}, /* 11 */ {"PLACEHOLDER", "/", 0, 0, SECRET_NONE, 0, true, true, 0},
/* 12 */ {"Spring Hill Zone", "Collect 100 Emblems", 0, 44, SECRET_NONE, 0, false, false, 0}, /* 12 */ {"Spring Hill Zone", "/", 0, 44, SECRET_NONE, 0, false, false, 0},
/* 13 */ {"Black Hole", "A Rank in all Special Stages", 0, 50, SECRET_NONE, 0, false, true, 0}, /* 13 */ {"Black Hole", "Get grade A in all Special Stages", 0, 50, SECRET_NONE, 0, false, true, 0},
/* 14 */ {"Emblem Hints", "Collect 40 Emblems", 0, 41, SECRET_EMBLEMHINTS, 0, false, true, 0}, /* 14 */ {"Emblem Hints", "/", 0, 41, SECRET_EMBLEMHINTS, 0, false, true, 0},
/* 15 */ {"Emblem Radar", "Collect 80 Emblems", 0, 43, SECRET_ITEMFINDER, 0, false, true, 0}, /* 15 */ {"Emblem Radar", "/", 0, 43, SECRET_ITEMFINDER, 0, false, true, 0},
/* 16 */ {"Pandora's Box", "Collect All Emblems", 0, 45, SECRET_PANDORA, 0, false, false, 0}, /* 16 */ {"Pandora's Box", "/", 0, 45, SECRET_PANDORA, 0, false, false, 0},
/* 17 */ {"Level Select", "Collect All Emblems", 20, 45, SECRET_LEVELSELECT, 1, false, true, 0}, /* 17 */ {"Level Select", "/", 20, 45, SECRET_LEVELSELECT, 1, false, true, 0},
}; };
// Default number of emblems and extra emblems // Default number of emblems and extra emblems
@ -624,9 +624,6 @@ void M_SetupDefaultConditionSets(void)
// -- 20: Beat AGZ // -- 20: Beat AGZ
M_AddRawCondition(20, 1, UC_MAPBEATEN, 40, 0, 0); M_AddRawCondition(20, 1, UC_MAPBEATEN, 40, 0, 0);
// -- 21: Beat SRB1 Remake
M_AddRawCondition(21, 1, UC_MAPBEATEN, 132, 0, 0);
// -- 22: Beat Black Hole // -- 22: Beat Black Hole
M_AddRawCondition(22, 1, UC_MAPBEATEN, 57, 0, 0); M_AddRawCondition(22, 1, UC_MAPBEATEN, 57, 0, 0);
@ -932,7 +929,7 @@ UINT8 M_CheckLevelEmblems(void)
// Update Score, Time, Rings emblems // Update Score, Time, Rings emblems
for (i = 0; i < numemblems; ++i) for (i = 0; i < numemblems; ++i)
{ {
if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].collected) if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].type == ET_MAP || emblemlocations[i].collected)
continue; continue;
levelnum = emblemlocations[i].level; levelnum = emblemlocations[i].level;
@ -966,6 +963,42 @@ UINT8 M_CheckLevelEmblems(void)
return somethingUnlocked; return somethingUnlocked;
} }
UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough.
{
INT32 i;
INT32 embtype;
INT16 levelnum;
UINT8 res;
UINT8 somethingUnlocked = 0;
UINT8 flags;
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].type != ET_MAP || emblemlocations[i].collected)
continue;
levelnum = emblemlocations[i].level;
embtype = emblemlocations[i].var;
flags = MV_BEATEN;
if (embtype & ME_ALLEMERALDS)
flags |= MV_ALLEMERALDS;
if (embtype & ME_ULTIMATE)
flags |= MV_ULTIMATE;
if (embtype & ME_PERFECT)
flags |= MV_PERFECT;
res = ((mapvisited[levelnum - 1] & flags) == flags);
emblemlocations[i].collected = res;
if (res)
++somethingUnlocked;
}
return somethingUnlocked;
}
// ------------------- // -------------------
// Quick unlock checks // Quick unlock checks
// ------------------- // -------------------

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