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)
project(SRB2
VERSION 2.1.14
VERSION 2.1.19
LANGUAGES C)
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://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/).

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
environment:
@ -47,7 +47,7 @@ before_build:
- upx -V
- ccache -V
- 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:
- 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: set /P GITSHORT=<%TMP%/gitshort.txt
- 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
- appveyor PushArtifact %BUILD_ARCHIVE%
- cmd: copy %BUILD_ARCHIVE% %BUILDSARCHIVE%
- appveyor PushArtifact %BUILDSARCHIVE%
test: off
deploy:
- provider: FTP
protocol: ftps
host:
secure: NsLJEPIBvmwCOj8Tg8RoRQ==
username:
secure: ejxi5mvk7oLYu7QtbYojajEPigMy0mokaKhuEVuDZcA=
password:
secure: Hbn6Uy3lT0YZ88yFJ3aW4w==
folder: appveyor
application:
active_mode: false
on:
branch: master
appveyor_repo_tag: true
#deploy:
# - provider: FTP
# protocol: ftps
# host:
# secure: NsLJEPIBvmwCOj8Tg8RoRQ==
# username:
# secure: ejxi5mvk7oLYu7QtbYojajEPigMy0mokaKhuEVuDZcA=
# password:
# secure: Hbn6Uy3lT0YZ88yFJ3aW4w==
# folder: appveyor
# application:
# active_mode: false
# on:
# branch: master
# appveyor_repo_tag: true
on_finish:

View File

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

View File

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

3
debian/docs vendored
View File

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

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
/*.o
# DON'T REMOVE
# This keeps the folder from disappearing

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,2 +1,2 @@
/depend.dep
/*.o
# 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.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
/x64
# DON'T REMOVE
# This keeps the folder from disappearing

View File

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

View File

@ -1,2 +1,2 @@
/depend.dep
/*.o
# 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 +1,2 @@
/depend.dep
# DON'T REMOVE
# This keeps the folder from disappearing

View File

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

View File

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

View File

@ -231,6 +231,7 @@ if(${SRB2_CONFIG_HAVE_BLUA})
add_definitions(-DHAVE_BLUA)
set(SRB2_LUA_SOURCES
lua_baselib.c
lua_blockmaplib.c
lua_consolelib.c
lua_hooklib.c
lua_hudlib.c
@ -390,18 +391,25 @@ if(${SRB2_CONFIG_HWRENDER} AND ${SRB2_CONFIG_STATIC_OPENGL})
endif()
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})
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)
else()
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)
endif()
set(SRB2_USEASM ON)
add_definitions(-DUSEASM)
else()
set(SRB2_USEASM OFF)
add_definitions(-DNOASM -DNONX86)
add_definitions(-DNONX86 -DNORUSEASM)
endif()
# Targets

View File

@ -179,6 +179,9 @@ endif
ifdef LINUX
UNIXCOMMON=1
ifndef NOGME
HAVE_LIBGME=1
endif
endif
ifdef SOLARIS
@ -315,6 +318,13 @@ LIBS+=$(PNG_LDFLAGS)
CFLAGS+=$(PNG_CFLAGS)
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
OPTS+=-DHAVE_LIBGME
@ -366,6 +376,14 @@ endif
OPTS:=-fno-exceptions $(OPTS)
ifdef MOBJCONSISTANCY
OPTS+=-DMOBJCONSISTANCY
endif
ifdef PACKETDROP
OPTS+=-DPACKETDROP
endif
ifdef DEBUGMODE
# build with debugging information
@ -375,7 +393,7 @@ ifdef GCC48
else
CFLAGS+=-O0
endif
CFLAGS+= -Wall -DPARANOIA -DRANGECHECK
CFLAGS+= -Wall -DPARANOIA -DRANGECHECK -DPACKETDROP -DMOBJCONSISTANCY
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.
# (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
ifndef NOGETTEXT
ifdef GETTEXT
POS:=$(BIN)/en.mo
OPTS+=-DGETTEXT
endif
endif
ifdef DJGPPDOS
all: pre-build $(BIN)/$(EXENAME)

View File

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

View File

@ -258,6 +258,18 @@ INT32 I_PutEnv(char *variable)
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) {}
#include "../sdl/dosstr.c"

View File

@ -212,7 +212,7 @@ boolean B_CheckRespawn(player_t *player)
// Check if Sonic is busy first.
// 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->powers[pw_carry]))
return false;

View File

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

View File

@ -1165,7 +1165,7 @@ found:
if (var == &cv_forceskin)
{
var->value = R_SkinAvailable(var->string);
if (!R_SkinUnlock(var->value))
if (!R_SkinUsable(-1, var->value))
var->value = -1;
}
else
@ -1361,6 +1361,16 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
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.
if (netgame || multiplayer)
{
@ -1478,7 +1488,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
else if (newvalue >= numskins)
newvalue = -1;
} while ((oldvalue != newvalue)
&& !(R_SkinUnlock(newvalue)));
&& !(R_SkinUsable(-1, newvalue)));
}
else
newvalue = var->value + increment;
@ -1551,34 +1561,27 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if (var == &cv_chooseskin)
{
// 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;
do
if (increment > 0) // Going up!
{
newvalue++;
if (newvalue == MAXSKINS)
newvalue = 0;
} while (var->PossibleValue[newvalue].strvalue == NULL);
var->value = newvalue + 1;
var->string = var->PossibleValue[newvalue].strvalue;
var->func();
return;
}
else if (increment < 0) // Going down!
{
newvalue = var->value - 1;
do
}
else if (increment < 0) // Going down!
{
newvalue--;
if (newvalue == -1)
newvalue = MAXSKINS-1;
} while (var->PossibleValue[newvalue].strvalue == NULL);
var->value = newvalue + 1;
var->string = var->PossibleValue[newvalue].strvalue;
var->func();
return;
}
}
} while (var->PossibleValue[newvalue].strvalue == NULL);
var->value = newvalue + 1;
var->string = var->PossibleValue[newvalue].strvalue;
var->func();
return;
}
#ifdef PARANOIA
if (currentindice == -1)

View File

@ -84,19 +84,23 @@ UINT32 con_scalefactor; // text size scale factor
// hold 32 last lines of input for history
#define CON_MAXPROMPTCHARS 256
#define CON_PROMPTCHAR '>'
#define CON_PROMPTCHAR '$'
static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
static INT32 inputline; // current input line number
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.
static void CON_InputInit(void);
static void CON_RecalcSize(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_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
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"},
{2, "Blue"}, {3, "Green"}, {4, "Gray"},
{5, "Red"}, {0, NULL}};
consvar_t cons_backcolor = {"con_backcolor", "3", CV_SAVE, backcolor_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Gray"}, {2, "Brown"},
{3, "Red"}, {4, "Orange"}, {5, "Yellow"},
{6, "Green"}, {7, "Blue"}, {8, "Purple"},
{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);
@ -219,8 +225,9 @@ static void CONS_Bind_f(void)
// 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 *purplemap;
UINT8 *lgreenmap;
@ -229,44 +236,52 @@ UINT8 *graymap;
UINT8 *redmap;
UINT8 *orangemap;
// Console BG colors
UINT8 *cwhitemap;
UINT8 *corangemap;
UINT8 *cbluemap;
UINT8 *cgreenmap;
UINT8 *cgraymap;
UINT8 *credmap;
// Console BG color
UINT8 *consolebgmap = NULL;
void CON_ReSetupBackColormap(UINT16 num)
void CON_SetupBackColormap(void)
{
UINT16 i, j;
UINT8 k;
UINT8 *pal = W_CacheLumpName(R_GetPalname(num), PU_CACHE);
UINT16 i, palsum;
UINT8 j, palindex, shift;
UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
// setup the green translucent background colormaps
for (i = 0, k = 0; i < 768; i += 3, k++)
if (!consolebgmap)
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];
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));
case 0: palindex = 15; break; // White
case 1: palindex = 31; break; // Gray
case 2: palindex = 239; break; // Brown
case 3: palindex = 47; break; // Red
case 4: palindex = 63; break; // Orange
case 5: palindex = 79; shift = 7; break; // Yellow
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;
UINT8 *pal;
CON_SetupBackColormap();
}
cwhitemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
corangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
cbluemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
cgreenmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
cgraymap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
credmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
static void CON_SetupColormaps(void)
{
INT32 i;
yellowmap = (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);
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
// these don't need to be aligned, unless you convert the
@ -320,6 +321,9 @@ static void CON_SetupBackColormap(void)
redmap[9] = (UINT8)32;
orangemap[3] = (UINT8)52;
orangemap[9] = (UINT8)57;
// Init back colormap
CON_SetupBackColormap();
}
// Setup the console text buffer
@ -343,7 +347,7 @@ void CON_Init(void)
con_width = 0;
CON_RecalcSize();
CON_SetupBackColormap();
CON_SetupColormaps();
//note: CON_Ticker should always execute at least once before D_Display()
con_clipviewtop = -1; // -1 does not clip
@ -386,14 +390,10 @@ void CON_Init(void)
//
static void CON_InputInit(void)
{
INT32 i;
// prepare the first prompt line
memset(inputlines, 0, sizeof (inputlines));
for (i = 0; i < 32; i++)
inputlines[i][0] = CON_PROMPTCHAR;
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
//
boolean CON_Responder(event_t *ev)
{
static boolean consdown;
static boolean shiftdown;
static boolean ctrldown;
static UINT8 consdown = false; // console is treated differently due to rare usage
// sequential completions a la 4dos
static char completion[80];
@ -639,13 +717,8 @@ boolean CON_Responder(event_t *ev)
// let go keyup events, don't eat them
if (ev->type != ev_keydown && ev->type != ev_console)
{
if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
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])
if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
consdown = false;
return false;
}
@ -684,94 +757,110 @@ boolean CON_Responder(event_t *ev)
consoletoggle = true;
return true;
}
}
// eat shift only if console active
if (key == KEY_LSHIFT || key == KEY_RSHIFT)
{
shiftdown = true;
// Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
if (key == KEY_LSHIFT || key == KEY_RSHIFT
|| key == KEY_LCTRL || key == KEY_RCTRL
|| key == KEY_LALT || key == KEY_RALT)
return true;
}
// same for ctrl
if (key == KEY_LCTRL || key == KEY_RCTRL)
// ctrl modifier -- changes behavior, adds shortcuts
if (ctrldown)
{
ctrldown = true;
return true;
// show all cvars/commands that match what we have inputted
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)
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
// remember typing for several completions (a-la-4dos)
if (inputlines[inputline][input_cx-1] != ' ')
if (!completion[0])
{
if (strlen(inputlines[inputline]+1) < 80)
strcpy(completion, inputlines[inputline]+1);
else
completion[0] = 0;
if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
return true;
strcpy(completion, inputlines[inputline]);
comskips = varskips = 0;
}
else
@ -783,37 +872,26 @@ boolean CON_Responder(event_t *ev)
if (--varskips < 0)
comskips = -comskips - 2;
}
else if (comskips > 0)
comskips--;
else if (comskips > 0) comskips--;
}
else
{
if (comskips < 0)
varskips++;
else
comskips++;
if (comskips < 0) varskips++;
else comskips++;
}
}
if (comskips >= 0)
{
cmd = COM_CompleteCommand(completion, comskips);
if (!cmd)
// dirty: make sure if comskips is zero, to have a neg value
if (!cmd) // dirty: make sure if comskips is zero, to have a neg value
comskips = -comskips - 1;
}
if (comskips < 0)
cmd = CV_CompleteVar(completion, varskips);
if (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;
}
CON_InputSetString(va("%s ", cmd));
else
{
if (comskips > 0)
@ -839,47 +917,80 @@ boolean CON_Responder(event_t *ev)
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;
}
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;
}
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
if (key == KEY_ENTER)
{
if (input_cx < 2)
if (!input_len)
return true;
// push the command
COM_BufAddText(inputlines[inputline]+1);
COM_BufAddText(inputlines[inputline]);
COM_BufAddText("\n");
CONS_Printf("%s\n", inputlines[inputline]);
CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]);
inputline = (inputline+1) & 31;
inputhist = inputline;
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
inputlines[inputline][0] = CON_PROMPTCHAR;
input_cx = 1;
CON_InputClear();
return true;
}
// backspace command prompt
if (key == KEY_BACKSPACE)
// backspace and delete command prompt
if (input_sel != input_cur)
{
if (input_cx > 1)
if (key == KEY_BACKSPACE || key == KEY_DEL)
{
input_cx--;
inputlines[inputline][input_cx] = 0;
CON_InputDelSelection();
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;
}
@ -888,18 +999,15 @@ boolean CON_Responder(event_t *ev)
{
// copy one of the previous inputlines to the current
do
{
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
// current line + 1 because we cycle through the 32 input lines
if (inputhist == inputline)
inputhist = (inputline + 1) & 31;
M_Memcpy(inputlines[inputline], inputlines[inputhist], CON_MAXPROMPTCHARS);
input_cx = strlen(inputlines[inputline]);
CON_InputSetString(inputlines[inputhist]);
return true;
}
@ -909,23 +1017,14 @@ boolean CON_Responder(event_t *ev)
if (inputhist == inputline)
return true;
do
{
inputhist = (inputhist + 1) & 31;
} while (inputhist != inputline && !inputlines[inputhist][1]);
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
while (inputhist != inputline && !inputlines[inputhist][0]);
// back to currentline
if (inputhist == inputline)
{
inputlines[inputline][0] = CON_PROMPTCHAR;
input_cx = 1;
}
CON_InputClear();
else
{
strcpy(inputlines[inputline], inputlines[inputhist]);
input_cx = strlen(inputlines[inputline]);
}
CON_InputSetString(inputlines[inputhist]);
return true;
}
@ -950,15 +1049,12 @@ boolean CON_Responder(event_t *ev)
return false;
// 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;
inputlines[inputline][input_cx + 1] = 0;
input_cx++;
}
if (input_sel != input_cur)
CON_InputDelSelection();
CON_InputAddChar(key);
return true;
}
@ -1242,26 +1338,89 @@ void CONS_Error(const char *msg)
//
static void CON_DrawInput(void)
{
char *p;
size_t c;
INT32 x, y;
INT32 charwidth = (INT32)con_scalefactor << 3;
// input line scrolls left if it gets too long
p = inputlines[inputline];
if (input_cx >= con_width-11)
p += input_cx - (con_width-11) + 1;
const char *p = inputlines[inputline];
size_t c, clen, cend;
UINT8 lellip = 0, rellip = 0;
INT32 x, y, i;
y = con_curlines - 12 * con_scalefactor;
x = charwidth*2;
for (c = 0, x = charwidth; c < con_width-11; c++, x += charwidth)
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
clen = con_width-13;
// draw the blinking cursor
//
x = ((input_cx >= con_width-11) ? (INT32)(con_width-11) : (INT32)((input_cx + 1)) * charwidth);
if (con_tick < 4)
V_DrawCharacter(x, y, '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
if (input_len <= clen)
{
c = 0;
clen = input_len;
}
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
@ -1306,11 +1465,11 @@ static void CON_DrawHudlines(void)
else
{
//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;
}
@ -1417,7 +1576,7 @@ static void CON_DrawConsole(void)
{
// inu: no more width (was always 0 and vid.width)
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
@ -1448,7 +1607,7 @@ static void CON_DrawConsole(void)
charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
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;
// Console bg colors:
extern UINT8 *cwhitemap, *corangemap, *cbluemap, *cgreenmap, *cgraymap,
*credmap;
// Console bg color (auto updated to match)
extern UINT8 *consolebgmap;
void CON_ReSetupBackColormap(UINT16 num);
void CON_SetupBackColormap(void);
void CON_ClearHUD(void); // clear heads up messages
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.
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.
PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
@ -76,11 +76,19 @@ typedef enum
NUMPACKETTYPE
} packettype_t;
#ifdef PACKETDROP
void Command_Drop(void);
void Command_Droprate(void);
#endif
#ifdef _DEBUG
void Command_Numnodes(void);
#endif
#if defined(_MSC_VER)
#pragma pack(1)
#endif
// client to server packet
// Client to server packet
typedef struct
{
UINT8 client_tic;
@ -89,7 +97,7 @@ typedef struct
ticcmd_t cmd;
} ATTRPACK clientcmd_pak;
// splitscreen packet
// Splitscreen packet
// WARNING: must have the same format of clientcmd_pak, for more easy use
typedef struct
{
@ -110,16 +118,16 @@ typedef struct
UINT8 starttic;
UINT8 numtics;
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;
// sent to client when all consistency data
// Sent to client when all consistency data
// for players has been restored
typedef struct
{
UINT32 randomseed;
//ctf flag stuff
// CTF flag stuff
SINT8 flagplayer[2];
INT32 flagloose[2];
INT32 flagflags[2];
@ -127,11 +135,11 @@ typedef struct
fixed_t flagy[2];
fixed_t flagz[2];
UINT32 ingame; // spectator bit for each player
UINT32 ctfteam; // if not spectator, then which team?
UINT32 ingame; // Spectator bit for each player
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
UINT32 score[MAXPLAYERS]; // Everyone's score.
UINT32 score[MAXPLAYERS]; // Everyone's score
INT16 numboxes[MAXPLAYERS];
INT16 totalring[MAXPLAYERS];
tic_t realtime[MAXPLAYERS];
@ -140,14 +148,14 @@ typedef struct
typedef struct
{
//player stuff
// Player stuff
UINT8 playernum;
// Do not send anything visual related.
// Only send data that we need to know for physics.
UINT8 playerstate; //playerstate_t
UINT32 pflags; //pflags_t
UINT8 panim; //panim_t
UINT8 playerstate; // playerstate_t
UINT32 pflags; // pflags_t
UINT8 panim; // panim_t
angle_t aiming;
INT32 currentweapon;
@ -155,7 +163,7 @@ typedef struct
UINT16 powers[NUMPOWERS];
// Score is resynched in the confirm resync packet
INT32 health;
INT32 rings;
SINT8 lives;
SINT8 continues;
UINT8 scoreadd;
@ -164,6 +172,7 @@ typedef struct
UINT8 skincolor;
INT32 skin;
UINT32 availabilities;
// Just in case Lua does something like
// modify these at runtime
fixed_t camerascale;
@ -176,9 +185,9 @@ typedef struct
UINT8 charability;
UINT8 charability2;
UINT32 charflags;
UINT32 thokitem; //mobjtype_t
UINT32 spinitem; //mobjtype_t
UINT32 revitem; //mobjtype_t
UINT32 thokitem; // mobjtype_t
UINT32 spinitem; // mobjtype_t
UINT32 revitem; // mobjtype_t
fixed_t actionspd;
fixed_t mindash;
fixed_t maxdash;
@ -187,7 +196,6 @@ typedef struct
fixed_t playerspinheight;
fixed_t speed;
UINT8 jumping;
UINT8 secondjump;
UINT8 fly1;
tic_t glidetime;
@ -234,8 +242,9 @@ typedef struct
INT32 onconveyor;
//player->mo stuff
UINT8 hasmo; //boolean
UINT8 hasmo; // Boolean
INT32 health;
angle_t angle;
fixed_t x;
fixed_t y;
@ -261,10 +270,10 @@ typedef struct
typedef struct
{
UINT8 version; // different versions don't work
UINT8 subversion; // contains build version
UINT8 version; // Different versions don't work
UINT8 subversion; // Contains build version
// server launch stuffs
// Server launch stuffs
UINT8 serverplayer;
UINT8 totalslotnum; // "Slots": highest player number in use plus one.
@ -275,21 +284,22 @@ typedef struct
// 0xFF == not in game; else player skin num
UINT8 playerskins[MAXPLAYERS];
UINT8 playercolor[MAXPLAYERS];
UINT32 playeravailabilities[MAXPLAYERS];
UINT8 gametype;
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;
typedef struct {
UINT8 fileid;
UINT32 position;
UINT16 size;
UINT8 data[0]; // size is variable using hardware_MAXPACKETLENGTH
UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH
} ATTRPACK filetx_pak;
#ifdef _MSC_VER
@ -298,14 +308,14 @@ typedef struct {
typedef struct
{
UINT8 version; // different versions don't work
UINT8 subversion; // contains build version
UINT8 version; // Different versions don't work
UINT8 subversion; // Contains build version
UINT8 localplayers;
UINT8 mode;
} ATTRPACK clientconfig_pak;
#define MAXSERVERNAME 32
// this packet is too large
// This packet is too large
typedef struct
{
UINT8 version;
@ -371,45 +381,45 @@ typedef struct
} ATTRPACK plrconfig;
//
// Network packet data.
// Network packet data
//
typedef struct
{
UINT32 checksum;
UINT8 ack; // if not null the node asks for acknowledgement, the receiver must resend the ack
UINT8 ackreturn; // the return of the ack number
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 packettype;
UINT8 reserved; // padding
UINT8 reserved; // Padding
union
{
clientcmd_pak clientpak; // 144 bytes
client2cmd_pak client2pak; // 200 bytes
servertics_pak serverpak; // 132495 bytes
serverconfig_pak servercfg; // 773 bytes
resynchend_pak resynchend; //
resynch_pak resynchpak; //
UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes
filetx_pak filetxpak; // 139 bytes
clientconfig_pak clientcfg; // 136 bytes
serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes
askinfo_pak askinfo; // 61 bytes
msaskinfo_pak msaskinfo; // 22 bytes
plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes
plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes
clientcmd_pak clientpak; // 144 bytes
client2cmd_pak client2pak; // 200 bytes
servertics_pak serverpak; // 132495 bytes (more around 360, no?)
serverconfig_pak servercfg; // 773 bytes
resynchend_pak resynchend; //
resynch_pak resynchpak; //
UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...)
filetx_pak filetxpak; // 139 bytes
clientconfig_pak clientcfg; // 136 bytes
serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
askinfo_pak askinfo; // 61 bytes
msaskinfo_pak msaskinfo; // 22 bytes
plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes (I'd say 36~38)
plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE)
#ifdef NEWPING
UINT32 pingtable[MAXPLAYERS]; // 128 bytes
UINT32 pingtable[MAXPLAYERS]; // 128 bytes
#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;
#if defined(_MSC_VER)
#pragma pack()
#endif
#define MAXSERVERLIST 64 // depends only on the display
#define MAXSERVERLIST 64 // Depends only on the display
typedef struct
{
SINT8 node;
@ -420,7 +430,7 @@ extern serverelem_t serverlist[MAXSERVERLIST];
extern UINT32 serverlistcount;
extern INT32 mapchangepending;
// points inside doomcom
// Points inside doomcom
extern doomdata_t *netbuffer;
extern consvar_t cv_playbackspeed;
@ -441,26 +451,28 @@ extern consvar_t cv_playbackspeed;
#define KICK_MSG_CUSTOM_BAN 8
extern boolean server;
extern boolean dedicated; // for dedicated server
#define client (!server)
extern boolean dedicated; // For dedicated server
extern UINT16 software_MAXPACKETLENGTH;
extern boolean acceptnewnode;
extern SINT8 servernode;
void Command_Ping_f(void);
extern tic_t connectiontimeout;
extern tic_t jointimeout;
#ifdef NEWPING
extern UINT16 pingmeasurecount;
extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS];
#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);
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 SendNetXCmd(netxcmd_t id, const void *param, size_t nparam);
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_ClearPlayer(INT32 playernum);
void CL_UpdateServerList(boolean internetsearch, INT32 room);
// is there a game running
// Is there a game running
boolean Playing(void);
// Broadcasts special packets to other players
// to notify of game exit
void D_QuitNetGame(void);
//? how many ticks to run?
//? How many ticks to run?
void TryRunTics(tic_t realtic);
// 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 "m_cond.h" // condition initialization
#include "fastcmp.h"
#include "keys.h"
#ifdef CMAKECONFIG
#include "config.h"
@ -106,8 +107,6 @@ UINT8 window_notinfocus = false;
//
// DEMO LOOP
//
//static INT32 demosequence;
static const char *pagename = "MAP1PIC";
static char *startupwadfiles[MAX_WADFILES];
boolean devparm = false; // started game with -devparm
@ -176,6 +175,38 @@ void D_PostEvent(const event_t *ev)
void D_PostEvent_end(void) {};
#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
// Send all the events of the given timestamp down the responder chain
@ -188,6 +219,9 @@ void D_ProcessEvents(void)
{
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.
if (M_ScreenshotResponder(ev))
continue; // ate the event
@ -684,9 +718,7 @@ void D_StartTitle(void)
maptol = 0;
gameaction = ga_nothing;
playerdeadview = false;
displayplayer = consoleplayer = 0;
//demosequence = -1;
gametype = GT_COOP;
paused = false;
advancedemo = false;
@ -837,29 +869,24 @@ static void IdentifyVersion(void)
I_Error("File %s has been modified with non-music lumps",musicfile);
}
#endif
}
/* ======================================================================== */
// Just print the nice red titlebar like the original SRB2 for DOS.
/* ======================================================================== */
#ifdef PC_DOS
static inline void D_Titlebar(char *title1, char *title2)
{
// 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);
}
#if 1 // This section can be deleted when music_new is merged with music.dta
{
const char *musicfile = "music_new.dta";
const char *musicpath = va(pandf,srb2waddir,musicfile);
int ms = W_VerifyNMUSlumps(musicpath); // Don't forget the music!
if (ms == 1)
D_AddFile(musicpath);
else if (ms == 0)
I_Error("File %s has been modified with non-music lumps",musicfile);
}
#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.
@ -888,6 +915,31 @@ static inline void D_MakeTitleString(char *s)
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
@ -895,8 +947,6 @@ static inline void D_MakeTitleString(char *s)
void D_SRB2Main(void)
{
INT32 p;
char srb2[82]; // srb2 title banner
char title[82];
INT32 pstartmap = 1;
boolean autostart = false;
@ -939,20 +989,8 @@ void D_SRB2Main(void)
dedicated = M_CheckParm("-dedicated") != 0;
#endif
strcpy(title, "Sonic Robo Blast 2");
strcpy(srb2, "Sonic Robo Blast 2");
D_MakeTitleString(srb2);
#ifdef PC_DOS
D_Titlebar(srb2, title);
#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';
D_Titlebar();
#endif
if (devparm)
@ -1352,7 +1390,6 @@ void D_SRB2Main(void)
if (dedicated && server)
{
pagename = "TITLESKY";
levelstarttic = gametic;
G_SetGamestate(GS_LEVEL);
if (!P_SetupLevel(false))

View File

@ -34,7 +34,7 @@ void D_SRB2Loop(void) FUNCNORETURN;
// D_SRB2Main()
// Not a globally visible function, just included for source reference,
// 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);
@ -51,9 +51,6 @@ const char *D_Home(void);
//
// 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_StartTitle(void);

View File

@ -31,25 +31,27 @@
//
// NETWORKING
//
// gametic is the tic about to be (or currently being) run
// server:
// gametic is the tic about to (or currently being) run
// Server:
// maketic is the tic that hasn't had control made for it yet
// nettics: is the tic for each node
// firsttictosend: is the lowest value of nettics
// client:
// neededtic: is the tic needed by the client to run the game
// firsttictosend: is used to optimize a condition
// normally maketic >= gametic > 0
// nettics is the tic for each node
// firstticstosend is the lowest value of nettics
// Client:
// neededtic is the tic needed by the client to run the game
// firstticstosend is used to optimize a condition
// Normally maketic >= gametic > 0
#define FORCECLOSE 0x8000
tic_t connectiontimeout = (15*TICRATE);
tic_t connectiontimeout = (10*TICRATE);
/// \brief network packet
doomcom_t *doomcom = NULL;
/// \brief network packet data, points inside doomcom
doomdata_t *netbuffer = NULL;
#ifdef DEBUGFILE
FILE *debugfile = NULL; // put some net info in a file during the game
#endif
#define MAXREBOUND 8
static doomdata_t reboundstore[MAXREBOUND];
@ -62,7 +64,7 @@ INT32 net_bandwidth;
/// \brief max length per packet
INT16 hardware_MAXPACKETLENGTH;
void (*I_NetGet)(void) = NULL;
boolean (*I_NetGet)(void) = NULL;
void (*I_NetSend)(void) = NULL;
boolean (*I_NetCanSend)(void) = NULL;
boolean (*I_NetCanGet)(void) = NULL;
@ -129,9 +131,9 @@ boolean Net_GetNetStat(void)
// -----------------------------------------------------------------
// 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 URGENTFREESLOTENUM 10
#define URGENTFREESLOTNUM 10
#define ACKTOSENDTIMEOUT (TICRATE/11)
#ifndef NONET
@ -139,10 +141,10 @@ typedef struct
{
UINT8 acknum;
UINT8 nextacknum;
UINT8 destinationnode;
tic_t senttime;
UINT16 length;
UINT16 resentnum;
UINT8 destinationnode; // The node to send the ack to
tic_t senttime; // The time when the ack was sent
UINT16 length; // The packet size
UINT16 resentnum; // The number of times the ack has been resent
union {
SINT8 raw[MAXPACKETLENGTH];
doomdata_t data;
@ -152,11 +154,12 @@ typedef struct
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;
#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];
#endif
@ -212,11 +215,16 @@ FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b)
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)
{
node_t *node = &nodes[doomcom->remotenode];
INT32 i, numfreeslote = 0;
INT32 i, numfreeslot = 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++)
if (!ackpak[i].acknum)
{
// for low priority packet, make sure let freeslotes so urgents packets can be sent
numfreeslote++;
if (netbuffer->packettype >= PT_CANFAIL && numfreeslote < URGENTFREESLOTENUM)
continue;
// For low priority packets, make sure to let freeslots so urgent packets can be sent
if (netbuffer->packettype >= PT_CANFAIL)
{
numfreeslot++;
if (numfreeslot <= URGENTFREESLOTNUM)
continue;
}
ackpak[i].acknum = node->nextacknum;
ackpak[i].nextacknum = node->nextacknum;
@ -241,7 +252,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
ackpak[i].length = doomcom->datalength;
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].resentnum = 1;
}
@ -254,7 +265,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
*freeack = ackpak[i].acknum;
sendackpacket++; // for stat
sendackpacket++; // For stat
return true;
}
@ -266,14 +277,46 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
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)
{
nodes[node].lasttimeacktosend_sent = I_GetTime();
return nodes[node].firstacktosend;
}
static void Removeack(INT32 i)
static void RemoveAck(INT32 i)
{
INT32 node = ackpak[i].destinationnode;
#ifndef NEWPING
@ -290,31 +333,31 @@ static void Removeack(INT32 i)
DEBFILE(va("Remove ack %d\n",ackpak[i].acknum));
#endif
ackpak[i].acknum = 0;
if (nodes[node].flags & CLOSE)
if (nodes[node].flags & NF_CLOSE)
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)
{
INT32 i;
boolean goodpacket = true;
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)
{
node->remotefirstack = netbuffer->ackreturn;
// search the ackbuffer and free it
// Search the ackbuffer and free it
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes
&& 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)
{
UINT8 ack = netbuffer->ack;
@ -323,23 +366,23 @@ static boolean Processackpak(void)
{
DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack));
duppacket++;
goodpacket = false; // discard packet (duplicate)
goodpacket = false; // Discard packet (duplicate)
}
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)
if (node->acktosend[i] == ack)
{
DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack));
duppacket++;
goodpacket = false; // discard packet (duplicate)
goodpacket = false; // Discard packet (duplicate)
break;
}
if (goodpacket)
{
// is a good packet so increment the acknowledge number,
// then search for a "hole" in the queue
// Is a good packet so increment the acknowledge number,
// Then search for a "hole" in the queue
UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1);
if (!nextfirstack)
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
// will be incremented when the nextfirstack comes (code above)
// Don't increment firsacktosend, put it in asktosend queue
// Will be incremented when the nextfirstack comes (code above)
UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND);
DEBFILE(va("out of order packet (%d expected)\n", nextfirstack));
if (newhead != node->acktosend_tail)
@ -394,8 +437,8 @@ static boolean Processackpak(void)
node->acktosend[node->acktosend_head] = ack;
node->acktosend_head = newhead;
}
else // buffer full discard packet, sender will resend it
{ // we can admit the packet but we will not detect the duplication after :(
else // Buffer full discard packet, sender will resend it
{ // We can admit the packet but we will not detect the duplication after :(
DEBFILE("no more freeackret\n");
goodpacket = false;
}
@ -430,25 +473,29 @@ static void GotAcks(void)
if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode)
{
if (ackpak[i].acknum == netbuffer->u.textcmd[j])
Removeack(i);
else
// nextacknum is first equal to acknum, then when receiving bigger ack
// there is big chance the packet is lost
// when resent, nextacknum = nodes[node].nextacknum
// will redo the same but with different value
if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
&& ackpak[i].senttime > 0)
{
ackpak[i].senttime--; // hurry up
}
RemoveAck(i);
// nextacknum is first equal to acknum, then when receiving bigger ack
// there is big chance the packet is lost
// When resent, nextacknum = nodes[node].nextacknum
// will redo the same but with different value
else if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
&& ackpak[i].senttime > 0)
{
ackpak[i].senttime--; // hurry up
}
}
}
#endif
static inline void Net_ConnectionTimeout(INT32 node)
void Net_ConnectionTimeout(INT32 node)
{
// send a very special packet to self (hack the reboundstore queue)
// main code will handle it
// Don't timeout several times
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].ack = 0;
reboundstore[rebound_head].ackreturn = 0;
@ -456,12 +503,12 @@ static inline void Net_ConnectionTimeout(INT32 node)
reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1);
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!)
nodes[node].lasttimepacketreceived = I_GetTime();
}
// resend the data if needed
// Resend the data if needed
void Net_AckTicker(void)
{
#ifndef NONET
@ -477,7 +524,7 @@ void Net_AckTicker(void)
if (ackpak[i].acknum && ackpak[i].senttime + node->timeout < I_GetTime())
#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",
i, nodei));
@ -497,7 +544,7 @@ void Net_AckTicker(void)
ackpak[i].senttime = I_GetTime();
ackpak[i].resentnum++;
ackpak[i].nextacknum = node->nextacknum;
retransmit++; // for stat
retransmit++; // For stat
HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum,
(size_t)(ackpak[i].length - BASEPACKETSIZE));
}
@ -505,15 +552,15 @@ void Net_AckTicker(void)
for (i = 1; i < MAXNETNODES; i++)
{
// this is something like node open flag
// This is something like node open flag
if (nodes[i].firstacktosend)
{
// we haven't sent a packet for a long time
// acknowledge packet if needed
// We haven't sent a packet for a long time
// Acknowledge packet if needed
if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime())
Net_SendAcks(i);
if (!(nodes[i].flags & CLOSE)
if (!(nodes[i].flags & NF_CLOSE)
&& nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime())
{
Net_ConnectionTimeout(i);
@ -523,9 +570,9 @@ void Net_AckTicker(void)
#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 ....)
void Net_UnAcknowledgPacket(INT32 node)
void Net_UnAcknowledgePacket(INT32 node)
{
#ifdef NONET
(void)node;
@ -564,20 +611,29 @@ void Net_UnAcknowledgPacket(INT32 node)
#endif
}
boolean Net_AllAckReceived(void)
{
#ifndef NONET
/** Checks if all acks have been received
*
* \return True if all acks have been received
*
*/
static boolean Net_AllAcksReceived(void)
{
INT32 i;
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum)
return false;
#endif
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)
{
#ifdef NONET
@ -587,7 +643,7 @@ void Net_WaitAllAckReceived(UINT32 timeout)
timeout = tictac + timeout*NEWTICRATE;
HGetPacket();
while (timeout > I_GetTime() && !Net_AllAckReceived())
while (timeout > I_GetTime() && !Net_AllAcksReceived())
{
while (tictac == I_GetTime())
I_Sleep();
@ -598,18 +654,18 @@ void Net_WaitAllAckReceived(UINT32 timeout)
#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
nodes[node].ping = PINGDEFAULT;
nodes[node].varping = VARPINGDEFAULT;
nodes[node].timeout = TIMEOUT(nodes[node].ping,nodes[node].varping);
node->ping = PINGDEFAULT;
node->varping = VARPINGDEFAULT;
node->timeout = TIMEOUT(node->ping, node->varping);
#endif
nodes[node].firstacktosend = 0;
nodes[node].nextacknum = 1;
nodes[node].remotefirstack = 0;
nodes[node].flags = 0;
node->firstacktosend = 0;
node->nextacknum = 1;
node->remotefirstack = 0;
node->flags = 0;
}
static void InitAck(void)
@ -622,9 +678,14 @@ static void InitAck(void)
#endif
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)
{
#ifdef NONET
@ -652,12 +713,25 @@ void Net_CloseConnection(INT32 node)
#else
INT32 i;
boolean forceclose = (node & FORCECLOSE) != 0;
if (node == -1)
{
DEBFILE(M_GetText("Net_CloseConnection: node -1 detected!\n"));
return; // nope, just ignore it
}
node &= ~FORCECLOSE;
if (!node)
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)
if (GetAcktosend(node))
@ -676,8 +750,8 @@ void Net_CloseConnection(INT32 node)
ackpak[i].acknum = 0;
}
InitNode(node);
AbortSendFiles(node);
InitNode(&nodes[node]);
SV_AbortSendFiles(node);
I_NetFreeNodenum(node);
#endif
}
@ -729,9 +803,15 @@ static void fprintfstring(char *s, size_t len)
}
if (mode)
fprintf(debugfile, "]");
}
static void fprintfstringnewline(char *s, size_t len)
{
fprintfstring(s, len);
fprintf(debugfile, "\n");
}
/// \warning Keep this up-to-date if you add/remove/rename packet types
static const char *packettypename[NUMPACKETTYPE] =
{
"NOTHING",
@ -749,15 +829,22 @@ static const char *packettypename[NUMPACKETTYPE] =
"ASKINFO",
"SERVERINFO",
"PLAYERINFO",
"REQUESTFILE",
"ASKINFOVIAMS",
"PLAYERCONFIGS",
"RESYNCHEND",
"RESYNCHGET",
"FILEFRAGMENT",
"TEXTCMD",
"TEXTCMD2",
"CLIENTJOIN",
"NODETIMEOUT",
"RESYNCHING",
#ifdef NEWPING
"PING"
#endif
};
static void DebugPrintpacket(const char *header)
@ -770,20 +857,31 @@ static void DebugPrintpacket(const char *header)
{
case PT_ASKINFO:
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;
case PT_CLIENTJOIN:
fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers,
netbuffer->u.clientcfg.mode);
break;
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 ",
(UINT32)ExpandTics(netbuffer->u.serverpak.starttic), netbuffer->u.serverpak.numslots,
netbuffer->u.serverpak.numtics,
sizeu1((size_t)(&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics])));
fprintfstring((char *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics],(size_t)(
&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics]));
(UINT32)ExpandTics(serverpak->starttic), serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd));
/// \todo Display more readable information about net commands
fprintfstringnewline((char *)cmd, ntxtcmd);
/*fprintfstring((char *)cmd, 3);
if (ntxtcmd > 4)
{
fprintf(debugfile, "[%s]", netxcmdnames[*((cmd) + 3) - 1]);
fprintfstring(((char *)cmd) + 4, ntxtcmd - 4);
}
fprintf(debugfile, "\n");*/
break;
}
case PT_CLIENTCMD:
case PT_CLIENT2CMD:
case PT_CLIENTMIS:
@ -797,7 +895,8 @@ static void DebugPrintpacket(const char *header)
case PT_TEXTCMD:
case PT_TEXTCMD2:
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;
case PT_SERVERCFG:
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.fileneedednum,
(UINT32)LONG(netbuffer->u.serverinfo.time));
fprintfstring((char *)netbuffer->u.serverinfo.fileneeded,
fprintfstringnewline((char *)netbuffer->u.serverinfo.fileneeded,
(UINT8)((UINT8 *)netbuffer + doomcom->datalength
- (UINT8 *)netbuffer->u.serverinfo.fileneeded));
break;
@ -827,20 +926,102 @@ static void DebugPrintpacket(const char *header)
break;
case PT_REQUESTFILE:
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));
break;
}
}
#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
//
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength)
{
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)
{
@ -871,7 +1052,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
(void)reliable;
(void)acknum;
#else
// do this before GetFreeAcknum because this function backup
// do this before GetFreeAcknum because this function backups
// the current packet
doomcom->remotenode = (INT16)node;
if (doomcom->datalength <= 0)
@ -884,7 +1065,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
return false;
}
if (node < MAXNETNODES) // can be a broadcast
if (node < MAXNETNODES) // Can be a broadcast
netbuffer->ackreturn = GetAcktosend(node);
else
netbuffer->ackreturn = 0;
@ -905,20 +1086,30 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
netbuffer->ack = acknum;
netbuffer->checksum = NetbufferChecksum();
sendbytes += packetheaderlength + doomcom->datalength; // for stat
sendbytes += packetheaderlength + doomcom->datalength; // For stat
// simulate internet :)
if (true || rand()<(INT32)RAND_MAX/5)
#ifdef PACKETDROP
// Simulate internet :)
//if (rand() >= (INT32)(RAND_MAX * (PACKETLOSSRATE / 100.f)))
if (!ShouldDropPacket())
{
#endif
#ifdef DEBUGFILE
if (debugfile)
DebugPrintpacket("SEND");
DebugPrintpacket("SENT");
#endif
I_NetSend();
#ifdef PACKETDROP
}
else
{
if (packetdropquantity[netbuffer->packettype] > 0)
packetdropquantity[netbuffer->packettype]--;
#ifdef DEBUGFILE
else if (debugfile)
DebugPrintpacket("NOTSEND");
if (debugfile)
DebugPrintpacket("NOT SENT");
#endif
}
#endif
#endif // ndef NONET
@ -933,7 +1124,9 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
//
boolean HGetPacket(void)
{
// get a packet from self
//boolean nodejustjoined;
// Get a packet from self
if (rebound_tail != rebound_head)
{
M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]);
@ -958,16 +1151,17 @@ boolean HGetPacket(void)
while(true)
{
//nodejustjoined = I_NetGet();
I_NetGet();
if (doomcom->remotenode == -1)
if (doomcom->remotenode == -1) // No packet received
return false;
getbytes += packetheaderlength + doomcom->datalength; // for stat
getbytes += packetheaderlength + doomcom->datalength; // For stat
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;
}
@ -976,6 +1170,7 @@ boolean HGetPacket(void)
if (netbuffer->checksum != NetbufferChecksum())
{
DEBFILE("Bad packet checksum\n");
//Net_CloseConnection(nodejustjoined ? (doomcom->remotenode | FORCECLOSE) : doomcom->remotenode);
Net_CloseConnection(doomcom->remotenode);
continue;
}
@ -985,11 +1180,26 @@ boolean HGetPacket(void)
DebugPrintpacket("GET");
#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())
continue; // discarded (duplicated)
// a packet with just ackreturn
// A packet with just ackreturn
if (netbuffer->packettype == PT_NOTHING)
{
GotAcks();
@ -1002,9 +1212,10 @@ boolean HGetPacket(void)
return true;
}
static void Internal_Get(void)
static boolean Internal_Get(void)
{
doomcom->remotenode = -1;
return false;
}
FUNCNORETURN static ATTRNORETURN void Internal_Send(void)
@ -1089,7 +1300,7 @@ boolean D_CheckNetGame(void)
if (netgame)
ret = true;
if (!server && netgame)
if (client && netgame)
netgame = false;
server = true; // WTF? server always true???
// no! The deault mode is server. Client is set elsewhere
@ -1230,4 +1441,6 @@ void D_CloseConnection(void)
netgame = false;
addedtogame = false;
}
D_ResetTiccmds();
}

View File

@ -18,10 +18,10 @@
#ifndef __D_NET__
#define __D_NET__
// Max computers in a game.
// Max computers in a game
#define MAXNETNODES 32
#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)
@ -32,17 +32,17 @@ extern float lostpercent, duppercent, gamelostpercent;
extern INT32 packetheaderlength;
boolean Net_GetNetStat(void);
extern INT32 getbytes;
extern INT64 sendbytes; // realtime updated
extern INT64 sendbytes; // Realtime updated
extern SINT8 nodetoplayer[MAXNETNODES];
extern SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
extern UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
extern boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen)
extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen
extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game
INT32 Net_GetFreeAcks(boolean urgent);
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,
size_t packetlength);
boolean HGetPacket(void);
@ -52,9 +52,11 @@ void D_SaveBan(void);
#endif
boolean D_CheckNetGame(void);
void D_CloseConnection(void);
void Net_UnAcknowledgPacket(INT32 node);
void Net_UnAcknowledgePacket(INT32 node);
void Net_CloseConnection(INT32 node);
void Net_ConnectionTimeout(INT32 node);
void Net_AbortPacketType(UINT8 packettype);
void Net_SendAcks(INT32 node);
void Net_WaitAllAckReceived(UINT32 timeout);
#endif

View File

@ -82,6 +82,7 @@ static void AutoBalance_OnChange(void);
static void TeamScramble_OnChange(void);
static void NetTimeout_OnChange(void);
static void JoinTimeout_OnChange(void);
static void Ringslinger_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 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}};
static CV_PossibleValue_t matchboxes_cons_t[] = {{0, "Normal"}, {1, "Random"}, {2, "Non-Random"},
{3, "None"}, {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 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};
// 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_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
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
consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
@ -365,6 +366,35 @@ boolean splitscreen = false;
boolean circuitmap = false;
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
// =========================================================================
@ -453,7 +483,6 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_itemrespawntime);
CV_RegisterVar(&cv_itemrespawn);
CV_RegisterVar(&cv_flagtime);
CV_RegisterVar(&cv_suddendeath);
// misc
CV_RegisterVar(&cv_friendlyfire);
@ -501,7 +530,6 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_startinglives);
CV_RegisterVar(&cv_countdowntime);
CV_RegisterVar(&cv_runscripts);
CV_RegisterVar(&cv_match_scoring);
CV_RegisterVar(&cv_overtime);
CV_RegisterVar(&cv_pause);
CV_RegisterVar(&cv_mute);
@ -517,9 +545,12 @@ void D_RegisterServerCommands(void)
// d_clisrv
CV_RegisterVar(&cv_maxplayers);
CV_RegisterVar(&cv_maxsend);
CV_RegisterVar(&cv_noticedownload);
CV_RegisterVar(&cv_downloadspeed);
COM_AddCommand("ping", Command_Ping_f);
CV_RegisterVar(&cv_nettimeout);
CV_RegisterVar(&cv_jointimeout);
CV_RegisterVar(&cv_skipmapcheck);
CV_RegisterVar(&cv_sleep);
@ -581,6 +612,7 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_screenshot_option);
CV_RegisterVar(&cv_screenshot_folder);
CV_RegisterVar(&cv_screenshot_colorprofile);
CV_RegisterVar(&cv_moviemode);
// PNG variables
CV_RegisterVar(&cv_zlib_level);
@ -638,7 +670,29 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_resetmusic);
// 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
CV_RegisterVar(&cv_crosshair);
@ -696,6 +750,7 @@ void D_RegisterClientCommands(void)
// s_sound.c
CV_RegisterVar(&cv_soundvolume);
CV_RegisterVar(&cv_closedcaptioning);
CV_RegisterVar(&cv_digmusicvolume);
CV_RegisterVar(&cv_midimusicvolume);
CV_RegisterVar(&cv_numChannels);
@ -976,7 +1031,7 @@ UINT8 CanChangeSkin(INT32 playernum)
return true;
// 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;
// Can change skin in intermission and whatnot.
@ -1074,6 +1129,8 @@ static void SendNameAndColor(void)
if (!Playing())
return;
players[consoleplayer].availabilities = R_GetSkinAvailabilities();
// If you're not in a netgame, merely update the skin, color, and name.
if (!netgame)
{
@ -1092,7 +1149,7 @@ static void SendNameAndColor(void)
SetPlayerSkinByNum(consoleplayer, 0);
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;
@ -1139,7 +1196,7 @@ static void SendNameAndColor(void)
// check if player has the skin loaded (cv_skin may have
// the name of a skin that was available in the previous game)
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_skin.value = 0;
@ -1147,6 +1204,7 @@ static void SendNameAndColor(void)
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME);
WRITEUINT32(p, (UINT32)players[consoleplayer].availabilities);
WRITEUINT8(p, (UINT8)cv_playercolor.value);
WRITEUINT8(p, (UINT8)cv_skin.value);
SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf);
@ -1189,6 +1247,8 @@ static void SendNameAndColor2(void)
if (!Playing())
return;
players[secondplaya].availabilities = R_GetSkinAvailabilities();
// If you're not in a netgame, merely update the skin, color, and name.
if (botingame)
{
@ -1217,7 +1277,7 @@ static void SendNameAndColor2(void)
SetPlayerSkinByNum(secondplaya, forcedskin);
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;
@ -1272,6 +1332,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
#endif
READSTRINGN(*cp, name, MAXPLAYERNAME);
p->availabilities = READUINT32(*cp);
color = 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]))
{
boolean kick = false;
INT32 s;
// team colors
if (G_GametypeHasTeams())
@ -1302,6 +1364,16 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
if (!p->skincolor)
kick = true;
// availabilities
for (s = 0; s < MAXSKINS; s++)
{
if (!skins[s].availability && (p->availabilities & (1 << s)))
{
kick = true;
break;
}
}
if (kick)
{
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.
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",
mapnum, newgametype, pultmode, resetplayers, delay, skipprecutscene);
if (netgame || multiplayer)
if ((netgame || multiplayer) && !((gametype == newgametype) && (newgametype == GT_COOP)))
FLS = false;
if (delay != 2)
@ -1534,8 +1609,13 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
mapchangepending = 0;
// spawn the server if needed
// reset players if there is a new one
if (!(adminplayer == consoleplayer) && SV_SpawnServer())
buf[0] &= ~(1<<1);
if (!(adminplayer == consoleplayer))
{
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
if (botskin)
@ -1587,7 +1667,7 @@ static void Command_Map_f(void)
return;
}
if (!server && !(adminplayer == consoleplayer))
if (client && !(adminplayer == consoleplayer))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
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
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
// Alternatively, bail if the map header is completely missing anyway.
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);
return;
}
else
fromlevelselect = ((netgame || multiplayer) && ((gametype == newgametype) && (newgametype == GT_COOP)));
// 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;
}
fromlevelselect = false;
D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, false);
D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect);
}
/** Receives a map command and changes the map.
@ -1765,17 +1846,14 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
if (demoplayback && !timingdemo)
precache = false;
if (resetplayer)
{
if (!FLS || (netgame || multiplayer))
emeralds = 0;
}
if (resetplayer && !FLS)
emeralds = 0;
#ifdef HAVE_BLUA
LUAh_MapChange();
#endif
G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene);
G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene, FLS);
if (demoplayback && !timingdemo)
precache = true;
CON_ToggleOff();
@ -1914,7 +1992,7 @@ static void Got_Suicide(UINT8 **cp, INT32 playernum)
// You can't suicide someone else. Nice try, there.
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)
{
XBOXSTATIC UINT8 buf[2];
@ -2081,7 +2159,7 @@ static void Command_Teamchange_f(void)
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"));
return;
@ -2178,7 +2256,7 @@ static void Command_Teamchange2_f(void)
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"));
return;
@ -2574,7 +2652,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
if (players[playernum].spectator)
{
players[playernum].score = 0;
players[playernum].health = 1;
players[playernum].rings = 0;
if (players[playernum].mo)
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.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else
if (!server) // cannot change remotely
if (client) // cannot change remotely
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
@ -2688,7 +2766,7 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
READMEM(*cp, sentmd5, 16);
if (!server)
if (client)
return;
// Do the final pass to compare with the sent md5
@ -2710,7 +2788,7 @@ static void Command_Verify_f(void)
char *temp;
INT32 playernum;
if (!server)
if (client)
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
@ -2794,7 +2872,7 @@ static void Command_MotD_f(void)
return;
}
if ((netgame || multiplayer) && !server)
if ((netgame || multiplayer) && client)
SendNetXCmd(XD_SETMOTD, mymotd, sizeof(motd));
else
{
@ -2932,6 +3010,7 @@ static void Command_Addfile(void)
XBOXSTATIC char buf[256];
char *buf_p = buf;
INT32 i;
int musiconly; // W_VerifyNMUSlumps isn't boolean
if (COM_Argc() != 2)
{
@ -2946,7 +3025,9 @@ static void Command_Addfile(void)
if (!isprint(fn[i]) || fn[i] == ';')
return;
if (!W_VerifyNMUSlumps(fn))
musiconly = W_VerifyNMUSlumps(fn);
if (!musiconly)
{
// ... But only so long as they contain nothing more then music and sprites.
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.
if (!(netgame || multiplayer) || W_VerifyNMUSlumps(fn))
if (!(netgame || multiplayer) || musiconly)
{
P_AddWadFile(fn, NULL);
return;
@ -2978,9 +3059,7 @@ static void Command_Addfile(void)
#else
FILE *fhandle;
fhandle = fopen(fn, "rb");
if (fhandle)
if ((fhandle = W_OpenWadFile(&fn, true)) != NULL)
{
tic_t t = I_GetTime();
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);
fclose(fhandle);
}
else
{
CONS_Printf(M_GetText("File %s not found.\n"), fn);
else // file not found
return;
}
#endif
WRITEMEM(buf_p, md5sum, 16);
}
@ -3045,13 +3121,19 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
filestatus_t ncs = FS_NOTFOUND;
UINT8 md5sum[16];
boolean kick = false;
boolean toomany = false;
INT32 i;
size_t packetsize = 0;
serverinfo_pak *dummycheck = NULL;
// Shut the compiler up.
(void)dummycheck;
READSTRINGN(*cp, filename, 240);
READMEM(*cp, md5sum, 16);
// Only the server processes this message.
if (!server)
if (client)
return;
// Disallow non-printing characters and semicolons.
@ -3071,13 +3153,25 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
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];
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);
else if (ncs == FS_MD5SUMBAD)
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);
if (ncs != FS_FOUND)
if (ncs != FS_FOUND || !P_AddWadFile(filename, NULL))
{
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);
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;
}
P_AddWadFile(filename, NULL);
G_SetGameModified(true);
}
@ -3318,6 +3416,11 @@ static void NetTimeout_OnChange(void)
connectiontimeout = (tic_t)cv_nettimeout.value;
}
static void JoinTimeout_OnChange(void)
{
jointimeout = (tic_t)cv_jointimeout.value;
}
UINT32 timelimitintics = 0;
/** Deals with a timelimit change by printing the change to the console.
@ -3960,7 +4063,7 @@ static void Command_Archivetest_f(void)
}
// assign mobjnum
i = 0;
i = 1;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
((mobj_t *)th)->mobjnum = i++;
@ -4002,13 +4105,6 @@ static void Command_Archivetest_f(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!
if (!(netgame || multiplayer))
return;
@ -4017,7 +4113,7 @@ static void ForceSkin_OnChange(void)
CONS_Printf("The server has lifted the forced skin restrictions.\n");
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);
}
}
@ -4056,7 +4152,7 @@ static void Skin_OnChange(void)
return; // do whatever you want
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);
return;
@ -4099,8 +4195,7 @@ static void Color_OnChange(void)
if (!Playing())
return; // do whatever you want
if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player.
&& (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CONTINUING))
if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player.
{
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return;

View File

@ -20,6 +20,12 @@
// console vars
extern consvar_t cv_playername;
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
extern consvar_t cv_seenames, cv_allowseenames;
#endif
@ -32,7 +38,6 @@ extern consvar_t cv_joyport2;
#endif
extern consvar_t cv_joyscale;
extern consvar_t cv_joyscale2;
extern consvar_t cv_controlperkey;
// splitscreen with second mouse
extern consvar_t cv_mouse2port;
@ -40,25 +45,12 @@ extern consvar_t cv_usemouse2;
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON)
extern consvar_t cv_mouse2opt;
#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
extern consvar_t cv_itemrespawntime;
extern consvar_t cv_itemrespawn;
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_hidetime;
@ -77,9 +69,6 @@ extern consvar_t cv_autobalance;
extern consvar_t cv_teamscramble;
extern consvar_t cv_scrambleonchange;
extern consvar_t cv_useranalog, cv_useranalog2;
extern consvar_t cv_analog, cv_analog2;
extern consvar_t cv_netstat;
#ifdef WALLSPLATS
extern consvar_t cv_splats;
@ -101,8 +90,6 @@ extern consvar_t cv_recycler;
extern consvar_t cv_itemfinder;
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_startinglives;
@ -121,17 +108,7 @@ extern consvar_t cv_maxping;
extern consvar_t cv_skipmapcheck;
extern consvar_t cv_sleep, cv_screenshot_option, cv_screenshot_folder;
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;
extern consvar_t cv_sleep;
typedef enum
{
@ -162,6 +139,8 @@ typedef enum
MAXNETXCMD
} netxcmd_t;
extern const char *netxcmdnames[MAXNETXCMD - 1];
#if defined(_MSC_VER)
#pragma pack(1)
#endif
@ -210,7 +189,6 @@ void Command_ExitGame_f(void);
void Command_Retry_f(void);
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 ObjectPlace_OnChange(void);
void ItemFinder_OnChange(void);
void D_SetPassword(const char *pw);

View File

@ -62,44 +62,49 @@
#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
{
INT32 ram;
char *filename; // name of the file or ptr of the data in ram
UINT32 size;
union {
char *filename; // Name of the file
char *ram; // Pointer to the data in RAM
} id;
UINT32 size; // Size of the file
UINT8 fileid;
INT32 node; // destination
struct filetx_s *next; // a queue
INT32 node; // Destination
struct filetx_s *next; // Next file in the list
} filetx_t;
// current transfers (one for each node)
// Current transfers (one for each node)
typedef struct filetran_s
{
filetx_t *txlist;
UINT32 position;
FILE *currentfile;
filetx_t *txlist; // Linked list of all files for the node
UINT32 position; // The current position in the file
FILE *currentfile; // The file currently being sent/received
} filetran_t;
static filetran_t transfer[MAXNETNODES];
// read time of file: stat _stmtime
// write time of file: utime
// Read time of file: stat _stmtime
// Write time of file: utime
// receiver structure
INT32 fileneedednum;
fileneeded_t fileneeded[MAX_WADFILES];
// Receiver structure
INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files
char downloaddir[256] = "DOWNLOAD";
#ifdef CLIENT_LOADINGSCREEN
// for cl loading screen
INT32 lastfilenum = 0;
INT32 lastfilenum = -1;
#endif
/** Fills a serverinfo packet with information about wad files loaded.
*
* \todo Give this function a better name since it is in global scope.
*
*/
UINT8 *PutFileNeeded(void)
{
@ -111,19 +116,19 @@ UINT8 *PutFileNeeded(void)
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))
filestatus = 0;
else
filestatus = 1; // important
filestatus = 1; // Important
// Store in the upper four bits
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))
filestatus += (0 << 4); // won't send
filestatus += (0 << 4); // Won't send
else
filestatus += (1 << 4); // will send if requested
filestatus += (1 << 4); // Will send if requested
bytesused += (nameonlylength(wadfilename) + 22);
@ -144,7 +149,12 @@ UINT8 *PutFileNeeded(void)
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)
{
INT32 i;
@ -155,14 +165,14 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
p = (UINT8 *)fileneededstr;
for (i = 0; i < fileneedednum; i++)
{
fileneeded[i].status = FS_NOTFOUND;
filestatus = READUINT8(p);
fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet
filestatus = READUINT8(p); // The first byte is the file status
fileneeded[i].important = (UINT8)(filestatus & 3);
fileneeded[i].willsend = (UINT8)(filestatus >> 4);
fileneeded[i].totalsize = READUINT32(p);
fileneeded[i].phandle = NULL;
READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
READMEM(p, fileneeded[i].md5sum, 16);
fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size
fileneeded[i].file = NULL; // The file isn't open yet
READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name
READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum
}
}
@ -171,13 +181,16 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave)
fileneedednum = 1;
fileneeded[0].status = FS_REQUESTED;
fileneeded[0].totalsize = UINT32_MAX;
fileneeded[0].phandle = NULL;
fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, tmpsave);
}
/** Checks the server to see if we CAN download all the files,
* before starting to create them and requesting.
*
* \return True if we can download all the files
*
*/
boolean CL_CheckDownloadable(void)
{
@ -239,8 +252,12 @@ boolean CL_CheckDownloadable(void)
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.
*
* \return True if the packet was successfully sent
* \note Sends a PT_REQUESTFILE packet
*
*/
boolean CL_SendRequestFile(void)
{
@ -287,7 +304,8 @@ boolean CL_SendRequestFile(void)
}
// 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];
UINT8 *p = netbuffer->u.textcmd;
@ -298,16 +316,33 @@ void Got_RequestFilePak(INT32 node)
if (id == 0xFF)
break;
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 i, j;
char wadfilename[MAX_WADPATH];
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"))
// return 1;
@ -333,7 +368,7 @@ INT32 CL_CheckFiles(void)
}
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;
continue;
}
@ -343,11 +378,11 @@ INT32 CL_CheckFiles(void)
if (i >= fileneedednum || j >= numwadfiles)
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))
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);
fileneeded[i].status = FS_OPEN;
++i;
@ -356,11 +391,15 @@ INT32 CL_CheckFiles(void)
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++)
{
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++)
{
nameonly(strcpy(wadfilename, wadfiles[j]->filename));
@ -375,6 +414,14 @@ INT32 CL_CheckFiles(void)
if (fileneeded[i].status != FS_NOTFOUND || !fileneeded[i].important)
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);
CONS_Debug(DBG_NETPLAY, "found %d\n", fileneeded[i].status);
if (fileneeded[i].status != FS_FOUND)
@ -383,7 +430,7 @@ INT32 CL_CheckFiles(void)
return ret;
}
// load it now
// Load it now
void CL_LoadServerFiles(void)
{
INT32 i;
@ -394,7 +441,7 @@ void CL_LoadServerFiles(void)
for (i = 1; i < fileneedednum; i++)
{
if (fileneeded[i].status == FS_OPEN)
continue; // already loaded
continue; // Already loaded
else if (fileneeded[i].status == FS_FOUND)
{
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));
}
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
static INT32 filetosend = 0;
// Number of files to send
// 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 *p;
filetx_t **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request
INT32 i;
char wadfilename[MAX_WADPATH];
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;
while (*q)
q = &((*q)->next);
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (p)
memset(p, 0, sizeof (filetx_t));
else
I_Error("SendFile: No more ram\n");
p->filename = (char *)malloc(MAX_WADPATH);
if (!p->filename)
I_Error("SendFile: No more ram\n");
if (!p)
I_Error("SV_SendFile: No more memory\n");
// a minimum of security, can get only file in srb2 direcory
strlcpy(p->filename, filename, MAX_WADPATH);
nameonly(p->filename);
// Initialise with zeros
memset(p, 0, sizeof (filetx_t));
// 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++)
{
strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH);
nameonly(wadfilename);
if (!stricmp(wadfilename, p->filename))
if (!stricmp(wadfilename, p->id.filename))
{
// copy filename with full path
strlcpy(p->filename, wadfiles[i]->filename, MAX_WADPATH);
// Copy file name with full path
strlcpy(p->id.filename, wadfiles[i]->filename, MAX_WADPATH);
break;
}
}
// Handle non-loaded file requests
if (!wadfiles[i])
{
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
// don't inform client (probably hacker)
// Not found
// 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));
free(p->filename);
free(p->id.filename);
free(p);
*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)
{
// too big
// don't inform client (client sucks, man)
// Too big
// Don't inform client (client sucks, man)
DEBFILE(va("Client %d request %s: file too big, not sending\n", node, filename));
free(p->filename);
free(p->id.filename);
free(p);
*q = NULL;
return;
return false; // cancel the rest of the requests
}
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->next = NULL; // end of list
filetosend++;
p->next = NULL; // End of list
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 *p;
filetx_t **q; // A pointer to the "next" field of the last file in the list
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;
while (*q)
q = &((*q)->next);
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (p)
memset(p, 0, sizeof (filetx_t));
else
I_Error("SendRam: No more ram\n");
p->ram = freemethod;
p->filename = data;
if (!p)
I_Error("SV_SendRam: No more memory\n");
// Initialise with zeros
memset(p, 0, sizeof (filetx_t));
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->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;
// Free the file request according to the freemethod parameter used with SV_SendFile/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)
fclose(transfer[node].currentfile);
free(p->filename);
free(p->id.filename);
break;
case SF_Z_RAM:
Z_Free(p->filename);
case SF_Z_RAM: // It's a memory block allocated with Z_Alloc or the likes, use Z_Free
Z_Free(p->id.ram);
break;
case SF_RAM:
free(p->filename);
case SF_NOFREERAM:
case SF_RAM: // It's a memory block allocated with malloc, use free
free(p->id.ram);
case SF_NOFREERAM: // Nothing to free
break;
}
// Remove the file request from the list
transfer[node].txlist = p->next;
transfer[node].currentfile = NULL;
free(p);
filetosend--;
// Indicate that the transmission is over
transfer[node].currentfile = NULL;
filestosend--;
}
#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;
filetx_pak *p;
size_t size;
filetx_t *f;
INT32 packetsent = PACKETPERTIC, ram, i;
INT32 packetsent, ram, i, j;
INT32 maxpacketsent;
if (!filetosend)
if (!filestosend) // No file to send
return;
if (!packetsent)
packetsent++;
// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
while (packetsent-- && filetosend != 0)
if (cv_downloadspeed.value) // New (and experimental) behavior
{
for (i = currentnode, ram = 0; ram < MAXNETNODES;
i = (i+1) % MAXNETNODES, ram++)
packetsent = cv_downloadspeed.value;
// 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)
goto found;
}
// 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:
currentnode = (i+1) % MAXNETNODES;
f = transfer[i].txlist;
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;
transfer[i].currentfile =
fopen(f->filename, "rb");
fopen(f->id.filename, "rb");
if (!transfer[i].currentfile)
I_Error("File %s does not exist",
f->filename);
f->id.filename);
fseek(transfer[i].currentfile, 0, SEEK_END);
filesize = ftell(transfer[i].currentfile);
@ -596,45 +741,47 @@ void FiletxTicker(void)
// Nobody wants to transfer a file bigger
// than 4GB!
if (filesize >= LONG_MAX)
I_Error("filesize of %s is too large", f->filename);
if (-1 == filesize)
I_Error("Error getting filesize of %s", f->filename);
I_Error("filesize of %s is too large", f->id.filename);
if (filesize == -1)
I_Error("Error getting filesize of %s", f->id.filename);
f->size = (UINT32)filesize;
fseek(transfer[i].currentfile, 0, SEEK_SET);
}
else
transfer[i].currentfile = (FILE *)1;
else // Sending RAM
transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open
transfer[i].position = 0;
}
// Build a packet containing a file fragment
p = &netbuffer->u.filetxpak;
size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE);
if (f->size-transfer[i].position < size)
size = f->size-transfer[i].position;
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)
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);
// put flag so receiver know the totalsize
// Put flag so receiver knows the total size
if (transfer[i].position + size == f->size)
p->position |= LONG(0x80000000);
p->fileid = f->fileid;
p->size = SHORT((UINT16)size);
netbuffer->packettype = PT_FILEFRAGMENT;
if (!HSendPacket(i, true, 0, FILETXHEADER + size)) // reliable SEND
{ // not sent for some odd reason, retry at next call
if (!ram)
fseek(transfer[i].currentfile,transfer[i].position,SEEK_SET);
// exit the while (can't send this one so why should i send the next?)
break;
// Send the packet
if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND
{ // Success
transfer[i].position = (UINT32)(transfer[i].position + size);
if (transfer[i].position == f->size) // Finish?
SV_EndFileSend(i);
}
else // success
{
transfer[i].position = (UINT32)(size+transfer[i].position);
if (transfer[i].position == f->size) // finish ?
EndSend(i);
else
{ // Not sent for some odd reason, retry at next call
if (!ram)
fseek(transfer[i].currentfile,transfer[i].position, SEEK_SET);
// 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)
{
INT32 filenum = netbuffer->u.filetxpak.fileid;
fileneeded_t *file = &fileneeded[filenum];
char *filename = file->filename;
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)
{
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;
}
if (fileneeded[filenum].status == FS_REQUESTED)
if (file->status == FS_REQUESTED)
{
if (fileneeded[filenum].phandle) I_Error("Got_Filetxpak: allready open file\n");
fileneeded[filenum].phandle = fopen(fileneeded[filenum].filename, "wb");
if (!fileneeded[filenum].phandle) I_Error("Can't create file %s: %s",fileneeded[filenum].filename, strerror(errno));
CONS_Printf("\r%s...\n",fileneeded[filenum].filename);
fileneeded[filenum].currentsize = 0;
fileneeded[filenum].status = FS_DOWNLOADING;
if (file->file)
I_Error("Got_Filetxpak: already open file\n");
file->file = fopen(filename, "wb");
if (!file->file)
I_Error("Can't create file %s: %s", filename, strerror(errno));
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);
UINT16 size = SHORT(netbuffer->u.filetxpak.size);
// use a special tric to know when file is finished (not allways used)
// WARNING: filepak can arrive out of order so don't stop now !
// Use a special trick to know when the file is complete (not always used)
// WARNING: file fragments can arrive out of order so don't stop yet!
if (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
fseek(fileneeded[filenum].phandle,pos,SEEK_SET);
if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].phandle)!=1)
I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].phandle)));
fileneeded[filenum].currentsize += size;
// We can receive packet in the wrong order, anyway all os support gaped file
fseek(file->file, pos, SEEK_SET);
if (fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1)
I_Error("Can't write to %s: %s\n",filename, strerror(ferror(file->file)));
file->currentsize += size;
// finished?
if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize)
// Finished?
if (file->currentsize == file->totalsize)
{
fclose(fileneeded[filenum].phandle);
fileneeded[filenum].phandle = NULL;
fileneeded[filenum].status = FS_FOUND;
fclose(file->file);
file->file = NULL;
file->status = FS_FOUND;
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
fileneeded[filenum].filename);
filename);
}
}
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)
{
Net_SendAcks(servernode);
@ -702,33 +884,50 @@ void Got_Filetxpak(void)
#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)
EndSend(node);
SV_EndFileSend(node);
}
void CloseNetFile(void)
{
INT32 i;
// is sending?
// Is sending?
for (i = 0; i < MAXNETNODES; i++)
AbortSendFiles(i);
SV_AbortSendFiles(i);
// receiving a file?
// Receiving a file?
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);
// file is not complete delete it
fclose(fileneeded[i].file);
// File is not complete delete it
remove(fileneeded[i].filename);
}
// remove FILEFRAGMENT from acknledge list
// Remove PT_FILEFRAGMENT from acknowledge list
Net_AbortPacketType(PT_FILEFRAGMENT);
}
// functions cut and pasted from doomatic :)
// Functions cut and pasted from Doomatic :)
void nameonly(char *s)
{

View File

@ -29,21 +29,21 @@ typedef enum
FS_FOUND,
FS_REQUESTED,
FS_DOWNLOADING,
FS_OPEN, // is opened and used in w_wad
FS_OPEN, // Is opened and used in w_wad
FS_MD5SUMBAD
} filestatus_t;
typedef struct
{
UINT8 important;
UINT8 willsend; // is the server willing to send it?
UINT8 willsend; // Is the server willing to send it?
char filename[MAX_WADPATH];
UINT8 md5sum[16];
// used only for download
FILE *phandle;
// Used only for download
FILE *file;
UINT32 currentsize;
UINT32 totalsize;
filestatus_t status; // the value returned by recsearch
filestatus_t status; // The value returned by recsearch
} fileneeded_t;
extern INT32 fileneedednum;
@ -58,28 +58,25 @@ UINT8 *PutFileNeeded(void);
void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr);
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);
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);
void FiletxTicker(void);
void SV_FileSendTicker(void);
void Got_Filetxpak(void);
boolean SV_SendingFile(INT32 node);
boolean CL_CheckDownloadable(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);
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,
boolean completepath);
filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum);

View File

@ -32,18 +32,21 @@
// Extra abilities/settings for skins (combinable stuff)
typedef enum
{
SF_SUPER = 1, // Can turn super in singleplayer/co-op mode.
SF_SUPERANIMS = 1<<1, // If super, use the super sonic animations
SF_SUPERSPIN = 1<<2, // Should spin frames be played while super?
SF_HIRES = 1<<3, // Draw the sprite 2x as small?
SF_SUPER = 1, // Can turn super in singleplayer/co-op mode?
SF_NOSUPERSPIN = 1<<1, // 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 at different size?
SF_NOSKID = 1<<4, // No skid particles etc
SF_NOSPEEDADJUST = 1<<5, // Skin-specific version of disablespeedadjust
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_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_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
} skinflags_t;
@ -64,7 +67,7 @@ typedef enum
CA_JUMPBOOST,
CA_AIRDRILL,
CA_JUMPTHOK,
CA_DASHMODE,
CA_BOUNCE,
CA_TWINSPIN
} charability_t;
@ -73,7 +76,7 @@ typedef enum
{
CA2_NONE=0,
CA2_SPINDASH,
CA2_MULTIABILITY,
CA2_GUNSLINGER,
CA2_MELEE
} charability2_t;
@ -117,10 +120,8 @@ typedef enum
// Did you get a time-over?
PF_TIMEOVER = 1<<10,
// Ready for Super?
PF_SUPERREADY = 1<<11,
// Character action status
PF_STARTJUMP = 1<<11,
PF_JUMPED = 1<<12,
PF_SPINNING = 1<<13,
PF_STARTDASH = 1<<14,
@ -132,12 +133,11 @@ typedef enum
// Sliding (usually in water) like Labyrinth/Oil Ocean
PF_SLIDING = 1<<17,
/*** NIGHTS STUFF ***/
// Is the player in NiGHTS mode?
PF_NIGHTSMODE = 1<<18,
PF_TRANSFERTOCLOSEST = 1<<19,
// Bouncing
PF_BOUNCING = 1<<18,
// Spill rings after falling
/*** NIGHTS STUFF ***/
PF_TRANSFERTOCLOSEST = 1<<19,
PF_NIGHTSFALL = 1<<20,
PF_DRILLING = 1<<21,
PF_SKIDDOWN = 1<<22,
@ -151,9 +151,15 @@ typedef enum
PF_ANALOGMODE = 1<<26, // Analog mode?
// 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;
typedef enum
@ -164,7 +170,7 @@ typedef enum
PA_EDGE,
PA_WALK,
PA_RUN,
PA_PEEL,
PA_DASH,
PA_PAIN,
PA_ROLL,
PA_JUMP,
@ -178,23 +184,34 @@ typedef enum
typedef enum
{
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
} shieldtype_t; // pw_shield
@ -205,6 +222,10 @@ typedef enum
CR_GENERIC,
// Tails carry.
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.
CR_ZOOMTUBE,
CR_ROPEHANG,
@ -244,9 +265,7 @@ typedef enum
pw_nights_helper,
pw_nights_linkfreeze,
//for linedef exec 427
pw_nocontrol,
pw_ingoop, // In goop
pw_nocontrol, //for linedef exec 427
NUMPOWERS
} powertype_t;
@ -297,10 +316,8 @@ typedef struct player_s
// It is updated with cmd->aiming.
angle_t aiming;
// This is only used between levels,
// mo->health is used during levels.
/// \todo Remove this. We don't need a second health definition for players.
INT32 health;
// player's ring count
INT32 rings;
SINT8 pity; // i pity the fool.
INT32 currentweapon; // current weapon selected.
@ -324,6 +341,7 @@ typedef struct player_s
UINT8 skincolor;
INT32 skin;
UINT32 availabilities;
UINT32 score; // player score
fixed_t dashspeed; // dashing speed
@ -361,8 +379,7 @@ typedef struct player_s
UINT8 gotcontinue; // Got continue from this stage?
fixed_t speed; // Player's speed (distance formula of MOMX and MOMY values)
UINT8 jumping; // Jump counter
UINT8 secondjump;
UINT8 secondjump; // Jump counter
UINT8 fly1; // Tails flying
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);
}
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)
{
static CPUInfoFlags DOS_CPUInfo;

View File

@ -90,6 +90,10 @@ static unsigned long nombre = NEWTICRATE*10;
static void I_BlitScreenVesa1(void); //see later
void I_FinishUpdate (void)
{
// draw captions if enabled
if (cv_closedcaptioning.value)
SCR_ClosedCaptions();
// draw FPS if enabled
if (cv_ticrate.value)
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.
// 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".
#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...
#define MAXPLAYERS 32
#define MAXSKINS MAXPLAYERS
#define MAXSKINS 32
#define PLAYERSMASK (MAXPLAYERS-1)
#define MAXPLAYERNAME 21
@ -407,6 +407,7 @@ void M_StartupLocale(void);
extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL;
char *va(const char *format, ...) FUNCPRINTF;
char *M_GetToken(const char *inputString);
void M_UnGetToken(void);
char *sizeu1(size_t num);
char *sizeu2(size_t num);
char *sizeu3(size_t num);
@ -435,6 +436,9 @@ extern INT32 cv_debug;
// Misc stuff for later...
// =======================
// Modifier key variables, accessible anywhere
extern UINT8 shiftdown, ctrldown, altdown;
// if we ever make our alloc stuff...
#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.
#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__

View File

@ -241,6 +241,12 @@ typedef struct
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
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.
UINT8 numGradedMares; ///< Internal. For grade support.
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_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_WIDEICON 32 ///< If you're in a circumstance where it fits, use a wide map icon
extern mapheader_t* mapheaderinfo[NUMMAPS];
@ -307,7 +314,7 @@ enum GameType
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;
@ -372,6 +379,7 @@ nightsdata_t ntemprecords;
extern UINT32 token; ///< Number of tokens collected in a level
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 sstimer; ///< Time allotted in the special stage
extern UINT32 bluescore; ///< Blue Team Scores
@ -445,19 +453,17 @@ extern mapthing_t *redctfstarts[MAXPLAYERS]; // CTF
#if defined (macintosh)
#define DEBFILE(msg) I_OutputMsg(msg)
extern FILE *debugfile;
#else
#define DEBUGFILE
#ifdef DEBUGFILE
#define DEBFILE(msg) { if (debugfile) { fputs(msg, debugfile); fflush(debugfile); } }
extern FILE *debugfile;
#else
#define DEBFILE(msg) {}
extern FILE *debugfile;
#endif
#endif
#ifdef DEBUGFILE
extern FILE *debugfile;
extern INT32 debugload;
#endif

View File

@ -162,6 +162,18 @@ INT32 I_PutEnv(char *variable)
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) {}
#include "../sdl/dosstr.c"

View File

@ -434,7 +434,6 @@ void F_StartIntro(void)
G_SetGamestate(GS_INTRO);
gameaction = ga_nothing;
playerdeadview = false;
paused = false;
CON_ToggleOff();
CON_ClearHUD();
@ -559,7 +558,7 @@ static void F_IntroDrawScene(void)
if (finalecount < 4)
S_StopMusic();
if (finalecount == 4)
S_ChangeMusicInternal("stjr", false);
S_ChangeMusicInternal("_stjr", false);
x = (BASEVIDWIDTH<<FRACBITS)/2 - FixedMul(334<<FRACBITS, aspect)/2;
y = (BASEVIDHEIGHT<<FRACBITS)/2 - FixedMul(358<<FRACBITS, aspect)/2;
V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH1", PU_CACHE)), aspect);
@ -771,7 +770,7 @@ void F_IntroDrawer(void)
F_RunWipe(99,true);
}
S_ChangeMusicInternal("read_m", false);
S_ChangeMusicInternal("_intro", false);
}
else if (intro_scenenum == 3)
roidtics = BASEVIDWIDTH - 64;
@ -974,7 +973,7 @@ static const char *credits[] = {
"Scott \"Graue\" Feeney",
"Nathan \"Jazz\" Giroux",
"Thomas \"Shadow Hog\" Igoe",
"\"Monster\" Iestyn Jealous",
"Iestyn \"Monster Iestyn\" Jealous",
"Ronald \"Furyhunter\" Kinard", // The SDL2 port
"John \"JTE\" Muniz",
"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)
"Andrew \"orospakr\" Clunis",
"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",
"Julio \"Chaos Zero 64\" Guir",
"\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog
@ -1021,7 +1021,7 @@ static const char *credits[] = {
"Paul \"Boinciel\" Clempson",
"Cyan Helkaraxe",
"Kepa \"Nev3r\" Iceta",
"\"Monster\" Iestyn Jealous",
"Iestyn \"Monster Iestyn\" Jealous",
"Jarel \"Arrow\" Jones",
"Stefan \"Stuf\" Rimalia",
"Shane Mychal Sexton",
@ -1124,13 +1124,12 @@ void F_StartCredits(void)
}
gameaction = ga_nothing;
playerdeadview = false;
paused = false;
CON_ToggleOff();
CON_ClearHUD();
S_StopMusic();
S_ChangeMusicInternal("credit", false);
S_ChangeMusicInternal("_creds", false);
finalecount = 0;
animtimer = 0;
@ -1271,7 +1270,6 @@ void F_StartGameEvaluation(void)
G_SaveGame((UINT32)cursaveslot);
gameaction = ga_nothing;
playerdeadview = false;
paused = false;
CON_ToggleOff();
CON_ClearHUD();
@ -1382,7 +1380,6 @@ void F_StartGameEnd(void)
G_SetGamestate(GS_GAMEEND);
gameaction = ga_nothing;
playerdeadview = false;
paused = false;
CON_ToggleOff();
CON_ClearHUD();
@ -1427,7 +1424,7 @@ void F_StartTitleScreen(void)
// IWAD dependent stuff.
S_ChangeMusicInternal("titles", looptitle);
S_ChangeMusicInternal("_title", looptitle);
animtimer = 0;
@ -1585,7 +1582,6 @@ void F_StartContinue(void)
gameaction = ga_nothing;
keypressed = false;
playerdeadview = false;
paused = false;
CON_ToggleOff();
CON_ClearHUD();
@ -1593,7 +1589,7 @@ void F_StartContinue(void)
// In case menus are still up?!!
M_ClearMenus(true);
S_ChangeMusicInternal("contsc", false);
S_ChangeMusicInternal("_conti", false);
S_StopSounds();
timetonext = TICRATE*11;
@ -1727,7 +1723,7 @@ static void F_AdvanceToNextScene(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 (server)
@ -1742,7 +1738,7 @@ void F_EndCutScene(void)
else if (nextmap < 1100-1)
G_NextLevel();
else
Y_EndGame();
G_EndGame();
}
}
@ -1754,7 +1750,6 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
G_SetGamestate(GS_CUTSCENE);
gameaction = ga_nothing;
playerdeadview = false;
paused = false;
CON_ToggleOff();

View File

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

View File

@ -88,6 +88,7 @@ UINT8 modeattacking = ATTACKING_NONE;
boolean disableSpeedAdjust = false;
boolean imcontinuing = false;
boolean runemeraldmanager = false;
UINT16 emeraldspawndelay = 60*TICRATE;
// menu demo things
UINT8 numDemos = 3;
@ -155,6 +156,7 @@ UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage.
UINT16 emeralds;
UINT32 token; // Number of tokens collected in a level
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
// Old Special Stage
@ -696,8 +698,7 @@ void G_SetNightsRecords(void)
free(gpath);
// If the mare count changed, this will update the score display
CV_AddValue(&cv_nextmap, 1);
CV_AddValue(&cv_nextmap, -1);
Nextmap_OnChange();
}
// 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)
cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]);
}
if (cv_analog.value || twodlevel
if (twodlevel
|| (player->mo && (player->mo->flags2 & MF2_TWOD))
|| (!demoplayback && (player->climbing
|| (player->pflags & PF_NIGHTSMODE)
|| (player->pflags & PF_SLIDING)
|| (player->pflags & PF_FORCESTRAFE)))) // Analog
|| (player->powers[pw_carry] == CR_NIGHTSMODE)
|| (player->pflags & (PF_SLIDING|PF_FORCESTRAFE))))) // Analog
forcestrafe = true;
if (forcestrafe) // Analog
if (forcestrafe)
{
if (turnright)
side += sidemove[speed];
@ -1031,6 +1031,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
side += ((axis * sidemove[1]) >> 10);
}
}
else if (cv_analog.value) // Analog
{
if (turnright)
cmd->buttons |= BT_CAMRIGHT;
if (turnleft)
cmd->buttons |= BT_CAMLEFT;
}
else
{
if (turnright)
@ -1118,15 +1125,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
if (PLAYER1INPUTDOWN(gc_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 (camera.chase && !resetdown)
@ -1188,10 +1186,19 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
if (!mouseaiming && cv_mousemove.value)
forward += mousey;
if (cv_analog.value ||
(!demoplayback && (player->climbing
if ((!demoplayback && (player->climbing
|| (player->pflags & PF_SLIDING)))) // Analog for mouse
side += mousex*2;
else if (cv_analog.value)
{
if (mousex)
{
if (mousex > 0)
cmd->buttons |= BT_CAMRIGHT;
else
cmd->buttons |= BT_CAMLEFT;
}
}
else
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);
if (cv_analog.value) {
cmd->angleturn = (INT16)(thiscam->angle >> 16);
if (player->awayviewtics)
cmd->angleturn = (INT16)(player->awayviewmobj->angle >> 16);
else
cmd->angleturn = (INT16)(thiscam->angle >> 16);
}
else
{
@ -1302,12 +1310,11 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
if (turnleft)
cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]);
}
if (cv_analog2.value || twodlevel
if (twodlevel
|| (player->mo && (player->mo->flags2 & MF2_TWOD))
|| player->climbing
|| (player->pflags & PF_NIGHTSMODE)
|| (player->pflags & PF_SLIDING)
|| (player->pflags & PF_FORCESTRAFE)) // Analog
|| (player->powers[pw_carry] == CR_NIGHTSMODE)
|| (player->pflags & (PF_SLIDING|PF_FORCESTRAFE))) // Analog
forcestrafe = true;
if (forcestrafe) // Analog
{
@ -1322,6 +1329,13 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
side += ((axis * sidemove[1]) >> 10);
}
}
else if (cv_analog2.value) // Analog
{
if (turnright)
cmd->buttons |= BT_CAMRIGHT;
if (turnleft)
cmd->buttons |= BT_CAMLEFT;
}
else
{
if (turnright)
@ -1406,15 +1420,6 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
if (PLAYER2INPUTDOWN(gc_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 (camera2.chase && !resetdown)
@ -1476,9 +1481,19 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
if (!mouseaiming && cv_mousemove2.value)
forward += mouse2y;
if (cv_analog2.value || player->climbing
if (player->climbing
|| (player->pflags & PF_SLIDING)) // Analog for mouse
side += mouse2x*2;
else if (cv_analog2.value)
{
if (mouse2x)
{
if (mouse2x > 0)
cmd->buttons |= BT_CAMRIGHT;
else
cmd->buttons |= BT_CAMLEFT;
}
}
else
cmd->angleturn = (INT16)(cmd->angleturn - (mouse2x*8));
@ -1526,9 +1541,10 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
}
if (cv_analog2.value) {
cmd->angleturn = (INT16)(thiscam->angle >> 16);
if (player->awayviewtics)
cmd->angleturn = (INT16)(player->awayviewmobj->angle >> 16);
else
cmd->angleturn = (INT16)(thiscam->angle >> 16);
}
else
{
@ -1565,11 +1581,6 @@ static void Analog_OnChange(void)
// 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) {
CV_SetValue(&cv_analog, 0);
return;
@ -1590,11 +1601,6 @@ static void Analog2_OnChange(void)
// 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) {
CV_SetValue(&cv_analog2, 0);
return;
@ -2081,6 +2087,7 @@ void G_PlayerReborn(INT32 player)
UINT8 mare;
UINT8 skincolor;
INT32 skin;
UINT32 availabilities;
tic_t jointime;
boolean spectator;
INT16 bot;
@ -2105,6 +2112,7 @@ void G_PlayerReborn(INT32 player)
skincolor = players[player].skincolor;
skin = players[player].skin;
availabilities = players[player].availabilities;
camerascale = players[player].camerascale;
shieldscale = players[player].shieldscale;
charability = players[player].charability;
@ -2150,6 +2158,7 @@ void G_PlayerReborn(INT32 player)
// save player config truth reborn
p->skincolor = skincolor;
p->skin = skin;
p->availabilities = availabilities;
p->camerascale = camerascale;
p->shieldscale = shieldscale;
p->charability = charability;
@ -2193,7 +2202,7 @@ void G_PlayerReborn(INT32 player)
p->pflags |= PF_JUMPDOWN;
p->playerstate = PST_LIVE;
p->health = 1; // 0 rings
p->rings = 0; // 0 rings
p->panim = PA_IDLE; // standing animation
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.
{
P_MovePlayerToStarpost(playernum);
#ifdef HAVE_BLUA
LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :)
#endif
return;
}
@ -2624,7 +2636,7 @@ void G_ExitLevel(void)
CONS_Printf(M_GetText("The round has ended.\n"));
// 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)
{
INT32 i;
boolean gottoken = false;
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))
nextmap = (INT16)(spstage_start-1);
if (gametype == GT_COOP && token)
if ((gottoken = (gametype == GT_COOP && token)))
{
token--;
gottoken = true;
if (!(emeralds & EMERALD1))
nextmap = (INT16)(sstage_start - 1); // Special Stage 1
@ -2900,7 +2910,7 @@ static void G_DoCompleted(void)
if (nextmap < NUMMAPS && !mapheaderinfo[nextmap])
P_AllocMapHeader(nextmap);
if (skipstats)
if (skipstats && !modeattacking) // Don't skip stats if we're in record attack
G_AfterIntermission();
else
{
@ -2920,7 +2930,7 @@ void G_AfterIntermission(void)
if (nextmap < 1100-1)
G_NextLevel();
else
Y_EndGame();
G_EndGame();
}
}
@ -3006,6 +3016,38 @@ static void G_DoContinued(void)
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
//
@ -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
//
// 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;
@ -3570,7 +3612,8 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
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;
}
else if (pultmode)
@ -3584,13 +3627,16 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
players[i].continues = 1;
}
if (!((netgame || multiplayer) && (FLS)))
players[i].score = 0;
// The latter two should clear by themselves, but just in case
players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS);
// Clear cheatcodes too, just in case.
players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS);
players[i].score = players[i].xtralife = 0;
players[i].xtralife = 0;
}
// Reset unlockable triggers
@ -3624,7 +3670,6 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
mapmusflags |= MUSIC_RELOADRESET;
ultimatemode = pultmode;
playerdeadview = false;
automapactive = false;
imcontinuing = false;
@ -3652,6 +3697,9 @@ char *G_BuildMapTitle(INT32 mapnum)
{
char *title = NULL;
if (!mapheaderinfo[mapnum-1])
P_AllocMapHeader(mapnum-1);
if (strcmp(mapheaderinfo[mapnum-1]->lvlttl, ""))
{
size_t len = 1;
@ -3885,7 +3933,7 @@ void G_GhostAddColor(ghostcolor_t color)
ghostext.color = (UINT8)color;
}
void G_GhostAddScale(UINT16 scale)
void G_GhostAddScale(fixed_t scale)
{
if (!demorecording || !(demoflags & DF_GHOST))
return;
@ -3919,12 +3967,8 @@ void G_WriteGhostTic(mobj_t *ghost)
if (!(demoflags & DF_GHOST))
return; // No ghost data to write.
if (ghost->player && ghost->player->pflags & PF_NIGHTSMODE && ghost->tracer)
{
// We're talking about the NiGHTS thing, not the normal platforming thing!
if (ghost->player && ghost->player->powers[pw_carry] == CR_NIGHTSMODE) // We're talking about the NiGHTS thing, not the normal platforming thing!
ziptic |= GZT_NIGHTS;
ghost = ghost->tracer;
}
ziptic_p = demo_p++; // the ziptic, written at the end of this function
@ -4106,11 +4150,9 @@ void G_ConsGhostTic(void)
demo_p++;
if (ziptic & GZT_SPR2)
demo_p++;
if(ziptic & GZT_NIGHTS) {
if (!testmo->player || !(testmo->player->pflags & PF_NIGHTSMODE) || !testmo->tracer)
if (ziptic & GZT_NIGHTS) {
if (!testmo->player || !(testmo->player->powers[pw_carry] == CR_NIGHTSMODE))
nightsfail = true;
else
testmo = testmo->tracer;
}
if (ziptic & GZT_EXTRA)
@ -4727,7 +4769,7 @@ void G_BeginRecording(void)
// Don't do it.
WRITEFIXED(demo_p, player->jumpfactor);
// Save netvar data (SONICCD, etc)
// Save netvar data
CV_SaveNetVars(&demo_p);
memset(&oldcmd,0,sizeof(oldcmd));
@ -5141,7 +5183,7 @@ void G_DoPlayDemo(char *defdemoname)
memset(playeringame,0,sizeof(playeringame));
playeringame[0] = true;
P_SetRandSeed(randseed);
G_InitNew(false, G_BuildMapName(gamemap), true, true);
G_InitNew(false, G_BuildMapName(gamemap), true, true, false);
// Set skin
SetPlayerSkin(0, skin);
@ -5621,7 +5663,7 @@ boolean G_CheckDemoStatus(void)
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.
#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);
demorecording = false;

View File

@ -56,6 +56,9 @@ extern INT16 rw_maximums[NUM_WEAPONS];
// used in game menu
extern consvar_t cv_crosshair, cv_crosshair2;
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_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;
@ -89,7 +92,7 @@ void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo);
void G_DoReborn(INT32 playernum);
void G_PlayerReborn(INT32 player);
void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer,
boolean skipprecutscene);
boolean skipprecutscene, boolean FLS);
char *G_BuildMapTitle(INT32 mapnum);
// XMOD spawning
@ -139,7 +142,7 @@ void G_GhostAddSpin(void);
void G_GhostAddRev(void);
void G_GhostAddColor(ghostcolor_t color);
void G_GhostAddFlip(void);
void G_GhostAddScale(UINT16 scale);
void G_GhostAddScale(fixed_t scale);
void G_GhostAddHit(mobj_t *victim);
void G_WriteGhostTic(mobj_t *ghost);
void G_ConsGhostTic(void);
@ -171,6 +174,7 @@ void G_NextLevel(void);
void G_Continue(void);
void G_UseContinue(void);
void G_AfterIntermission(void);
void G_EndGame(void); // moved from y_inter.c/h and renamed
void G_Ticker(boolean run);
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}};
// 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_mousesens2 = {"mousesens2", "35", 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_mouseysens2 = {"mouseysens2", "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", "12", 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", "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};
INT32 mousex, mousey;
@ -977,8 +977,6 @@ static const char *gamecontrolname[num_gamecontrols] =
"tossflag",
"use",
"camtoggle",
"camleft",
"camright",
"camreset",
"lookup",
"lookdown",
@ -1074,8 +1072,6 @@ void G_Controldefault(void)
gamecontrol[gc_use ][0] = KEY_JOY1+1; //B
gamecontrol[gc_use ][1] = '.';
gamecontrol[gc_camtoggle ][1] = ',';
gamecontrol[gc_camleft ][0] = 'o';
gamecontrol[gc_camright ][0] = 'p';
gamecontrol[gc_camreset ][0] = 'c';
gamecontrol[gc_lookup ][0] = KEY_PGUP;
gamecontrol[gc_lookdown ][0] = KEY_PGDN;
@ -1154,10 +1150,8 @@ void G_Controldefault(void)
#else
void G_Controldefault(void)
{
gamecontrol[gc_forward ][0] = KEY_UPARROW;
gamecontrol[gc_forward ][1] = 'w';
gamecontrol[gc_backward ][0] = KEY_DOWNARROW;
gamecontrol[gc_backward ][1] = 's';
gamecontrol[gc_forward ][0] = 'w';
gamecontrol[gc_backward ][0] = 's';
gamecontrol[gc_strafeleft ][0] = 'a';
gamecontrol[gc_straferight][0] = 'd';
gamecontrol[gc_turnleft ][0] = KEY_LEFTARROW;
@ -1178,19 +1172,16 @@ void G_Controldefault(void)
gamecontrol[gc_fire ][1] = KEY_MOUSE1+0;
gamecontrol[gc_firenormal ][0] = 'c';
gamecontrol[gc_tossflag ][0] = '\'';
gamecontrol[gc_use ][0] = 'x';
gamecontrol[gc_use ][0] = KEY_LSHIFT;
gamecontrol[gc_camtoggle ][0] = 'v';
gamecontrol[gc_camleft ][0] = '[';
gamecontrol[gc_camright ][0] = ']';
gamecontrol[gc_camreset ][0] = 'r';
gamecontrol[gc_lookup ][0] = KEY_PGUP;
gamecontrol[gc_lookdown ][0] = KEY_PGDN;
gamecontrol[gc_lookup ][0] = KEY_UPARROW;
gamecontrol[gc_lookdown ][0] = KEY_DOWNARROW;
gamecontrol[gc_centerview ][0] = KEY_END;
gamecontrol[gc_talkkey ][0] = 't';
gamecontrol[gc_teamkey ][0] = 'y';
gamecontrol[gc_scores ][0] = KEY_TAB;
gamecontrol[gc_jump ][0] = 'z';
gamecontrol[gc_jump ][1] = KEY_MOUSE1+1;
gamecontrol[gc_jump ][0] = KEY_SPACE;
gamecontrol[gc_console ][0] = KEY_CONSOLE;
gamecontrol[gc_pause ][0] = KEY_PAUSE;
#ifdef WMINPUT

View File

@ -105,8 +105,6 @@ typedef enum
gc_tossflag,
gc_use,
gc_camtoggle,
gc_camleft,
gc_camright,
gc_camreset,
gc_lookup,
gc_lookdown,
@ -126,6 +124,8 @@ typedef enum
// mouse values are used once
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 mlooky; //mousey with mlookSensitivity

View File

@ -564,8 +564,6 @@ static inline void HWR_SubsecPoly(INT32 num, poly_t *poly)
subsector_t *sub;
seg_t *lseg;
sscount++;
sub = &subsectors[num];
count = sub->numlines;
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 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;
// 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 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;
// 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];
FSurfaceInfo Surf;
float sdupx, sdupy;
if (w < 0 || h < 0)
return; // consistency w/ software
@ -664,10 +669,16 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
// | /|
// |/ |
// 0--1
v[0].x = v[3].x = (x - 160.0f)/160.0f;
v[2].x = v[1].x = ((x+w) - 160.0f)/160.0f;
v[0].y = v[1].y = -(y - 100.0f)/100.0f;
v[2].y = v[3].y = -((y+h) - 100.0f)/100.0f;
sdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f;
sdupy = FIXED_TO_FLOAT(vid.fdupy)*2.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
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);
#ifdef USE_PNG
ret = M_SavePNG(lbmname, buf, vid.width, vid.height, NULL);
ret = M_SavePNG(lbmname, buf, vid.width, vid.height, false);
#else
ret = saveTGA(lbmname, buf, vid.width, vid.height);
#endif

View File

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

View File

@ -247,27 +247,33 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_BMNE
// Monitor Boxes
&lspr[NOLIGHT], // SPR_SRBX
&lspr[NOLIGHT], // SPR_RRBX
&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
&lspr[NOLIGHT], // SPR_MSTV
&lspr[NOLIGHT], // SPR_XLTV
// Monitor Miscellany
&lspr[NOLIGHT], // SPR_MTEX
&lspr[NOLIGHT], // SPR_TRRI
&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
&lspr[NOLIGHT], // SPR_MISL
@ -290,6 +296,7 @@ light_t *t_lspr[NUMSPRITES] =
// Techno Hill Scenery
&lspr[NOLIGHT], // SPR_THZP
&lspr[NOLIGHT], // SPR_FWR5
&lspr[REDBALL_L], // SPR_ALRM
// Deep Sea Scenery
@ -356,18 +363,32 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_ELEM
&lspr[NOLIGHT], // SPR_FORC
&lspr[NOLIGHT], // SPR_PITY
&lspr[NOLIGHT], // SPR_FIRS
&lspr[NOLIGHT], // SPR_BUBS
&lspr[NOLIGHT], // SPR_ZAPS
&lspr[INVINCIBLE_L], // SPR_IVSP
&lspr[SUPERSPARK_L], // SPR_SSPK
&lspr[NOLIGHT], // SPR_GOAL
// Freed Animals
&lspr[NOLIGHT], // SPR_BIRD
&lspr[NOLIGHT], // SPR_BUNY
&lspr[NOLIGHT], // SPR_MOUS
&lspr[NOLIGHT], // SPR_CHIC
&lspr[NOLIGHT], // SPR_COWZ
&lspr[NOLIGHT], // SPR_RBRD
// Flickies
&lspr[NOLIGHT], // SPR_FBUB
&lspr[NOLIGHT], // SPR_FL01
&lspr[NOLIGHT], // SPR_FL02
&lspr[NOLIGHT], // SPR_FL03
&lspr[NOLIGHT], // SPR_FL04
&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
&lspr[NOLIGHT], // SPR_SPRY
@ -383,7 +404,7 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_SPLA
&lspr[NOLIGHT], // SPR_SMOK
&lspr[NOLIGHT], // SPR_BUBL
&lspr[SUPERSPARK_L], // SPR_WZAP
&lspr[RINGLIGHT_L], // SPR_WZAP
&lspr[SUPERSPARK_L], // SPR_TFOG
&lspr[NIGHTSLIGHT_L], // SPR_SEED // Sonic CD flower seed
&lspr[NOLIGHT], // SPR_PRTL
@ -391,9 +412,12 @@ light_t *t_lspr[NUMSPRITES] =
// Game Indicators
&lspr[NOLIGHT], // SPR_SCOR
&lspr[NOLIGHT], // SPR_DRWN
&lspr[NOLIGHT], // SPR_LCKN
&lspr[NOLIGHT], // SPR_TTAG
&lspr[NOLIGHT], // SPR_GFLG
&lspr[NOLIGHT], // SPR_CORK
// Ring Weapons
&lspr[RINGLIGHT_L], // SPR_RRNG
&lspr[RINGLIGHT_L], // SPR_RNGB
@ -473,23 +497,6 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_GWLG
&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
&lspr[NOLIGHT],
&lspr[NOLIGHT],

View File

@ -45,7 +45,7 @@
#include "hw_md2.h"
#define R_FAKEFLOORS
//#define HWPRECIP
#define HWPRECIP
#define SORTING
//#define POLYSKY
@ -1084,9 +1084,9 @@ static void HWR_SplitWall(sector_t *sector, wallVert3D *wallVerts, INT32 texnum,
float endheight = 0.0f, endbheight = 0.0f;
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 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
// use this as a temp var to store P_GetZAt's return value each time
fixed_t temp;
@ -1558,6 +1558,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (gr_backsector)
{
INT32 gr_toptexture, gr_bottomtexture;
// two sided line
if (gr_backsector->heightsec != -1)
{
@ -1608,19 +1609,22 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif
}
gr_toptexture = R_GetTextureNum(gr_sidedef->toptexture);
gr_bottomtexture = R_GetTextureNum(gr_sidedef->bottomtexture);
// check TOP TEXTURE
if ((
#ifdef ESLOPE
worldhighslope < worldtopslope ||
#endif
worldhigh < worldtop
) && texturetranslation[gr_sidedef->toptexture])
) && gr_toptexture)
{
if (drawtextured)
{
fixed_t texturevpegtop; // top
grTex = HWR_GetTexture(texturetranslation[gr_sidedef->toptexture]);
grTex = HWR_GetTexture(gr_toptexture);
// PEGGING
if (gr_linedef->flags & ML_DONTPEGTOP)
@ -1638,7 +1642,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
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
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[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
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)
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
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
}
@ -1695,13 +1699,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#ifdef ESLOPE
worldlowslope > worldbottomslope ||
#endif
worldlow > worldbottom) && texturetranslation[gr_sidedef->bottomtexture]) //only if VISIBLE!!!
worldlow > worldbottom) && gr_bottomtexture) //only if VISIBLE!!!
{
if (drawtextured)
{
fixed_t texturevpegbottom = 0; // bottom
grTex = HWR_GetTexture(texturetranslation[gr_sidedef->bottomtexture]);
grTex = HWR_GetTexture(gr_bottomtexture);
// PEGGING
#ifdef ESLOPE
@ -1721,7 +1725,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
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
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[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
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)
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
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
}
gr_midtexture = texturetranslation[gr_sidedef->midtexture];
gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture);
if (gr_midtexture)
{
FBITFIELD blendmode;
@ -2134,7 +2138,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
else
{
// 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 (drawtextured)
@ -2232,13 +2236,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (*rover->topheight < lowcut || *rover->bottomheight > highcut)
continue;
texnum = texturetranslation[sides[rover->master->sidenum[0]].midtexture];
texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = gr_curline->linedef-gr_backsector->lines[0];
newline = rover->master->frontsector->lines[0] + linenum;
texnum = texturetranslation[sides[newline->sidenum[0]].midtexture];
texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
}
#ifdef ESLOPE
@ -2366,13 +2370,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (*rover->topheight < lowcut || *rover->bottomheight > highcut)
continue;
texnum = texturetranslation[sides[rover->master->sidenum[0]].midtexture];
texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = gr_curline->linedef-gr_backsector->lines[0];
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
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)
{
sscount++;
// subsector
sub = &subsectors[num];
// sector
@ -3718,6 +3721,9 @@ static void HWR_Subsector(size_t num)
while (count--)
{
#ifdef POLYOBJECTS
if (!line->polyseg) // ignore segs that belong to polyobjects
#endif
HWR_AddLine(line);
line++;
}
@ -4223,6 +4229,7 @@ static void HWR_DrawSprite(gr_vissprite_t *spr)
GLPatch_t *gpatch; // sprite patch converted to hardware
FSurfaceInfo Surf;
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)
this_scale = FIXED_TO_FLOAT(spr->mobj->scale);
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,
// 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
wv = wallVerts;
@ -4401,7 +4409,6 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr)
FOutVector *wv;
GLPatch_t *gpatch; // sprite patch converted to hardware
FSurfaceInfo Surf;
sector_t *sector;
if (!spr->mobj)
return;
@ -4455,19 +4462,38 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr)
//Hurdler: 25/04/2000: now support colormap in hardware mode
HWR_GetMappedPatch(gpatch, spr->colormap);
sector = spr->mobj->subsector->sector;
if (sector->ffloors)
// colormap test
{
ffloor_t *caster = sector->lightlist[R_GetPlaneLight(sector, spr->mobj->z, false)].caster;
sector = caster ? &sectors[caster->secnum] : sector;
}
sector_t *sector = spr->mobj->subsector->sector;
UINT8 lightlevel = 255;
extracolormap_t *colormap = sector->extra_colormap;
// sprite lighting by modulating the RGB components
if (sector->extra_colormap)
Surf.FlatColor.rgba = HWR_Lighting(spr->sectorlight,sector->extra_colormap->rgba,sector->extra_colormap->fadergba, false, false);
if (sector->numlights)
{
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
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)
{
@ -4501,8 +4527,8 @@ static void HWR_SortVisSprites(void)
gr_vissprite_t *ds, *dsprev, *dsnext, *dsfirst;
gr_vissprite_t *best = NULL;
gr_vissprite_t unsorted;
float bestdist;
INT32 bestdispoffset;
float bestdist = 0.0f;
INT32 bestdispoffset = 0;
if (!gr_visspritecount)
return;
@ -5040,8 +5066,14 @@ static void HWR_ProjectSprite(mobj_t *thing)
size_t lumpoff;
unsigned rot;
UINT8 flip;
boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
angle_t ang;
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)
return;
@ -5056,7 +5088,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
tz = (tr_x * gr_viewcos) + (tr_y * gr_viewsin);
// 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;
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);
#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)
{
// use single rotation for all views
@ -5104,8 +5157,6 @@ static void HWR_ProjectSprite(mobj_t *thing)
else
{
// 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
rot = 6; // F7 slot
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
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
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
x1 = gr_windowcenterx + (tx * gr_centerx / tz);
@ -5136,10 +5190,17 @@ static void HWR_ProjectSprite(mobj_t *thing)
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);
if (thing->eflags & MFE_VERTICALFLIP)
if (vflip)
{
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;
@ -5185,6 +5246,8 @@ static void HWR_ProjectSprite(mobj_t *thing)
vis->patchlumpnum = sprframe->lumppat[rot];
vis->flip = flip;
vis->mobj = thing;
vis->z1 = z1;
vis->z2 = z2;
//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"
@ -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",
// thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]);
if (thing->eflags & MFE_VERTICALFLIP)
vis->vflip = true;
else
vis->vflip = false;
vis->vflip = vflip;
vis->precip = false;
}
@ -5296,6 +5356,11 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
//
vis = HWR_NewVisSprite();
vis->x1 = x1;
#if 0
vis->x2 = x2;
#else
(void)x2;
#endif
vis->x2 = tx;
vis->tz = tz;
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
vis->ty = FIXED_TO_FLOAT(thing->z + spritecachedinfo[lumpoff].topoffset) - gr_viewz;
vis->sectorlight = 0xff;
vis->precip = true;
}
#endif
@ -5330,7 +5394,7 @@ static void HWR_DrawSkyBackground(player_t *player)
// 0--1
(void)player;
HWR_GetTexture(skytexture);
HWR_GetTexture(texturetranslation[skytexture]);
//Hurdler: the sky is the only texture who need 4.0f instead of 1.0
// 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]);
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[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;
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;
// 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;
#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
fseek(file, model->header.offsetSkins, SEEK_SET);
if (model->header.numSkins > 0)
@ -319,8 +336,6 @@ static md2_model_t *md2_readModel(const char *filename)
md2_freeModel (model);
return 0;
}
;
}
// read texture coordinates
@ -334,8 +349,6 @@ static md2_model_t *md2_readModel(const char *filename)
md2_freeModel (model);
return 0;
}
}
// read triangles
@ -769,6 +782,7 @@ void HWR_InitMD2(void)
md2_playermodels[s].grpatch = NULL;
md2_playermodels[s].skin = -1;
md2_playermodels[s].notfound = true;
md2_playermodels[s].error = false;
}
for (i = 0; i < NUMSPRITES; i++)
{
@ -777,6 +791,7 @@ void HWR_InitMD2(void)
md2_models[i].grpatch = NULL;
md2_models[i].skin = -1;
md2_models[i].notfound = true;
md2_models[i].error = false;
}
// read the md2.dat file
@ -1349,7 +1364,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
UINT32 durs = spr->mobj->state->tics;
UINT32 tics = spr->mobj->tics;
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;
spriteframe_t *sprframe;
float finalscale;
@ -1378,6 +1393,8 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
else
md2 = &md2_models[spr->mobj->sprite];
if (md2->error)
return; // we already failed loading this before :(
if (!md2->model)
{
//CONS_Debug(DBG_RENDER, "Loading MD2... (%s)", sprnames[spr->mobj->sprite]);
@ -1391,6 +1408,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
else
{
//CONS_Debug(DBG_RENDER, " FAILED\n");
md2->error = true; // prevent endless fail
return;
}
}
@ -1464,7 +1482,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
p.x = FIXED_TO_FLOAT(spr->mobj->x);
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);
else
p.z = FIXED_TO_FLOAT(spr->mobj->z);

View File

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

View File

@ -244,6 +244,7 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...)
#define pglMaterialfv glMaterialfv
/* Raster functions */
#define pglPixelStorei glPixelStorei
#define pglReadPixels glReadPixels
/* Texture mapping */
@ -262,15 +263,8 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...)
/* texture mapping */ //GL_EXT_copy_texture
#ifndef KOS_GL_COMPATIBILITY
#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
/* 1.0 functions */
@ -365,6 +359,8 @@ typedef void (APIENTRY * PFNglMaterialfv) (GLint face, GLenum pname, GLfloat *pa
static PFNglMaterialfv pglMaterialfv;
/* 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);
static PFNglReadPixels pglReadPixels;
@ -391,7 +387,7 @@ static PFNglBindTexture pglBindTexture;
/* 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);
static PFNglCopyTexImage2D pglCopyTexImage2D;
#endif
/* GLU functions */
typedef GLint (APIENTRY * PFNgluBuild2DMipmaps) (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data);
static PFNgluBuild2DMipmaps pgluBuild2DMipmaps;
@ -403,7 +399,6 @@ static PFNglActiveTexture pglActiveTexture;
typedef void (APIENTRY *PFNglMultiTexCoord2f) (GLenum, GLfloat, GLfloat);
static PFNglMultiTexCoord2f pglMultiTexCoord2f;
#endif
#endif
#ifndef MINI_GL_COMPATIBILITY
/* 1.2 Parms */
@ -494,6 +489,7 @@ boolean SetupGLfunc(void)
GETOPENGLFUNC(pglLightModelfv , glLightModelfv)
GETOPENGLFUNC(pglMaterialfv , glMaterialfv)
GETOPENGLFUNC(pglPixelStorei , glPixelStorei)
GETOPENGLFUNC(pglReadPixels , glReadPixels)
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
boolean SetupGLFunc13(void)
{
#ifdef MINI_GL_COMPATIBILITY
return false;
#else
const GLubyte *version = pglGetString(GL_VERSION);
int glmajor, glminor;
gl13 = false;
#ifdef MINI_GL_COMPATIBILITY
return false;
#else
#ifdef STATIC_OPENGL
gl13 = true;
#else
// Parse the GL version
if (version != NULL)
{
if (sscanf((const char*)version, "%d.%d", &glmajor, &glminor) == 2)
{
// Look, we gotta prepare for the inevitable arrival of GL 2.0 code...
switch (glmajor)
{
case 1:
if (glminor == 3) gl13 = true;
break;
case 2:
case 3:
case 4:
gl13 = true;
default:
break;
}
if (glmajor == 1 && glminor >= 3)
gl13 = true;
else if (glmajor > 1)
gl13 = true;
}
}
@ -568,9 +552,6 @@ boolean SetupGLFunc13(void)
}
else
DBG_Printf("GL_ARB_multitexture support: disabled\n");
#undef GETOPENGLFUNC
#endif
return true;
#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 *row = malloc(dst_stride);
if (!row) return;
pglPixelStorei(GL_PACK_ALIGNMENT, 1);
pglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, dst_data);
pglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for(i = 0; i < height/2; i++)
{
memcpy(row, top, dst_stride);
@ -913,7 +896,9 @@ EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height,
INT32 j;
GLubyte *image = malloc(width*height*3*sizeof (*image));
if (!image) return;
pglPixelStorei(GL_PACK_ALIGNMENT, 1);
pglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, image);
pglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (i = height-1; i >= 0; i--)
{
for (j = 0; j < width; j++)
@ -1815,13 +1800,11 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value)
min_filter = GL_NEAREST;
#endif
}
#ifndef STATIC_OPENGL
if (!pgluBuild2DMipmaps)
{
MipMap = GL_FALSE;
min_filter = GL_LINEAR;
}
#endif
Flush(); //??? if we want to change filter mode by texture, remove this
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;
GLfloat s, t;

View File

@ -91,7 +91,7 @@ patch_t *tallminus;
patch_t *emeraldpics[7];
patch_t *tinyemeraldpics[7];
static patch_t *emblemicon;
static patch_t *tokenicon;
patch_t *tokenicon;
//-------------------------------------------
// misc vars
@ -198,21 +198,6 @@ void HU_LoadGraphics(void)
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;
for (i = 0; i < LT_FONTSIZE; i++)
{
@ -470,7 +455,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
boolean action = false;
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);
flags = READUINT8(*p);
@ -757,15 +742,8 @@ void HU_clearChatChars(void)
//
boolean HU_Responder(event_t *ev)
{
static boolean shiftdown = false;
UINT8 c;
if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
{
shiftdown = (ev->type == ev_keydown);
return chat_on;
}
if (ev->type != ev_keydown)
return false;
@ -797,6 +775,14 @@ boolean HU_Responder(event_t *ev)
}
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;
// use console translations
@ -854,7 +840,7 @@ static void HU_DrawChat(void)
else
{
//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;
}
@ -871,7 +857,7 @@ static void HU_DrawChat(void)
else
{
//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;
@ -883,7 +869,7 @@ static void HU_DrawChat(void)
}
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
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,
((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);
// 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);
}
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);
else
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]);
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]);
else
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
{
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);
}
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);
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);
else
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
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)
{
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);
else
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)
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
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
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
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;
}
@ -1311,7 +1309,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
strlcpy(name, tab[i].name, 9);
V_DrawString(x + 20, y,
((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);
if (gametype == GT_CTF)
@ -1337,12 +1335,12 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
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);
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);
else
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);
V_DrawString(x + 20, y,
((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);
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]);
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]);
else
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
{
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);
else
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)
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
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
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
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;
if (y > 160)

View File

@ -21,7 +21,7 @@
//------------------------------------
// heads up font
//------------------------------------
#define HU_FONTSTART '\x1F' // the first font character
#define HU_FONTSTART '\x19' // the first font character
#define HU_FONTEND '~'
#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1)
@ -71,6 +71,7 @@ extern patch_t *rmatcico;
extern patch_t *bmatcico;
extern patch_t *tagico;
extern patch_t *tallminus;
extern patch_t *tokenicon;
// set true when entering a chat message
extern boolean chat_on;
@ -78,9 +79,6 @@ extern boolean chat_on;
// set true whenever the tab rankings are being shown for any reason
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.
void HU_Init(void);

View File

@ -85,7 +85,7 @@ extern doomcom_t *doomcom;
/** \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
*/

View File

@ -296,6 +296,14 @@ char *I_GetEnv(const char *name);
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);
#endif

View File

@ -179,6 +179,7 @@ static UINT8 UPNP_support = TRUE;
#include "i_system.h"
#include "i_net.h"
#include "d_net.h"
#include "d_netfil.h"
#include "i_tcp.h"
#include "m_argv.h"
@ -482,21 +483,12 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
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 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)
{
SINT8 j;
@ -506,13 +498,81 @@ static void cleanupnodes(void)
// Why can't I start at zero?
for (j = 1; j < MAXNETNODES; j++)
//if (!(nodeingame[j] || SV_SendingFile(j)))
if (!nodeingame[j])
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
#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;
int j;
@ -535,13 +595,12 @@ static void SOCK_Get(void)
doomcom->remotenode = (INT16)j; // good packet from a game player
doomcom->datalength = (INT16)c;
nodesocket[j] = mysockets[n];
return;
return false;
}
}
// not found
// find a free slot
cleanupnodes();
j = getfreenode();
if (j > 0)
{
@ -564,14 +623,15 @@ static void SOCK_Get(void)
}
if (i == numbans)
SOCK_bannednode[j] = false;
return;
return true;
}
else
DEBFILE("New node detected: No more free slots\n");
}
}
doomcom->remotenode = -1; // no packet
return false;
}
#endif
@ -1256,7 +1316,6 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
gaie = I_getaddrinfo(address, port, &hints, &ai);
if (gaie == 0)
{
cleanupnodes();
newnode = getfreenode();
}
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_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;
@ -412,6 +415,7 @@ static int lib_consPrintf(lua_State *L)
if (n < 2)
return luaL_error(L, "CONS_Printf requires at least two arguments: player and text.");
//HUDSAFE
INLEVEL
plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
if (!plr)
return LUA_ErrInvalid(L, "player_t");

View File

@ -43,6 +43,10 @@ enum hook {
hook_PlayerMsg,
hook_HurtMsg,
hook_PlayerSpawn,
hook_ShieldSpawn,
hook_ShieldSpecial,
hook_MobjMoveBlocked,
hook_MapThingSpawn,
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
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_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
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?)
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_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source); // Hook for P_KillMobj by mobj type
UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for P_KillMobj by mobj type
#define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type
#define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type
#define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping)
@ -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_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_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_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

View File

@ -54,6 +54,10 @@ const char *const hookNames[hook_MAX+1] = {
"PlayerMsg",
"HurtMsg",
"PlayerSpawn",
"ShieldSpawn",
"ShieldSpecial",
"MobjMoveBlocked",
"MapThingSpawn",
NULL
};
@ -74,12 +78,30 @@ typedef struct hook_s* hook_p;
#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;
// Takes hook, function, and additional arguments (mobj type to act on, etc.)
static int lib_addHook(lua_State *L)
{
static struct hook_s hook = {NULL, 0, 0, {0}, false};
static UINT32 nextid;
hook_p hookp, *lastp;
hook.type = luaL_checkoption(L, 1, NULL, hookNames);
@ -106,9 +128,12 @@ static int lib_addHook(lua_State *L)
case hook_BossDeath:
case hook_MobjRemoved:
case hook_HurtMsg:
case hook_MobjMoveBlocked:
case hook_MapThingSpawn:
hook.s.mt = MT_NULL;
if (lua_isnumber(L, 2))
hook.s.mt = lua_tonumber(L, 2);
luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t");
break;
case hook_BotAI:
hook.s.skinname = NULL;
@ -141,18 +166,51 @@ static int lib_addHook(lua_State *L)
hooksAvailable[hook.type/8] |= 1<<(hook.type%8);
// iterate the hook metadata structs
// set hook.id to the highest id + 1
// set lastp to the last hook struct's "next" pointer.
lastp = &roothook;
hook.id = 0;
for (hookp = roothook; hookp; hookp = hookp->next)
hook.id = nextid++;
// Special cases for some hook types (see the comments above mobjthinkerhooks declaration)
switch(hook.type)
{
if (hookp->id >= hook.id)
hook.id = hookp->id+1;
lastp = &hookp->next;
case hook_MobjThinker:
lastp = &mobjthinkerhooks[hook.s.mt];
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.
hookp = ZZ_Alloc(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);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == which
&& (hookp->s.mt == MT_NULL || hookp->s.mt == mo->type))
// Look for all generic mobj hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
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)
LUA_PushUserdata(gL, mo, META_MOBJ);
@ -217,7 +295,7 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which)
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
for (hookp = playerhooks; hookp; hookp = hookp->next)
if (hookp->type == which)
{
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);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == which
&& (hookp->s.mt == MT_NULL || hookp->s.mt == thing1->type))
// Look for all generic mobj collision hooks
for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
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)
{
@ -372,6 +479,59 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
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
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);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_TouchSpecial
&& (hookp->s.mt == MT_NULL || hookp->s.mt == special->type))
// Look for all generic touch special hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
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)
{
@ -412,7 +596,7 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
}
// 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;
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);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_ShouldDamage
&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
// Look for all generic should damage hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == hook_ShouldDamage)
{
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);
}
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);
return shouldDamage;
}
// 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;
boolean hooked = false;
@ -469,9 +688,9 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDamage
&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
// Look for all generic mobj damage hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDamage)
{
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);
}
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);
return hooked;
}
// 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;
boolean hooked = false;
@ -512,9 +761,9 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDeath
&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
// Look for all generic mobj death hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDeath)
{
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);
}
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);
return hooked;
}
@ -652,9 +929,8 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_LinedefExecute
&& !strcmp(hookp->s.funcname, line->text))
for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next)
if (!strcmp(hookp->s.funcname, line->text))
{
if (lua_gettop(gL) == 0)
{
@ -729,7 +1005,7 @@ boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
}
// 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;
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, 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, -4);
lua_pushvalue(gL, -4);
lua_pushvalue(gL, -4);
if (lua_pcall(gL, 3, 1, 0)) {
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);
@ -798,4 +1076,66 @@ void LUAh_NetArchiveHook(lua_CFunction archFunc)
// 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

View File

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

View File

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

View File

@ -38,12 +38,22 @@ extern lua_State *gL;
#define META_SUBSECTOR "SUBSECTOR_T*"
#define META_SECTOR "SECTOR_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_CVAR "CONSVAR_T*"
#define META_SECTORLINES "SECTOR_T*LINES"
#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_PATCH "PATCH_T*"
@ -64,6 +74,7 @@ int LUA_PlayerLib(lua_State *L);
int LUA_SkinLib(lua_State *L);
int LUA_ThinkerLib(lua_State *L);
int LUA_MapLib(lua_State *L);
int LUA_BlockmapLib(lua_State *L);
int LUA_HudLib(lua_State *L);
#endif

View File

@ -185,15 +185,98 @@ static const char *const ffloor_opt[] = {
"alpha",
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 valid_opt[] ={"valid",NULL};
///////////////////////////////////
// sector list iterate functions //
///////////////////////////////////
// iterates through a sector's thinglist!
static int lib_iterateSectorThinglist(lua_State *L)
{
mobj_t *state = 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)
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 *ffloor = NULL;
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 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;
}
////////////////////
// sector.lines[] //
////////////////////
// sector.lines, i -> sector.lines[i]
// sector.lines.valid, for validity checking
static int sectorlines_get(lua_State *L)
@ -323,6 +413,10 @@ static int sectorlines_num(lua_State *L)
return 1;
}
//////////////
// sector_t //
//////////////
static int sector_get(lua_State *L)
{
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:
lua_pushfixed(L, sector->ceilingheight);
return 1;
case sector_floorpic: { // floorpic
levelflat_t *levelflat;
INT16 i;
for (i = 0, levelflat = levelflats; i != sector->floorpic; i++, levelflat++)
;
lua_pushlstring(L, levelflat->name, 8);
case sector_floorpic: // floorpic
lua_pushlstring(L, levelflats[sector->floorpic].name, 8);
return 1;
}
case sector_ceilingpic: { // ceilingpic
levelflat_t *levelflat;
INT16 i;
for (i = 0, levelflat = levelflats; i != sector->ceilingpic; i++, levelflat++)
;
lua_pushlstring(L, levelflat->name, 8);
case sector_ceilingpic: // ceilingpic
lua_pushlstring(L, levelflats[sector->ceilingpic].name, 8);
return 1;
}
case sector_lightlevel:
lua_pushinteger(L, sector->lightlevel);
return 1;
@ -400,46 +484,6 @@ static int sector_get(lua_State *L)
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)
{
sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
@ -514,6 +558,10 @@ static int sector_num(lua_State *L)
return 1;
}
/////////////////
// subsector_t //
/////////////////
static int subsector_get(lua_State *L)
{
subsector_t *subsector = *((subsector_t **)luaL_checkudata(L, 1, META_SUBSECTOR));
@ -553,6 +601,10 @@ static int subsector_num(lua_State *L)
return 1;
}
////////////
// line_t //
////////////
static int line_get(lua_State *L)
{
line_t *line = *((line_t **)luaL_checkudata(L, 1, META_LINE));
@ -650,6 +702,10 @@ static int line_num(lua_State *L)
return 1;
}
////////////////////
// line.sidenum[] //
////////////////////
static int sidenum_get(lua_State *L)
{
UINT16 *sidenum = *((UINT16 **)luaL_checkudata(L, 1, META_SIDENUM));
@ -678,6 +734,10 @@ static int sidenum_get(lua_State *L)
return 1;
}
////////////
// side_t //
////////////
static int side_get(lua_State *L)
{
side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE));
@ -779,6 +839,10 @@ static int side_num(lua_State *L)
return 1;
}
//////////////
// vertex_t //
//////////////
static int vertex_get(lua_State *L)
{
vertex_t *vertex = *((vertex_t **)luaL_checkudata(L, 1, META_VERTEX));
@ -818,9 +882,293 @@ static int vertex_num(lua_State *L)
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)
{
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 sectors.iterate() directly, use it as 'for sector in sectors.iterate do <block> end'.");
lua_settop(L, 2);
@ -838,6 +1186,8 @@ static int lib_iterateSectors(lua_State *L)
static int lib_getSector(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))
@ -864,9 +1214,15 @@ static int lib_numsectors(lua_State *L)
return 1;
}
//////////////////
// subsectors[] //
//////////////////
static int lib_iterateSubsectors(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 subsectors.iterate() directly, use it as 'for subsector in subsectors.iterate do <block> end'.");
lua_settop(L, 2);
@ -884,6 +1240,8 @@ static int lib_iterateSubsectors(lua_State *L)
static int lib_getSubsector(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))
@ -910,9 +1268,15 @@ static int lib_numsubsectors(lua_State *L)
return 1;
}
/////////////
// lines[] //
/////////////
static int lib_iterateLines(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 lines.iterate() directly, use it as 'for line in lines.iterate do <block> end'.");
lua_settop(L, 2);
@ -930,6 +1294,8 @@ static int lib_iterateLines(lua_State *L)
static int lib_getLine(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))
@ -956,9 +1322,15 @@ static int lib_numlines(lua_State *L)
return 1;
}
/////////////
// sides[] //
/////////////
static int lib_iterateSides(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 sides.iterate() directly, use it as 'for side in sides.iterate do <block> end'.");
lua_settop(L, 2);
@ -976,6 +1348,8 @@ static int lib_iterateSides(lua_State *L)
static int lib_getSide(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))
@ -1002,9 +1376,15 @@ static int lib_numsides(lua_State *L)
return 1;
}
////////////////
// vertexes[] //
////////////////
static int lib_iterateVertexes(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 vertexes.iterate() directly, use it as 'for vertex in vertexes.iterate do <block> end'.");
lua_settop(L, 2);
@ -1022,6 +1402,8 @@ static int lib_iterateVertexes(lua_State *L)
static int lib_getVertex(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))
@ -1048,6 +1430,121 @@ static int lib_numvertexes(lua_State *L)
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)
{
ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
@ -1191,6 +1688,10 @@ static int ffloor_set(lua_State *L)
return 0;
}
/////////////////////
// mapheaderinfo[] //
/////////////////////
static int lib_getMapheaderinfo(lua_State *L)
{
// i -> mapheaderinfo[i-1]
@ -1223,6 +1724,10 @@ static int lib_nummapheaders(lua_State *L)
return 1;
}
/////////////////
// mapheader_t //
/////////////////
static int mapheaderinfo_get(lua_State *L)
{
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_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);
lua_pushcfunction(L, mapheaderinfo_get);
lua_setfield(L, -2, "__index");
@ -1425,6 +1965,28 @@ int LUA_MapLib(lua_State *L)
lua_setmetatable(L, -2);
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_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getMapheaderinfo);

View File

@ -34,6 +34,7 @@ enum mobj_e {
mobj_angle,
mobj_sprite,
mobj_frame,
mobj_sprite2,
mobj_anim_duration,
mobj_touching_sectorlist,
mobj_subsector,
@ -93,6 +94,7 @@ static const char *const mobj_opt[] = {
"angle",
"sprite",
"frame",
"sprite2",
"anim_duration",
"touching_sectorlist",
"subsector",
@ -189,6 +191,9 @@ static int mobj_get(lua_State *L)
case mobj_frame:
lua_pushinteger(L, mo->frame);
break;
case mobj_sprite2:
lua_pushinteger(L, mo->sprite2);
break;
case mobj_anim_duration:
lua_pushinteger(L, mo->anim_duration);
break;
@ -411,6 +416,9 @@ static int mobj_set(lua_State *L)
case mobj_frame:
mo->frame = (UINT32)luaL_checkinteger(L, 3);
break;
case mobj_sprite2:
mo->sprite2 = P_GetMobjSprite2(mo, (UINT8)luaL_checkinteger(L, 3));
break;
case mobj_anim_duration:
mo->anim_duration = (UINT16)luaL_checkinteger(L, 3);
break;
@ -503,7 +511,8 @@ static int mobj_set(lua_State *L)
for (i = 0; i < numskins; i++)
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 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)
{
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 mapthings.iterate() directly, use it as 'for mapthing in mapthings.iterate do <block> end'.");
lua_settop(L, 2);
@ -760,6 +771,8 @@ static int lib_iterateMapthings(lua_State *L)
static int lib_getMapthing(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))

View File

@ -25,6 +25,8 @@
static int lib_iteratePlayers(lua_State *L)
{
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)
{
//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;
// 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)
{
lua_Integer i = luaL_checkinteger(L, 2);
@ -122,8 +126,8 @@ static int player_get(lua_State *L)
lua_pushfixed(L, plr->bob);
else if (fastcmp(field,"aiming"))
lua_pushangle(L, plr->aiming);
else if (fastcmp(field,"health"))
lua_pushinteger(L, plr->health);
else if (fastcmp(field,"rings"))
lua_pushinteger(L, plr->rings);
else if (fastcmp(field,"pity"))
lua_pushinteger(L, plr->pity);
else if (fastcmp(field,"currentweapon"))
@ -190,8 +194,6 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->gotcontinue);
else if (fastcmp(field,"speed"))
lua_pushfixed(L, plr->speed);
else if (fastcmp(field,"jumping"))
lua_pushboolean(L, plr->jumping);
else if (fastcmp(field,"secondjump"))
lua_pushinteger(L, plr->secondjump);
else if (fastcmp(field,"fly1"))
@ -382,8 +384,8 @@ static int player_set(lua_State *L)
else if (plr == &players[secondarydisplayplayer])
localaiming2 = plr->aiming;
}
else if (fastcmp(field,"health"))
plr->health = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"rings"))
plr->rings = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"pity"))
plr->pity = (SINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"currentweapon"))
@ -455,8 +457,6 @@ static int player_set(lua_State *L)
plr->gotcontinue = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"speed"))
plr->speed = luaL_checkfixed(L, 3);
else if (fastcmp(field,"jumping"))
plr->jumping = luaL_checkboolean(L, 3);
else if (fastcmp(field,"secondjump"))
plr->secondjump = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"fly1"))

View File

@ -48,6 +48,7 @@ static lua_CFunction liblist[] = {
LUA_SkinLib, // skin_t, skins[]
LUA_ThinkerLib, // thinker_t
LUA_MapLib, // line_t, side_t, sector_t, subsector_t
LUA_BlockmapLib, // blockmap stuff
LUA_HudLib, // HUD stuff
NULL
};
@ -395,6 +396,7 @@ void LUA_InvalidateLevel(void)
{
thinker_t *th;
size_t i;
ffloor_t *rover = NULL;
if (!gL)
return;
@ -406,7 +408,15 @@ void LUA_InvalidateLevel(void)
for (i = 0; i < numsubsectors; i++)
LUA_InvalidateUserdata(&subsectors[i]);
for (i = 0; i < numsectors; 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++)
{
LUA_InvalidateUserdata(&lines[i]);
@ -416,6 +426,16 @@ void LUA_InvalidateLevel(void)
LUA_InvalidateUserdata(&sides[i]);
for (i = 0; i < numvertexes; 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)
@ -455,6 +475,11 @@ enum
ARCH_SIDE,
ARCH_SUBSECTOR,
ARCH_SECTOR,
#ifdef HAVE_LUA_SEGS
ARCH_SEG,
ARCH_NODE,
#endif
ARCH_FFLOOR,
ARCH_MAPHEADER,
ARCH_TEND=0xFF,
@ -474,6 +499,11 @@ static const struct {
{META_SIDE, ARCH_SIDE},
{META_SUBSECTOR,ARCH_SUBSECTOR},
{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},
{NULL, ARCH_NULL}
};
@ -566,14 +596,14 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
{
mobjinfo_t *info = *((mobjinfo_t **)lua_touserdata(gL, myindex));
WRITEUINT8(save_p, ARCH_MOBJINFO);
WRITEUINT8(save_p, info - mobjinfo);
WRITEUINT16(save_p, info - mobjinfo);
break;
}
case ARCH_STATE:
{
state_t *state = *((state_t **)lua_touserdata(gL, myindex));
WRITEUINT8(save_p, ARCH_STATE);
WRITEUINT8(save_p, state - states);
WRITEUINT16(save_p, state - states);
break;
}
case ARCH_MOBJ:
@ -664,6 +694,56 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
}
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:
{
mapheader_t *header = *((mapheader_t **)lua_touserdata(gL, myindex));
@ -842,6 +922,23 @@ static UINT8 UnArchiveValue(int TABLESINDEX)
case ARCH_SECTOR:
LUA_PushUserdata(gL, &sectors[READUINT16(save_p)], META_SECTOR);
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:
LUA_PushUserdata(gL, &sectors[READUINT16(save_p)], META_MAPHEADER);
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

View File

@ -16,84 +16,127 @@
#include "lua_script.h"
#include "lua_libs.h"
#define META_ITERATIONSTATE "iteration state"
static const char *const iter_opt[] = {
"all",
"mobj",
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)
{
int state = luaL_checkoption(L, 1, "mobj", iter_opt);
thinker_t *th = NULL, *next = NULL;
struct iterationState *it;
thinker_t *th = NULL;
actionf_p1 searchFunc;
const char *searchMeta;
if (gamestate != GS_LEVEL)
return luaL_error(L, "This function can only be used in a level!");
it = luaL_checkudata(L, 1, META_ITERATIONSTATE);
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:
searchFunc = NULL;
searchMeta = NULL;
break;
case 1:
default:
searchFunc = (actionf_p1)P_MobjThinker;
searchMeta = META_MOBJ;
break;
if (lua_islightuserdata(L, 2))
th = lua_touserdata(L, 2);
else
{
th = *(thinker_t **)lua_touserdata(L, -1);
if (!th)
{
if (it->next == LUA_REFNIL)
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)) {
if (lua_islightuserdata(L, 1))
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;
luaL_unref(L, LUA_REGISTRYINDEX, it->next);
it->next = LUA_REFNIL;
if (!th) // something got our userdata invalidated!
return 0;
if (th && !next)
next = th->next;
if (!next)
return luaL_error(L, "next thinker invalidated during iteration");
if (searchFunc == NULL)
{
if ((th = th->next) != &thinkercap)
for (; next != &thinkercap; next = next->next)
if (!it->filter || next->function.acp1 == it->filter)
{
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
LUA_PushUserdata(L, th, META_MOBJ);
else
lua_pushlightuserdata(L, th);
push_thinker(next);
if (next->next != &thinkercap)
{
push_thinker(next->next);
it->next = luaL_ref(L, LUA_REGISTRYINDEX);
}
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;
}
static int lib_startIterate(lua_State *L)
{
luaL_checkoption(L, 1, iter_opt[0], iter_opt);
lua_pushcfunction(L, lib_iterateThinkers);
lua_pushvalue(L, 1);
struct iterationState *it;
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;
}
#undef push_thinker
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_pushcfunction(L, lib_startIterate);
lua_pushcfunction(L, lib_iterateThinkers);
lua_pushcclosure(L, lib_startIterate, 1);
lua_setfield(L, -2, "iterate");
lua_setglobal(L, "thinkers");
return 0;

View File

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

View File

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

View File

@ -452,7 +452,7 @@ void Command_RTeleport_f(void)
else
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)
{
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->z = 123311;
players[consoleplayer].score = 1337;
players[consoleplayer].health = 1337;
players[consoleplayer].rings = 1337;
players[consoleplayer].mo->destscale = 25;
P_SetThingPosition(players[consoleplayer].mo);
@ -720,7 +720,7 @@ void Command_Setrings_f(void)
if (COM_Argc() > 1)
{
// 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)));
if (!G_IsSpecialStage(gamemap) || !useNightsSS)
players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings
@ -942,7 +942,7 @@ boolean OP_FreezeObjectplace(void)
if (!objectplacing)
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 true;
@ -968,7 +968,7 @@ void OP_NightsObjectplace(player_t *player)
if (player->pflags & PF_ATTACKDOWN)
{
// 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;
// Do nothing.
@ -1019,7 +1019,7 @@ void OP_NightsObjectplace(player_t *player)
}
// This places a ring!
if (cmd->buttons & BT_CAMRIGHT)
if (cmd->buttons & BT_WEAPONNEXT)
{
player->pflags |= PF_ATTACKDOWN;
if (!OP_HeightOkay(player, false))
@ -1030,7 +1030,7 @@ void OP_NightsObjectplace(player_t *player)
}
// This places a wing item!
if (cmd->buttons & BT_CAMLEFT)
if (cmd->buttons & BT_WEAPONPREV)
{
player->pflags |= PF_ATTACKDOWN;
if (!OP_HeightOkay(player, false))
@ -1054,7 +1054,7 @@ void OP_NightsObjectplace(player_t *player)
if (!OP_HeightOkay(player, false))
return;
if (player->mo->target->flags & MF_AMBUSH)
if (player->mo->target->flags2 & MF2_AMBUSH)
angle = (UINT16)player->anotherflyangle;
else
{
@ -1165,19 +1165,20 @@ void OP_ObjectplaceMovement(player_t *player)
if (player->pflags & PF_ATTACKDOWN)
{
// 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;
// Do nothing.
return;
}
if (cmd->buttons & BT_CAMLEFT)
if (cmd->buttons & BT_WEAPONPREV)
{
OP_CycleThings(-1);
player->pflags |= PF_ATTACKDOWN;
}
else if (cmd->buttons & BT_CAMRIGHT)
if (cmd->buttons & BT_WEAPONNEXT)
{
OP_CycleThings(1);
player->pflags |= PF_ATTACKDOWN;
@ -1254,7 +1255,7 @@ void Command_ObjectPlace_f(void)
{
objectplacing = true;
if ((players[0].pflags & PF_NIGHTSMODE))
if ((players[0].powers[pw_carry] == CR_NIGHTSMODE))
return;
if (!COM_CheckParm("-silent"))
@ -1264,10 +1265,10 @@ void Command_ObjectPlace_f(void)
HU_DoCEcho(va(M_GetText(
"\\\\\\\\\\\\\\\\\\\\\\\\\x82"
" Objectplace Controls: \x80\\\\"
"Camera L/R: Cycle mapthings\\"
" Jump: Float up \\"
" Spin: Float down \\"
" Fire Ring: Place object \\")));
"Weapon Next/Prev: Cycle mapthings\\"
" Jump: Float up \\"
" Spin: Float down \\"
" Fire Ring: Place object \\")));
}
// Save all the player's data.
@ -1297,7 +1298,7 @@ void Command_ObjectPlace_f(void)
// Like the classics, recover from death by entering objectplace
if (players[0].mo->health <= 0)
{
players[0].mo->health = players[0].health = 1;
players[0].mo->health = 1;
players[0].deadtimer = 0;
op_oldflags1 = mobjinfo[MT_PLAYER].flags;
++players[0].lives;
@ -1325,7 +1326,7 @@ void Command_ObjectPlace_f(void)
// Don't touch the NiGHTS Objectplace stuff.
// ... 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;
// If still in dummy state, get out of it.

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