Import v0.47 sources

This commit is contained in:
Tyrann 2006-09-10 19:02:51 +09:30
commit 130e22195a
362 changed files with 176466 additions and 0 deletions

892
Makefile Normal file
View File

@ -0,0 +1,892 @@
#
# A Makefile to run under MinGW on Win32
#
# Pretty crappy, but you can build all the targets if you clean in
# between. It's needed because various -DXXX flags change the object files and
# I'm still working on doing it properly... but slowly.
#
TYR_VERSION_MAJOR = 0
TYR_VERSION_MINOR = 47
TYR_VERSION_BUILD =
TYR_VERSION = $(TYR_VERSION_MAJOR).$(TYR_VERSION_MINOR)$(TYR_VERSION_BUILD)
# ============================================================================
# Choose the target here: (Yes, I know it sucks).
# ============================================================================
TARGET_APP = NQ
#TARGET_APP = QW
#TARGET_APP = QWSV
#TARGET_RENDER = SW
TARGET_RENDER = GL
#TARGET_OS = WIN32
TARGET_OS = LINUX
#DEBUG=y # Compile with debug info
#NO_X86_ASM=y # Compile with no x86 asm
# ============================================================================
# EXPORT ALL VARIABLES
export
.PHONY: default clean \
nq-w32-sw-objs nq-w32-gl-objs qw-w32-sw-objs qw-w32-gl-objs \
qwsv-w32-objs \
nq-linux-sw-objs nq-linux-gl-objs qw-linux-sw-objs qw-linux-gl-objs \
qwsv-linux-objs
# ============================================================================
# Helper functions
# ============================================================================
check_gcc = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
# ============================================================================
# FIXME - how to detect build env reliably...?
ifeq ($(OSTYPE),msys)
TOPDIR := $(shell pwd -W)
else
TOPDIR := $(shell pwd)
endif
# ----------------------------
# The two project directories
# ----------------------------
NQ_DIR = NQ
QW_DIR = QW
# ----------------------------
# Include dirs compiler flags
# ----------------------------
DX_INC = $(TOPDIR)/dxsdk/sdk/inc
ST_INC = $(TOPDIR)/scitech/include
WIN32_SW_INC = $(DX_INC) $(ST_INC)
WIN32_GL_INC = $(DX_INC)
QWSV_INC = $(TOPDIR)/$(QW_DIR)/server $(TOPDIR)/$(QW_DIR)/client
WIN32_SW_IFLAGS := -idirafter $(DX_INC)
WIN32_SW_IFLAGS += -idirafter $(ST_INC)
WIN32_SW_IFLAGS += -I$(TOPDIR)/include
WIN32_GL_IFLAGS := -idirafter $(DX_INC)
WIN32_GL_IFLAGS += -I$(TOPDIR)/include
QWSV_WIN32_IFLAGS = $(patsubst %,-idirafter %,$(QWSV_INC)) -I$(TOPDIR)/include
QWSV_LINUX_IFLAGS = $(patsubst %,-idirafter %,$(QWSV_INC)) -I$(TOPDIR)/include
# ------------------------------
# define flags
# ------------------------------
DFLAGS := -DTYR_VERSION=$(TYR_VERSION)
# App specific hacks
ifeq ($(TARGET_APP),NQ)
DFLAGS += -DNQ_HACK
endif
ifeq ($(TARGET_APP),QW)
DFLAGS += -DQW_HACK
endif
ifeq ($(TARGET_APP),QWSV)
DFLAGS += -DQW_HACK -DSERVERONLY
endif
ifeq ($(TARGET_RENDER),GL)
DFLAGS += -DGLQUAKE
endif
ifeq ($(TARGET_OS),WIN32)
DFLAGS += -DWIN32 -D_WIN32
endif
ifdef DEBUG
DFLAGS += -DDEBUG
else
DFLAGS += -DNDEBUG
endif
ifdef NO_X86_ASM
DFLAGS += -U__i386__
endif
NQ_LINUX_SW_DFLAGS = $(DFLAGS) -DX11 -DELF -I$(TOPDIR)/include
QW_LINUX_SW_DFLAGS = $(DFLAGS) -DX11 -DELF -I$(TOPDIR)/include
NQ_LINUX_GL_DFLAGS = $(DFLAGS) -DX11 -DELF -I$(TOPDIR)/include
QW_LINUX_GL_DFLAGS = $(DFLAGS) -DX11 -DELF -DGL_EXT_SHARED -I$(TOPDIR)/include
# -------------------------
# Set up the various FLAGS
# -------------------------
# NQ - Normal Quake
ifeq ($(TARGET_APP),NQ)
ifeq ($(TARGET_RENDER),SW)
ifeq ($(TARGET_OS),WIN32)
CPPFLAGS = $(DFLAGS) $(WIN32_SW_IFLAGS) -I$(TOPDIR)/NQ
endif
ifeq ($(TARGET_OS),LINUX)
CPPFLAGS = $(NQ_LINUX_SW_DFLAGS) -I$(TOPDIR)/NQ
endif
endif
ifeq ($(TARGET_RENDER),GL)
ifeq ($(TARGET_OS),WIN32)
CPPFLAGS = $(DFLAGS) $(WIN32_GL_IFLAGS) -I$(TOPDIR)/NQ
endif
ifeq ($(TARGET_OS),LINUX)
CPPFLAGS = $(NQ_LINUX_GL_DFLAGS) $(NQ_LINUX_GL_IFLAGS) -I$(TOPDIR)/NQ
endif
endif
endif
# QW - QuakeWorld Client
ifeq ($(TARGET_APP),QW)
ifeq ($(TARGET_RENDER),SW)
ifeq ($(TARGET_OS),WIN32)
CPPFLAGS = $(DFLAGS) $(WIN32_SW_IFLAGS) -I$(TOPDIR)/$(QW_DIR)/client -I$(TOPDIR)/$(QW_DIR)/common
endif
ifeq ($(TARGET_OS),LINUX)
CPPFLAGS = $(QW_LINUX_SW_DFLAGS) -I$(TOPDIR)/$(QW_DIR)/client -I$(TOPDIR)/$(QW_DIR)/common
endif
endif
ifeq ($(TARGET_RENDER),GL)
ifeq ($(TARGET_OS),WIN32)
CPPFLAGS = $(DFLAGS) $(WIN32_GL_IFLAGS) -I$(TOPDIR)/$(QW_DIR)/client -I$(TOPDIR)/$(QW_DIR)/common
endif
ifeq ($(TARGET_OS),LINUX)
CPPFLAGS = $(QW_LINUX_GL_DFLAGS) $(QW_LINUX_GL_IFLAGS) -I$(TOPDIR)/$(QW_DIR)/client -I$(TOPDIR)/$(QW_DIR)/common
endif
endif
endif
# QWSV - QuakeWorld Server
ifeq ($(TARGET_APP),QWSV)
ifeq ($(TARGET_OS),WIN32)
CPPFLAGS = $(DFLAGS) $(QWSV_WIN32_IFLAGS)
endif
ifeq ($(TARGET_OS),LINUX)
CPPFLAGS = $(DFLAGS) $(QWSV_LINUX_IFLAGS)
endif
endif
# ------------------------------------------------------
# Define the default target based on TARGET_* variables
# ------------------------------------------------------
DT_PREFIX =tyr-
ifeq ($(TARGET_RENDER),GL)
DT_RENDER =gl
else
DT_RENDER =
endif
ifeq ($(TARGET_APP),NQ)
DT_APP =quake
endif
ifeq ($(TARGET_APP),QW)
DT_APP =qwcl
endif
ifeq ($(TARGET_APP),QWSV)
DT_APP =qwsv
endif
ifeq ($(TARGET_OS),WIN32)
DT_EXT =.exe
else
DT_EXT =
endif
DEFAULT_TARGET = $(DT_PREFIX)$(DT_RENDER)$(DT_APP)$(DT_EXT)
# --------------
# Library stuff
# --------------
WIN_LIBDIR = C:/mingw-1.1/lib
NQ_ST_LIBDIR = scitech/lib/win32/vc
QW_ST_LIBDIR = scitech/lib/win32/vc
NQ_W32_COMMON_LIBS = wsock32 winmm dxguid
NQ_W32_SW_LIBS = mgllt ddraw
NQ_W32_GL_LIBS = opengl32 comctl32
LINUX_X11_LIBDIR = /usr/X11R6/lib
NQ_LINUX_COMMON_LIBS = m X11 Xext Xxf86dga Xxf86vm
NQ_LINUX_GL_LIBS = GL
NQ_W32_SW_LFLAGS = -mwindows $(patsubst %,-l%,$(NQ_W32_SW_LIBS) $(NQ_W32_COMMON_LIBS))
NQ_W32_GL_LFLAGS = -mwindows $(patsubst %,-l%,$(NQ_W32_GL_LIBS) $(NQ_W32_COMMON_LIBS))
NQ_LINUX_SW_LFLAGS = $(patsubst %,-l%,$(NQ_LINUX_COMMON_LIBS))
NQ_LINUX_GL_LFLAGS = $(patsubst %,-l%,$(NQ_LINUX_COMMON_LIBS) $(NQ_LINUX_GL_LIBS))
# ---------------------------------------
# Define some build variables
# ---------------------------------------
# Try to use a more recent GCC if available (my Debian system has several)
ifneq ($(shell which gcc-3.5),)
CC = gcc-3.5
else
ifneq ($(shell which gcc-3.4),)
CC = gcc-3.4
else
CC = gcc
endif
endif
CFLAGS := -Wall -Wno-trigraphs
# Library headers on the Windows side give warnings
ifeq ($(TARGET_OS),LINUX)
CFLAGS += -Werror
endif
ifdef DEBUG
CFLAGS += -g
STRIP_CMD = @echo "** Debug build - not stripping"
else
# Note that "-fomit-frame-pointer" seems to screw some things up
# (at least on MinGW)
CFLAGS += -O2
CFLAGS += $(call check_gcc,-fweb,)
CFLAGS += $(call check_gcc,-frename-registers,)
CFLAGS += $(call check_gcc,-mtune=i686,-mcpu=i686)
STRIP_CMD = strip
endif
# -------------------------------------
# Got to build something by default...
# -------------------------------------
default: $(DEFAULT_TARGET)
# ---------------------------
# Tweak the implicit ruleset
# ---------------------------
%.o: %.S
$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $^
%.o: %.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $^
# ----------------------------------------------------------------------------
# Normal Quake (NQ)
# ----------------------------------------------------------------------------
# Objects common to all versions of NQ, sources are c code
NQ_COMMON_C_OBJS = \
chase.o \
cl_demo.o \
cl_input.o \
cl_main.o \
cl_parse.o \
cl_tent.o \
cmd.o \
common.o \
console.o \
crc.o \
cvar.o \
host.o \
host_cmd.o \
keys.o \
mathlib.o \
menu.o \
net_dgrm.o \
net_loop.o \
net_main.o \
net_vcr.o \
pr_cmds.o \
pr_edict.o \
pr_exec.o \
r_part.o \
\
rb_tree.o \
\
sbar.o \
\
shell.o \
\
snd_dma.o \
snd_mem.o \
snd_mix.o \
sv_main.o \
sv_move.o \
sv_phys.o \
sv_user.o \
view.o \
wad.o \
world.o \
zone.o
NQ_COMMON_ASM_OBJS = \
math.o \
snd_mixa.o \
worlda.o
# Used in both SW and GL versions of NQ on the Win32 platform
NQ_W32_C_OBJS = \
cd_win.o \
conproc.o \
in_win.o \
net_win.o \
net_wins.o \
net_wipx.o \
snd_win.o \
sys_win.o
NQ_W32_ASM_OBJS = \
sys_wina.o
# Used in both SW and GL versions on NQ on the Linux platform
NQ_LINUX_C_OBJS = \
cd_linux.o \
net_udp.o \
net_bsd.o \
snd_linux.o \
sys_linux.o \
x11_core.o \
in_x11.o
NQ_LINUX_ASM_OBJS = \
sys_dosa.o
# Objects only used in software rendering versions of NQ
NQ_SW_C_OBJS = \
d_edge.o \
d_fill.o \
d_init.o \
d_modech.o \
d_part.o \
d_polyse.o \
d_scan.o \
d_sky.o \
d_sprite.o \
d_surf.o \
d_vars.o \
d_zpoint.o \
draw.o \
model.o \
r_aclip.o \
r_alias.o \
r_bsp.o \
r_draw.o \
r_edge.o \
r_efrag.o \
r_light.o \
r_main.o \
r_misc.o \
r_sky.o \
r_sprite.o \
r_surf.o \
r_vars.o \
screen.o
NQ_SW_ASM_OBJS = \
d_draw.o \
d_draw16.o \
d_parta.o \
d_polysa.o \
d_scana.o \
d_spr8.o \
d_varsa.o \
r_aclipa.o \
r_aliasa.o \
r_drawa.o \
r_edgea.o \
r_varsa.o \
surf16.o \
surf8.o
# Objects only used in software rendering versions of NQ on the Win32 Platform
NQ_W32_SW_C_OBJS = \
vid_win.o
NQ_W32_SW_ASM_OBJS = \
dosasm.o
# Objects only used in software rendering versions of NQ on the Linux Platform
NQ_LINUX_SW_C_OBJS = \
vid_x.o
NQ_LINUX_SW_AMS_OBJS =
# Objects only used in OpenGL rendering versions of NQ
NQ_GL_C_OBJS = \
drawhulls.o \
gl_draw.o \
gl_mesh.o \
gl_model.o \
gl_refrag.o \
gl_rlight.o \
gl_rmain.o \
gl_rmisc.o \
gl_rsurf.o \
gl_screen.o \
gl_warp.o
NQ_GL_ASM_OBJS =
# Objects only used in OpenGL rendering versions of NQ on the Win32 Platform
NQ_W32_GL_C_OBJS = \
gl_vidnt.o
NQ_W32_GL_ASM_OBJS =
# Objects only used in OpenGL rendering versions of NQ on the Linux Platform
NQ_LINUX_GL_C_OBJS = \
gl_vidlinuxglx.o
NQ_LINUX_GL_ASM_OBJS =
# Misc objects that don't seem to get used...
NQ_OTHER_ASM_OBJS = \
d_copy.o \
sys_dosa.o
NQ_OTHER_C_OBJS =
# =========================================================================== #
# Build the list of object files for each particular target
# (*sigh* - something has to be done about this makefile...)
NQ_W32_SW_OBJS := $(NQ_COMMON_C_OBJS)
NQ_W32_SW_OBJS += $(NQ_SW_C_OBJS)
NQ_W32_SW_OBJS += $(NQ_W32_C_OBJS)
NQ_W32_SW_OBJS += $(NQ_W32_SW_C_OBJS)
NQ_W32_SW_OBJS += winquake.res
ifdef NO_X86_ASM
NQ_W32_SW_OBJS += nonintel.o
else
NQ_W32_SW_OBJS += $(NQ_COMMON_ASM_OBJS)
NQ_W32_SW_OBJS += $(NQ_SW_ASM_OBJS)
NQ_W32_SW_OBJS += $(NQ_W32_ASM_OBJS)
NQ_W32_SW_OBJS += $(NQ_W32_SW_ASM_OBJS)
endif
NQ_W32_GL_OBJS := $(NQ_COMMON_C_OBJS)
NQ_W32_GL_OBJS += $(NQ_GL_C_OBJS)
NQ_W32_GL_OBJS += $(NQ_W32_C_OBJS)
NQ_W32_GL_OBJS += $(NQ_W32_GL_C_OBJS)
NQ_W32_GL_OBJS += winquake.res
ifndef NO_X86_ASM
NQ_W32_GL_OBJS += $(NQ_COMMON_ASM_OBJS)
NQ_W32_GL_OBJS += $(NQ_GL_ASM_OBJS)
NQ_W32_GL_OBJS += $(NQ_W32_ASM_OBJS)
NQ_W32_GL_OBJS += $(NQ_W32_GL_ASM_OBJS)
endif
NQ_LINUX_SW_OBJS := $(NQ_COMMON_C_OBJS)
NQ_LINUX_SW_OBJS += $(NQ_SW_C_OBJS)
NQ_LINUX_SW_OBJS += $(NQ_LINUX_C_OBJS)
NQ_LINUX_SW_OBJS += $(NQ_LINUX_SW_C_OBJS)
ifdef NO_X86_ASM
NQ_LINUX_SW_OBJS += nonintel.o
else
NQ_LINUX_SW_OBJS += $(NQ_COMMON_ASM_OBJS)
NQ_LINUX_SW_OBJS += $(NQ_SW_ASM_OBJS)
NQ_LINUX_SW_OBJS += $(NQ_LINUX_ASM_OBJS)
NQ_LINUX_SW_OBJS += $(NQ_LINUX_SW_ASM_OBJS)
endif
NQ_LINUX_GL_OBJS := $(NQ_COMMON_C_OBJS)
NQ_LINUX_GL_OBJS += $(NQ_GL_C_OBJS)
NQ_LINUX_GL_OBJS += $(NQ_LINUX_C_OBJS)
NQ_LINUX_GL_OBJS += $(NQ_LINUX_GL_C_OBJS)
ifndef NO_X86_ASM
NQ_LINUX_GL_OBJS += $(NQ_COMMON_ASM_OBJS)
NQ_LINUX_GL_OBJS += $(NQ_GL_ASM_OBJS)
NQ_LINUX_GL_OBJS += $(NQ_LINUX_ASM_OBJS)
NQ_LINUX_GL_OBJS += $(NQ_LINUX_GL_ASM_OBJS)
endif
# ------------------------
# Now, the build rules...
# ------------------------
# Win32
nq-w32-sw-objs:
$(MAKE) -C $(NQ_DIR) quake-sw-win32
nq-w32-gl-objs:
$(MAKE) -C $(NQ_DIR) quake-gl-win32
tyr-quake.exe: nq-w32-sw-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $(patsubst %,$(NQ_DIR)/%,$(NQ_W32_SW_OBJS)) -L$(WIN_LIBDIR) -L$(NQ_ST_LIBDIR) $(NQ_W32_SW_LFLAGS)
$(STRIP_CMD) $@
tyr-glquake.exe: nq-w32-gl-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $(patsubst %,$(NQ_DIR)/%,$(NQ_W32_GL_OBJS)) -L$(WIN_LIBDIR) -L$(NQ_ST_LIBDIR) $(NQ_W32_GL_LFLAGS)
$(STRIP_CMD) $@
# Linux
nq-linux-sw-objs:
$(MAKE) -C $(NQ_DIR) quake-sw-linux
nq-linux-gl-objs:
$(MAKE) -C $(NQ_DIR) quake-gl-linux
tyr-quake: nq-linux-sw-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o tyr-quake $(patsubst %,$(NQ_DIR)/%,$(NQ_LINUX_SW_OBJS)) -L$(LINUX_X11_LIBDIR) $(NQ_LINUX_SW_LFLAGS)
$(STRIP_CMD) $@
tyr-glquake: nq-linux-gl-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o tyr-glquake $(patsubst %,$(NQ_DIR)/%,$(NQ_LINUX_GL_OBJS)) -L$(LINUX_X11_LIBDIR) $(NQ_LINUX_GL_LFLAGS)
$(STRIP_CMD) $@
# ----------------------------------------------------------------------------
# QuakeWorld (QW) - Client
# ----------------------------------------------------------------------------
QW_SV_SHARED_C_OBJS = \
cmd.o \
common.o \
crc.o \
cvar.o \
mathlib.o \
md4.o \
net_chan.o \
pmove.o \
pmovetst.o \
rb_tree.o \
shell.o \
zone.o
QW_COMMON_C_OBJS = \
$(QW_SV_SHARED_C_OBJS) \
cl_cam.o \
cl_demo.o \
cl_ents.o \
cl_input.o \
cl_main.o \
cl_parse.o \
cl_pred.o \
cl_tent.o \
console.o \
keys.o \
menu.o \
r_part.o \
sbar.o \
skin.o \
snd_dma.o \
snd_mem.o \
snd_mix.o \
view.o \
wad.o
QW_COMMON_ASM_OBJS = \
math.o \
snd_mixa.o
QW_W32_C_OBJS = \
cd_win.o \
in_win.o \
net_wins.o \
snd_win.o \
sys_win.o
QW_W32_ASM_OBJS = \
sys_wina.o
QW_LINUX_SV_SHARED_C_OBJS = \
net_udp.o
QW_LINUX_C_OBJS = \
$(QW_LINUX_SV_SHARED_C_OBJS) \
cd_linux.o \
snd_linux.o \
sys_linux.o \
in_x11.o \
x11_core.o
QW_LINUX_ASM_OBJS = \
sys_dosa.o
QW_SW_C_OBJS = \
d_edge.o \
d_fill.o \
d_init.o \
d_modech.o \
d_part.o \
d_polyse.o \
d_scan.o \
d_sky.o \
d_sprite.o \
d_surf.o \
d_vars.o \
d_zpoint.o \
draw.o \
model.o \
r_aclip.o \
r_alias.o \
r_bsp.o \
r_draw.o \
r_edge.o \
r_efrag.o \
r_light.o \
r_main.o \
r_misc.o \
r_sky.o \
r_sprite.o \
r_surf.o \
r_vars.o \
screen.o
QW_SW_ASM_OBJS = \
d_draw.o \
d_draw16.o \
d_parta.o \
d_polysa.o \
d_scana.o \
d_spr8.o \
d_varsa.o \
r_aclipa.o \
r_aliasa.o \
r_drawa.o \
r_edgea.o \
r_varsa.o \
surf16.o \
surf8.o
QW_W32_SW_C_OBJS = \
vid_win.o
QW_W32_SW_ASM_OBJS =
QW_LINUX_SW_C_OBJS = \
vid_x.o
QW_LINUX_SW_ASM_OBJS =
QW_GL_C_OBJS = \
drawhulls.o \
gl_draw.o \
gl_mesh.o \
gl_model.o \
gl_ngraph.o \
gl_refrag.o \
gl_rlight.o \
gl_rmain.o \
gl_rmisc.o \
gl_rsurf.o \
gl_screen.o \
gl_warp.o
QW_GL_ASM_OBJS =
QW_W32_GL_C_OBJS = \
gl_vidnt.o
QW_W32_GL_ASM_OBJS =
QW_LINUX_GL_C_OBJS = \
gl_vidlinuxglx.o
QW_LINUX_GL_ASM_OBJS =
# ========================================================================== #
# Build the list of object files for each particular target
# (*sigh* - something has to be done about this makefile...)
QW_W32_SW_OBJS := $(QW_COMMON_C_OBJS)
QW_W32_SW_OBJS += $(QW_SW_C_OBJS)
QW_W32_SW_OBJS += $(QW_W32_C_OBJS)
QW_W32_SW_OBJS += $(QW_W32_SW_C_OBJS)
QW_W32_SW_OBJS += winquake.res
ifdef NO_X86_ASM
QW_W32_SW_OBJS += nonintel.o
else
QW_W32_SW_OBJS += $(QW_COMMON_ASM_OBJS)
QW_W32_SW_OBJS += $(QW_SW_ASM_OBJS)
QW_W32_SW_OBJS += $(QW_W32_ASM_OBJS)
QW_W32_SW_OBJS += $(QW_W32_SW_ASM_OBJS)
endif
QW_W32_GL_OBJS := $(QW_COMMON_C_OBJS)
QW_W32_GL_OBJS += $(QW_GL_C_OBJS)
QW_W32_GL_OBJS += $(QW_W32_C_OBJS)
QW_W32_GL_OBJS += $(QW_W32_GL_C_OBJS)
QW_W32_GL_OBJS += winquake.res
ifndef NO_X86_ASM
QW_W32_GL_OBJS += $(QW_COMMON_ASM_OBJS)
QW_W32_GL_OBJS += $(QW_GL_ASM_OBJS)
QW_W32_GL_OBJS += $(QW_W32_ASM_OBJS)
QW_W32_GL_OBJS += $(QW_W32_GL_ASM_OBJS)
endif
QW_LINUX_SW_OBJS := $(QW_COMMON_C_OBJS)
QW_LINUX_SW_OBJS += $(QW_SW_C_OBJS)
QW_LINUX_SW_OBJS += $(QW_LINUX_C_OBJS)
QW_LINUX_SW_OBJS += $(QW_LINUX_SW_C_OBJS)
ifdef NO_X86_ASM
QW_LINUX_SW_OBJS += nonintel.o
else
QW_LINUX_SW_OBJS += $(QW_COMMON_ASM_OBJS)
QW_LINUX_SW_OBJS += $(QW_SW_ASM_OBJS)
QW_LINUX_SW_OBJS += $(QW_LINUX_ASM_OBJS)
QW_LINUX_SW_OBJS += $(QW_LINUX_SW_ASM_OBJS)
endif
QW_LINUX_GL_OBJS := $(QW_COMMON_C_OBJS)
QW_LINUX_GL_OBJS += $(QW_GL_C_OBJS)
QW_LINUX_GL_OBJS += $(QW_LINUX_C_OBJS)
QW_LINUX_GL_OBJS += $(QW_LINUX_GL_C_OBJS)
ifndef NO_X86_ASM
QW_LINUX_GL_OBJS += $(QW_COMMON_ASM_OBJS)
QW_LINUX_GL_OBJS += $(QW_GL_ASM_OBJS)
QW_LINUX_GL_OBJS += $(QW_LINUX_ASM_OBJS)
QW_LINUX_GL_OBJS += $(QW_LINUX_GL_ASM_OBJS)
endif
# ---------
# QW Libs
# ---------
QW_W32_COMMON_LIBS = wsock32 dxguid winmm
QW_W32_SW_LIBS = mgllt
QW_W32_GL_LIBS = opengl32 comctl32
QW_LINUX_COMMON_LIBS = m X11 Xext Xxf86dga Xxf86vm
QW_LINUX_GL_LIBS = GL
QW_W32_SW_LFLAGS = -mwindows $(patsubst %,-l%,$(QW_W32_SW_LIBS) $(QW_W32_COMMON_LIBS))
QW_W32_GL_LFLAGS = -mwindows $(patsubst %,-l%,$(QW_W32_GL_LIBS) $(QW_W32_COMMON_LIBS))
QW_LINUX_SW_LFLAGS = $(patsubst %,-l%,$(QW_LINUX_COMMON_LIBS))
QW_LINUX_GL_LFLAGS = $(patsubst %,-l%,$(QW_LINUX_COMMON_LIBS) $(QW_LINUX_GL_LIBS))
# ---------------------
# build rules
# --------------------
# Win32
qw-w32-sw-objs:
$(MAKE) -C $(QW_DIR)/client qwcl-sw-win32
qw-w32-gl-objs:
$(MAKE) -C $(QW_DIR)/client qwcl-gl-win32
tyr-qwcl.exe: qw-w32-sw-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o tyr-qwcl.exe $(patsubst %,$(QW_DIR)/client/%,$(QW_W32_SW_OBJS)) -L$(WIN_LIBDIR) -L$(QW_ST_LIBDIR) $(QW_W32_SW_LFLAGS)
$(STRIP_CMD) $@
tyr-glqwcl.exe: qw-w32-gl-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o tyr-glqwcl.exe $(patsubst %,$(QW_DIR)/client/%,$(QW_W32_GL_OBJS)) -L$(WIN_LIBDIR) $(QW_W32_GL_LFLAGS)
$(STRIP_CMD) $@
# Linux
qw-linux-sw-objs:
$(MAKE) -C $(QW_DIR)/client qwcl-sw-linux
qw-linux-gl-objs:
$(MAKE) -C $(QW_DIR)/client qwcl-gl-linux
tyr-qwcl: qw-linux-sw-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o tyr-qwcl $(patsubst %,$(QW_DIR)/client/%,$(QW_LINUX_SW_OBJS)) -L$(LINUX_X11_LIBDIR) $(QW_LINUX_SW_LFLAGS)
$(STRIP_CMD) $@
tyr-glqwcl: qw-linux-gl-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o tyr-glqwcl $(patsubst %,$(QW_DIR)/client/%,$(QW_LINUX_GL_OBJS)) -L$(LINUX_X11_LIBDIR) $(QW_LINUX_GL_LFLAGS)
$(STRIP_CMD) $@
UNUSED_OBJS = cd_audio.o
# --------------------------------------------------------------------------
# QuakeWorld (QW) - Server
# --------------------------------------------------------------------------
QWSV_SHARED_OBJS = \
cmd.o \
common.o \
crc.o \
cvar.o \
mathlib.o \
md4.o \
net_chan.o \
pmove.o \
pmovetst.o \
rb_tree.o \
shell.o \
zone.o
QWSV_W32_SHARED_OBJS = \
net_wins.o
QWSV_LINUX_SHARED_OBJS = \
net_udp.o
QWSV_ONLY_OBJS = \
model.o \
pr_cmds.o \
pr_edict.o \
pr_exec.o \
sv_ccmds.o \
sv_ents.o \
sv_init.o \
sv_main.o \
sv_move.o \
sv_nchan.o \
sv_phys.o \
sv_send.o \
sv_user.o \
world.o
QWSV_W32_ONLY_OBJS = \
sys_win.o
QWSV_LINUX_ONLY_OBJS = \
sys_unix.o
QWSV_W32_OBJS = \
$(QWSV_SHARED_OBJS) \
$(QWSV_W32_SHARED_OBJS) \
$(QWSV_ONLY_OBJS) \
$(QWSV_W32_ONLY_OBJS)
QWSV_LINUX_OBJS = \
$(QWSV_SHARED_OBJS) \
$(QWSV_LINUX_SHARED_OBJS) \
$(QWSV_ONLY_OBJS) \
$(QWSV_LINUX_ONLY_OBJS)
# ----------------
# QWSV Libs
# ----------------
QWSV_W32_LIBS = wsock32 winmm
QWSV_W32_LFLAGS = -mconsole $(patsubst %,-l%,$(QWSV_W32_LIBS))
QWSV_LINUX_LIBS = m
QWSV_LINUX_LFLAGS = $(patsubst %,-l%,$(QWSV_LINUX_LIBS))
# -------------
# Build rules
# -------------
# Win32
qwsv-w32-objs:
$(MAKE) -C $(QW_DIR)/server qwsv-win32
tyr-qwsv.exe: qwsv-w32-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o tyr-qwsv.exe $(patsubst %,$(QW_DIR)/server/%,$(QWSV_W32_OBJS)) -L$(WIN_LIBDIR) $(QWSV_W32_LFLAGS)
$(STRIP_CMD) $@
# Linux
qwsv-linux-objs:
$(MAKE) -C $(QW_DIR)/server qwsv-linux
tyr-qwsv: qwsv-linux-objs
$(CC) $(CPPFLAGS) $(CFLAGS) -o tyr-qwsv $(patsubst %,$(QW_DIR)/server/%,$(QWSV_LINUX_OBJS)) $(QWSV_LINUX_LFLAGS)
$(STRIP_CMD) $@
# ----------------------------------------------------------------------------
# Very basic clean target (can't use xargs on MSYS)
# ----------------------------------------------------------------------------
# Main clean function...
clean:
@rm -f $(shell find . \( \
-name '*~' -o -name '#*#' -o -name '*.o' -o -name '*.res' \
\) -print)

69
NQ/3dfx.txt Normal file
View File

@ -0,0 +1,69 @@
GLQuake Drivers
Graphics Subsystem: Voodoo Graphics or Voodoo Rush
Copyright ( 1997 3Dfx Interactive, Inc. )
All Rights Reserved
3Dfx Interactive, Inc.
www: www.3dfx.com
news: news.3dfx.com
-----------------------------------------------------------------------
NOTE: GLQuake requires DirectX support DirectSound. DirectX can be
installed from the media provided with your Voodoo Based 3D Accelerator.
Glide 2.31 or HIGHER runtime drivers *MUST* be installed to use this
GLQuake driver. Please download these drivers from your board
manufacturer OR unsupported drivers from http://www.3dfx.com
-----------------------------------------------------------------------
Release Notes for GLQuake's mini-GL driver
What's in the distribution?
---------------------------
This distribution contains GLQuake Drivers for Voodoo Based 3D
Accelerators. These drivers were tested on the following boards:
Voodoo Graphics:
- Quantum 3D Obsidian
- Diamond Monster 3D
- Orchid Righteous 3D
- Deltron Realvision Flash 3D
- Guillemot MaxiGamer
- Skywell Magic 3D
Voodoo Rush:
- Hercules Stringray 128-3D
- Intergraph Intense 3D Voodoo
- Jazz Multimedia Adrenaline Rush
NOTE: The enclosed drivers are not meant to replace any Direct3D or
Glide drivers provided by your Voodoo Graphics card manufacturer.
Please obtain supported drivers from your board manufacturer.
OEMSR2 and NT users: Do NOT replace OPENGL32.DLL located in your
Windows\SYSTEM directory.
Requirements
------------
- Voodoo Graphics or Voodoo Rush Based 3D Accelerator
- Windows 95 (Windows NT is supported for Voodoo Rush)
- A PC with a Pentium 90 or higher CPU
- 16MB of RAM
- 2D Video card set at 16 bit color
Support and Frequently Asked Questions
--------------------------------------
GLQuake is currently unsupported. You may however find answers to
questions on various Quake dedicated websites. 3Dfx provides a GLQuake
newsgroup on news.3dfx.com (Newsgroup name is 3dfx.games.glquake ) to
discuss GLQuake with other users. 3Dfx also provides a regularly
updated GLQuake FAQ at: http://www.3dfx.com/game_dev/quake_faq.html
Voodoo Graphics and Voodoo Rush are trademarks of 3Dfx Interactive, Inc.
All other trademarks are the property of their respective owners.

34
NQ/Makefile Normal file
View File

@ -0,0 +1,34 @@
# ------------
# NQ Makefile - to be called from the main makefile
# ------------
# Not quite ready for buildirs, etc.
# More seperation required:
# - Core engine
# - Renderer
# - System
.PHONY: default \
quake-sw-win32 quake-gl-win32 quake-sw-linux quake-gl-linux
# Rules to compile stuff from the common dir...
%.o: ../common/%.S
$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $^
%.o: ../common/%.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $^
# Rule to make the resource file
%.res: %.rc
windres -i $^ -O coff -o $@
# Don't build anything by default
default:
quake-sw-win32: $(NQ_W32_SW_OBJS)
quake-gl-win32: $(NQ_W32_GL_OBJS)
quake-sw-linux: $(NQ_LINUX_SW_OBJS)
quake-gl-linux: $(NQ_LINUX_GL_OBJS)

95
NQ/chase.c Normal file
View File

@ -0,0 +1,95 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// chase.c -- chase camera code
#include "quakedef.h"
#include "world.h" /* trace_t */
cvar_t chase_back = { "chase_back", "100" };
cvar_t chase_up = { "chase_up", "16" };
cvar_t chase_right = { "chase_right", "0" };
cvar_t chase_active = { "chase_active", "0" };
vec3_t chase_pos;
vec3_t chase_angles;
vec3_t chase_dest;
vec3_t chase_dest_angles;
void
Chase_Init(void)
{
Cvar_RegisterVariable(&chase_back);
Cvar_RegisterVariable(&chase_up);
Cvar_RegisterVariable(&chase_right);
Cvar_RegisterVariable(&chase_active);
}
void
Chase_Reset(void)
{
// for respawning and teleporting
// start position 12 units behind head
}
void
TraceLine(vec3_t start, vec3_t end, vec3_t impact)
{
trace_t trace;
memset(&trace, 0, sizeof(trace));
SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
VectorCopy(trace.endpos, impact);
}
void
Chase_Update(void)
{
int i;
float dist;
vec3_t forward, up, right;
vec3_t dest, stop;
// if can't see player, reset
AngleVectors(cl.viewangles, forward, right, up);
// calc exact destination
for (i = 0; i < 3; i++)
chase_dest[i] = r_refdef.vieworg[i]
- forward[i] * chase_back.value - right[i] * chase_right.value;
chase_dest[2] = r_refdef.vieworg[2] + chase_up.value;
// find the spot the player is looking at
VectorMA(r_refdef.vieworg, 4096, forward, dest);
TraceLine(r_refdef.vieworg, dest, stop);
// calculate pitch to look at the same spot from camera
VectorSubtract(stop, r_refdef.vieworg, stop);
dist = DotProduct(stop, forward);
if (dist < 1)
dist = 1;
r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180;
// move towards destination
VectorCopy(chase_dest, r_refdef.vieworg);
}

365
NQ/cl_demo.c Normal file
View File

@ -0,0 +1,365 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "client.h"
#include "cmd.h"
#include "console.h"
#include "host.h"
#include "net.h"
#include "protocol.h"
#include "quakedef.h"
#include "sys.h"
static void CL_FinishTimeDemo(void);
/*
==============================================================================
DEMO CODE
When a demo is playing back, all NET_SendMessages are skipped, and
NET_GetMessages are read from the demo file.
Whenever cl.time gets past the last received message, another message is
read from the demo file.
==============================================================================
*/
/*
==============
CL_StopPlayback
Called when a demo file runs out, or the user starts a game
==============
*/
void
CL_StopPlayback(void)
{
if (!cls.demoplayback)
return;
fclose(cls.demofile);
cls.demoplayback = false;
cls.demofile = NULL;
cls.state = ca_disconnected;
if (cls.timedemo)
CL_FinishTimeDemo();
}
/*
====================
CL_WriteDemoMessage
Dumps the current net message, prefixed by the length and view angles
====================
*/
static void
CL_WriteDemoMessage(void)
{
int len;
int i;
float f;
len = LittleLong(net_message.cursize);
fwrite(&len, 4, 1, cls.demofile);
for (i = 0; i < 3; i++) {
f = LittleFloat(cl.viewangles[i]);
fwrite(&f, 4, 1, cls.demofile);
}
fwrite(net_message.data, net_message.cursize, 1, cls.demofile);
fflush(cls.demofile);
}
/*
====================
CL_GetMessage
Handles recording and playback of demos, on top of NET_ code
====================
*/
int
CL_GetMessage(void)
{
int r, i;
float f;
if (cls.demoplayback) {
// decide if it is time to grab the next message
// allways grab until fully connected
if (cls.state == ca_active) {
if (cls.timedemo) {
if (host_framecount == cls.td_lastframe)
return 0; // allready read this frame's message
cls.td_lastframe = host_framecount;
// if this is the second frame, grab the real td_starttime
// so the bogus time on the first frame doesn't count
if (host_framecount == cls.td_startframe + 1)
cls.td_starttime = realtime;
} else if (cl.time <= cl.mtime[0]) {
// don't need another message yet
return 0;
}
}
// get the next message
fread(&net_message.cursize, 4, 1, cls.demofile);
VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
for (i = 0; i < 3; i++) {
r = fread(&f, 4, 1, cls.demofile);
cl.mviewangles[0][i] = LittleFloat(f);
}
net_message.cursize = LittleLong(net_message.cursize);
if (net_message.cursize > MAX_MSGLEN)
Sys_Error("Demo message > MAX_MSGLEN");
r = fread(net_message.data, net_message.cursize, 1, cls.demofile);
if (r != 1) {
CL_StopPlayback();
return 0;
}
return 1;
}
while (1) {
r = NET_GetMessage(cls.netcon);
if (r != 1 && r != 2)
return r;
// discard nop keepalive message
if (net_message.cursize == 1 && net_message.data[0] == svc_nop)
Con_Printf("<-- server to client keepalive\n");
else
break;
}
if (cls.demorecording)
CL_WriteDemoMessage();
return r;
}
/*
====================
CL_Stop_f
stop recording a demo
====================
*/
void
CL_Stop_f(void)
{
if (cmd_source != src_command)
return;
if (!cls.demorecording) {
Con_Printf("Not recording a demo.\n");
return;
}
// write a disconnect message to the demo file
SZ_Clear(&net_message);
MSG_WriteByte(&net_message, svc_disconnect);
CL_WriteDemoMessage();
// finish up
fclose(cls.demofile);
cls.demofile = NULL;
cls.demorecording = false;
Con_Printf("Completed demo\n");
}
/*
====================
CL_Record_f
record <demoname> <map> [cd track]
====================
*/
void
CL_Record_f(void)
{
int c;
char name[MAX_OSPATH];
int track;
if (cmd_source != src_command)
return;
c = Cmd_Argc();
if (c != 2 && c != 3 && c != 4) {
Con_Printf("record <demoname> [<map> [cd track]]\n");
return;
}
if (strstr(Cmd_Argv(1), "..")) {
Con_Printf("Relative pathnames are not allowed.\n");
return;
}
if (c == 2 && cls.state >= ca_connected) {
Con_Printf("Can not record - already connected to server\n"
"Client demo recording must be started before"
" connecting\n");
return;
}
// write the forced cd track number, or -1
if (c == 4) {
track = atoi(Cmd_Argv(3));
Con_Printf("Forcing CD track to %i\n", cls.forcetrack);
} else
track = -1;
sprintf(name, "%s/%s", com_gamedir, Cmd_Argv(1));
//
// start the map up
//
if (c > 2)
Cmd_ExecuteString(va("map %s", Cmd_Argv(2)), src_command);
//
// open the demo file
//
COM_DefaultExtension(name, ".dem");
Con_Printf("recording to %s.\n", name);
cls.demofile = fopen(name, "wb");
if (!cls.demofile) {
Con_Printf("ERROR: couldn't open.\n");
return;
}
cls.forcetrack = track;
fprintf(cls.demofile, "%i\n", cls.forcetrack);
cls.demorecording = true;
}
/*
====================
CL_PlayDemo_f
play [demoname]
====================
*/
void
CL_PlayDemo_f(void)
{
char name[256];
int c;
qboolean neg = false;
if (cmd_source != src_command)
return;
if (Cmd_Argc() != 2) {
Con_Printf("play <demoname> : plays a demo\n");
return;
}
//
// disconnect from server
//
CL_Disconnect();
//
// open the demo file
//
strcpy(name, Cmd_Argv(1));
COM_DefaultExtension(name, ".dem");
Con_Printf("Playing demo from %s.\n", name);
COM_FOpenFile(name, &cls.demofile);
if (!cls.demofile) {
Con_Printf("ERROR: couldn't open.\n");
cls.demonum = -1; // stop demo loop
return;
}
cls.demoplayback = true;
cls.state = ca_connected;
cls.forcetrack = 0;
while ((c = getc(cls.demofile)) != '\n')
if (c == '-')
neg = true;
else
cls.forcetrack = cls.forcetrack * 10 + (c - '0');
if (neg)
cls.forcetrack = -cls.forcetrack;
// ZOID, fscanf is evil
// fscanf (cls.demofile, "%i\n", &cls.forcetrack);
}
/*
====================
CL_FinishTimeDemo
====================
*/
static void
CL_FinishTimeDemo(void)
{
int frames;
float time;
cls.timedemo = false;
// the first frame didn't count
frames = (host_framecount - cls.td_startframe) - 1;
time = realtime - cls.td_starttime;
if (!time)
time = 1;
Con_Printf("%i frames %5.1f seconds %5.1f fps\n", frames, time,
frames / time);
}
/*
====================
CL_TimeDemo_f
timedemo [demoname]
====================
*/
void
CL_TimeDemo_f(void)
{
if (cmd_source != src_command)
return;
if (Cmd_Argc() != 2) {
Con_Printf("timedemo <demoname> : gets demo speeds\n");
return;
}
CL_PlayDemo_f();
// cls.td_starttime will be grabbed at the second frame of the demo, so
// all the loading time doesn't get counted
cls.timedemo = true;
cls.td_startframe = host_framecount;
cls.td_lastframe = -1; // get a new message this frame
}

616
NQ/cl_input.c Normal file
View File

@ -0,0 +1,616 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cl.input.c -- builds an intended movement command to send to the server
// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
// rights reserved.
#include "quakedef.h"
#include "host.h"
#include "client.h"
#include "cmd.h"
#include "console.h"
#include "net.h"
#include "protocol.h"
/*
===============================================================================
KEY BUTTONS
Continuous button event tracking is complicated by the fact that two different
input sources (say, mouse button 1 and the control key) can both press the
same button, but the button should only be released when both of the
pressing key have been released.
When a key event issues a button command (+forward, +attack, etc), it appends
its key number as a parameter to the command so it can be matched up with
the release.
state bit 0 is the current state of the key
state bit 1 is edge triggered on the up to down transition
state bit 2 is edge triggered on the down to up transition
===============================================================================
*/
kbutton_t in_mlook, in_klook;
kbutton_t in_left, in_right, in_forward, in_back;
kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack;
kbutton_t in_up, in_down;
int in_impulse;
void
KeyDown(kbutton_t *b)
{
int k;
char *c;
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
k = -1; // typed manually at the console for continuous down
if (k == b->down[0] || k == b->down[1])
return; // repeating key
if (!b->down[0])
b->down[0] = k;
else if (!b->down[1])
b->down[1] = k;
else {
Con_Printf("Three keys down for a button!\n");
return;
}
if (b->state & 1)
return; // still down
b->state |= 1 + 2; // down + impulse down
}
void
KeyUp(kbutton_t *b)
{
int k;
char *c;
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else { // typed manually at the console, assume for unsticking, so clear all
b->down[0] = b->down[1] = 0;
b->state = 4; // impulse up
return;
}
if (b->down[0] == k)
b->down[0] = 0;
else if (b->down[1] == k)
b->down[1] = 0;
else
return; // key up without coresponding down (menu pass through)
if (b->down[0] || b->down[1])
return; // some other key is still holding it down
if (!(b->state & 1))
return; // still up (this should not happen)
b->state &= ~1; // now up
b->state |= 4; // impulse up
}
void
IN_KLookDown(void)
{
KeyDown(&in_klook);
}
void
IN_KLookUp(void)
{
KeyUp(&in_klook);
}
void
IN_MLookDown(void)
{
KeyDown(&in_mlook);
}
void
IN_MLookUp(void)
{
KeyUp(&in_mlook);
if (!(in_mlook.state & 1) && lookspring.value)
V_StartPitchDrift();
}
void
IN_UpDown(void)
{
KeyDown(&in_up);
}
void
IN_UpUp(void)
{
KeyUp(&in_up);
}
void
IN_DownDown(void)
{
KeyDown(&in_down);
}
void
IN_DownUp(void)
{
KeyUp(&in_down);
}
void
IN_LeftDown(void)
{
KeyDown(&in_left);
}
void
IN_LeftUp(void)
{
KeyUp(&in_left);
}
void
IN_RightDown(void)
{
KeyDown(&in_right);
}
void
IN_RightUp(void)
{
KeyUp(&in_right);
}
void
IN_ForwardDown(void)
{
KeyDown(&in_forward);
}
void
IN_ForwardUp(void)
{
KeyUp(&in_forward);
}
void
IN_BackDown(void)
{
KeyDown(&in_back);
}
void
IN_BackUp(void)
{
KeyUp(&in_back);
}
void
IN_LookupDown(void)
{
KeyDown(&in_lookup);
}
void
IN_LookupUp(void)
{
KeyUp(&in_lookup);
}
void
IN_LookdownDown(void)
{
KeyDown(&in_lookdown);
}
void
IN_LookdownUp(void)
{
KeyUp(&in_lookdown);
}
void
IN_MoveleftDown(void)
{
KeyDown(&in_moveleft);
}
void
IN_MoveleftUp(void)
{
KeyUp(&in_moveleft);
}
void
IN_MoverightDown(void)
{
KeyDown(&in_moveright);
}
void
IN_MoverightUp(void)
{
KeyUp(&in_moveright);
}
void
IN_SpeedDown(void)
{
KeyDown(&in_speed);
}
void
IN_SpeedUp(void)
{
KeyUp(&in_speed);
}
void
IN_StrafeDown(void)
{
KeyDown(&in_strafe);
}
void
IN_StrafeUp(void)
{
KeyUp(&in_strafe);
}
void
IN_AttackDown(void)
{
KeyDown(&in_attack);
}
void
IN_AttackUp(void)
{
KeyUp(&in_attack);
}
void
IN_UseDown(void)
{
KeyDown(&in_use);
}
void
IN_UseUp(void)
{
KeyUp(&in_use);
}
void
IN_JumpDown(void)
{
KeyDown(&in_jump);
}
void
IN_JumpUp(void)
{
KeyUp(&in_jump);
}
void
IN_Impulse(void)
{
in_impulse = Q_atoi(Cmd_Argv(1));
}
/*
===============
CL_KeyState
Returns 0.25 if a key was pressed and released during the frame,
0.5 if it was pressed and held
0 if held then released, and
1.0 if held for the entire time
===============
*/
float
CL_KeyState(kbutton_t *key)
{
float val;
qboolean impulsedown, impulseup, down;
impulsedown = key->state & 2;
impulseup = key->state & 4;
down = key->state & 1;
val = 0;
if (impulsedown && !impulseup) {
if (down)
val = 0.5; // pressed and held this frame
else
val = 0; // I_Error ();
}
// FIXME: both alternatives zero?
if (impulseup && !impulsedown) {
if (down)
val = 0; // I_Error ();
else
val = 0; // released this frame
}
if (!impulsedown && !impulseup) {
if (down)
val = 1.0; // held the entire frame
else
val = 0; // up the entire frame
}
if (impulsedown && impulseup) {
if (down)
val = 0.75; // released and re-pressed this frame
else
val = 0.25; // pressed and released this frame
}
key->state &= 1; // clear impulses
return val;
}
//==========================================================================
cvar_t cl_upspeed = { "cl_upspeed", "200" };
cvar_t cl_forwardspeed = { "cl_forwardspeed", "200", true };
cvar_t cl_backspeed = { "cl_backspeed", "200", true };
cvar_t cl_sidespeed = { "cl_sidespeed", "350" };
cvar_t cl_movespeedkey = { "cl_movespeedkey", "2.0" };
cvar_t cl_yawspeed = { "cl_yawspeed", "140" };
cvar_t cl_pitchspeed = { "cl_pitchspeed", "150" };
cvar_t cl_anglespeedkey = { "cl_anglespeedkey", "1.5" };
/*
================
CL_AdjustAngles
Moves the local angle positions
================
*/
void
CL_AdjustAngles(void)
{
float speed;
float up, down;
if (in_speed.state & 1)
speed = host_frametime * cl_anglespeedkey.value;
else
speed = host_frametime;
if (!(in_strafe.state & 1)) {
cl.viewangles[YAW] -=
speed * cl_yawspeed.value * CL_KeyState(&in_right);
cl.viewangles[YAW] +=
speed * cl_yawspeed.value * CL_KeyState(&in_left);
cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]);
}
if (in_klook.state & 1) {
V_StopPitchDrift();
cl.viewangles[PITCH] -=
speed * cl_pitchspeed.value * CL_KeyState(&in_forward);
cl.viewangles[PITCH] +=
speed * cl_pitchspeed.value * CL_KeyState(&in_back);
}
up = CL_KeyState(&in_lookup);
down = CL_KeyState(&in_lookdown);
cl.viewangles[PITCH] -= speed * cl_pitchspeed.value * up;
cl.viewangles[PITCH] += speed * cl_pitchspeed.value * down;
if (up || down)
V_StopPitchDrift();
if (cl.viewangles[PITCH] > 80)
cl.viewangles[PITCH] = 80;
if (cl.viewangles[PITCH] < -70)
cl.viewangles[PITCH] = -70;
if (cl.viewangles[ROLL] > 50)
cl.viewangles[ROLL] = 50;
if (cl.viewangles[ROLL] < -50)
cl.viewangles[ROLL] = -50;
}
/*
================
CL_BaseMove
Send the intended movement message to the server
================
*/
void
CL_BaseMove(usercmd_t *cmd)
{
if (cls.state != ca_active)
return;
CL_AdjustAngles();
memset(cmd, 0, sizeof(*cmd));
if (in_strafe.state & 1) {
cmd->sidemove += cl_sidespeed.value * CL_KeyState(&in_right);
cmd->sidemove -= cl_sidespeed.value * CL_KeyState(&in_left);
}
cmd->sidemove += cl_sidespeed.value * CL_KeyState(&in_moveright);
cmd->sidemove -= cl_sidespeed.value * CL_KeyState(&in_moveleft);
cmd->upmove += cl_upspeed.value * CL_KeyState(&in_up);
cmd->upmove -= cl_upspeed.value * CL_KeyState(&in_down);
if (!(in_klook.state & 1)) {
cmd->forwardmove += cl_forwardspeed.value * CL_KeyState(&in_forward);
cmd->forwardmove -= cl_backspeed.value * CL_KeyState(&in_back);
}
//
// adjust for speed key
//
if (in_speed.state & 1) {
cmd->forwardmove *= cl_movespeedkey.value;
cmd->sidemove *= cl_movespeedkey.value;
cmd->upmove *= cl_movespeedkey.value;
}
}
/*
==============
CL_SendMove
==============
*/
void
CL_SendMove(usercmd_t *cmd)
{
int i;
int bits;
sizebuf_t buf;
byte data[128];
buf.maxsize = 128;
buf.cursize = 0;
buf.data = data;
cl.cmd = *cmd;
/*
* send the movement message
*/
MSG_WriteByte(&buf, clc_move);
MSG_WriteFloat(&buf, cl.mtime[0]); /* so server can get ping times */
for (i = 0; i < 3; i++)
MSG_WriteAngle(&buf, cl.viewangles[i]);
MSG_WriteShort(&buf, cmd->forwardmove);
MSG_WriteShort(&buf, cmd->sidemove);
MSG_WriteShort(&buf, cmd->upmove);
/*
* send button bits
*/
bits = 0;
if (in_attack.state & 3)
bits |= 1;
in_attack.state &= ~2;
if (in_jump.state & 3)
bits |= 2;
in_jump.state &= ~2;
MSG_WriteByte(&buf, bits);
MSG_WriteByte(&buf, in_impulse);
in_impulse = 0;
if (cls.demoplayback)
return;
/*
* deliver the message
*/
/*
* allways dump the first two message, because it may contain leftover
* inputs from the last level
*/
if (++cl.movemessages <= 2)
return;
if (NET_SendUnreliableMessage(cls.netcon, &buf) == -1) {
Con_Printf("CL_SendMove: lost server connection\n");
CL_Disconnect();
}
}
/*
============
CL_InitInput
============
*/
void
CL_InitInput(void)
{
Cmd_AddCommand("+moveup", IN_UpDown);
Cmd_AddCommand("-moveup", IN_UpUp);
Cmd_AddCommand("+movedown", IN_DownDown);
Cmd_AddCommand("-movedown", IN_DownUp);
Cmd_AddCommand("+left", IN_LeftDown);
Cmd_AddCommand("-left", IN_LeftUp);
Cmd_AddCommand("+right", IN_RightDown);
Cmd_AddCommand("-right", IN_RightUp);
Cmd_AddCommand("+forward", IN_ForwardDown);
Cmd_AddCommand("-forward", IN_ForwardUp);
Cmd_AddCommand("+back", IN_BackDown);
Cmd_AddCommand("-back", IN_BackUp);
Cmd_AddCommand("+lookup", IN_LookupDown);
Cmd_AddCommand("-lookup", IN_LookupUp);
Cmd_AddCommand("+lookdown", IN_LookdownDown);
Cmd_AddCommand("-lookdown", IN_LookdownUp);
Cmd_AddCommand("+strafe", IN_StrafeDown);
Cmd_AddCommand("-strafe", IN_StrafeUp);
Cmd_AddCommand("+moveleft", IN_MoveleftDown);
Cmd_AddCommand("-moveleft", IN_MoveleftUp);
Cmd_AddCommand("+moveright", IN_MoverightDown);
Cmd_AddCommand("-moveright", IN_MoverightUp);
Cmd_AddCommand("+speed", IN_SpeedDown);
Cmd_AddCommand("-speed", IN_SpeedUp);
Cmd_AddCommand("+attack", IN_AttackDown);
Cmd_AddCommand("-attack", IN_AttackUp);
Cmd_AddCommand("+use", IN_UseDown);
Cmd_AddCommand("-use", IN_UseUp);
Cmd_AddCommand("+jump", IN_JumpDown);
Cmd_AddCommand("-jump", IN_JumpUp);
Cmd_AddCommand("impulse", IN_Impulse);
Cmd_AddCommand("+klook", IN_KLookDown);
Cmd_AddCommand("-klook", IN_KLookUp);
Cmd_AddCommand("+mlook", IN_MLookDown);
Cmd_AddCommand("-mlook", IN_MLookUp);
}

727
NQ/cl_main.c Normal file
View File

@ -0,0 +1,727 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cl_main.c -- client main loop
#include "quakedef.h"
#include "host.h"
#include "cvar.h"
#include "client.h"
#include "server.h"
#include "sound.h"
#include "console.h"
#include "net.h"
#include "protocol.h"
#include "screen.h"
#include "cmd.h"
#include "render.h"
#include "input.h"
#ifdef GLQUAKE
# include "gl_model.h"
#else
# include "model.h"
#endif
// we need to declare some mouse variables here, because the menu system
// references them even when on a unix system.
// these two are not intended to be set directly
cvar_t cl_name = { "_cl_name", "player", true };
cvar_t cl_color = { "_cl_color", "0", true };
cvar_t cl_shownet = { "cl_shownet", "0" }; // can be 0, 1, or 2
cvar_t cl_nolerp = { "cl_nolerp", "0" };
cvar_t lookspring = { "lookspring", "0", true };
cvar_t lookstrafe = { "lookstrafe", "0", true };
cvar_t sensitivity = { "sensitivity", "3", true };
cvar_t m_pitch = { "m_pitch", "0.022", true };
cvar_t m_yaw = { "m_yaw", "0.022", true };
cvar_t m_forward = { "m_forward", "1", true };
cvar_t m_side = { "m_side", "0.8", true };
client_static_t cls;
client_state_t cl;
// FIXME: put these on hunk?
efrag_t cl_efrags[MAX_EFRAGS];
entity_t cl_entities[MAX_EDICTS];
entity_t cl_static_entities[MAX_STATIC_ENTITIES];
lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
dlight_t cl_dlights[MAX_DLIGHTS];
int cl_numvisedicts;
entity_t *cl_visedicts[MAX_VISEDICTS];
/*
=====================
CL_ClearState
=====================
*/
void
CL_ClearState(void)
{
int i;
if (!sv.active)
Host_ClearMemory();
// wipe the entire cl structure
memset(&cl, 0, sizeof(cl));
SZ_Clear(&cls.message);
// clear other arrays
memset(cl_efrags, 0, sizeof(cl_efrags));
memset(cl_entities, 0, sizeof(cl_entities));
memset(cl_dlights, 0, sizeof(cl_dlights));
memset(cl_lightstyle, 0, sizeof(cl_lightstyle));
memset(cl_temp_entities, 0, sizeof(cl_temp_entities));
memset(cl_beams, 0, sizeof(cl_beams));
//
// allocate the efrags and chain together into a free list
//
cl.free_efrags = cl_efrags;
for (i = 0; i < MAX_EFRAGS - 1; i++)
cl.free_efrags[i].entnext = &cl.free_efrags[i + 1];
cl.free_efrags[i].entnext = NULL;
}
/*
=====================
CL_Disconnect
Sends a disconnect message to the server
This is also called on Host_Error, so it shouldn't cause any errors
=====================
*/
void
CL_Disconnect(void)
{
// stop sounds (especially looping!)
S_StopAllSounds(true);
// bring the console down and fade the colors back to normal
// SCR_BringDownConsole ();
// if running a local server, shut it down
if (cls.demoplayback)
CL_StopPlayback();
else if (cls.state >= ca_connected) {
if (cls.demorecording)
CL_Stop_f();
Con_DPrintf("Sending clc_disconnect\n");
SZ_Clear(&cls.message);
MSG_WriteByte(&cls.message, clc_disconnect);
NET_SendUnreliableMessage(cls.netcon, &cls.message);
SZ_Clear(&cls.message);
NET_Close(cls.netcon);
cls.state = ca_disconnected;
if (sv.active)
Host_ShutdownServer(false);
}
cls.demoplayback = false;
cls.timedemo = false;
cls.signon = 0;
}
void
CL_Disconnect_f(void)
{
CL_Disconnect();
if (sv.active)
Host_ShutdownServer(false);
}
/*
=====================
CL_EstablishConnection
Host should be either "local" or a net address to be passed on
=====================
*/
void
CL_EstablishConnection(char *host)
{
if (cls.state == ca_dedicated)
return;
if (cls.demoplayback)
return;
CL_Disconnect();
cls.netcon = NET_Connect(host);
if (!cls.netcon)
Host_Error("CL_Connect: connect failed");
Con_DPrintf("CL_EstablishConnection: connected to %s\n", host);
cls.demonum = -1; // not in the demo loop now
cls.state = ca_connected;
cls.signon = 0; // need all the signon messages before playing
}
/*
=====================
CL_SignonReply
An svc_signonnum has been received, perform a client side setup
=====================
*/
void
CL_SignonReply(void)
{
char str[8192];
Con_DPrintf("CL_SignonReply: %i\n", cls.signon);
switch (cls.signon) {
case 1:
MSG_WriteByte(&cls.message, clc_stringcmd);
MSG_WriteString(&cls.message, "prespawn");
break;
case 2:
MSG_WriteByte(&cls.message, clc_stringcmd);
MSG_WriteString(&cls.message, va("name \"%s\"\n", cl_name.string));
MSG_WriteByte(&cls.message, clc_stringcmd);
MSG_WriteString(&cls.message, va("color %i %i\n",
((int)cl_color.value) >> 4,
((int)cl_color.value) & 15));
MSG_WriteByte(&cls.message, clc_stringcmd);
sprintf(str, "spawn %s", cls.spawnparms);
MSG_WriteString(&cls.message, str);
break;
case 3:
MSG_WriteByte(&cls.message, clc_stringcmd);
MSG_WriteString(&cls.message, "begin");
Cache_Report(); // print remaining memory
// FIXME - this the right place for it?
cls.state = ca_firstupdate;
break;
case 4:
SCR_EndLoadingPlaque(); // allow normal screen updates
// FIXME - this the right place for it?
cls.state = ca_active;
break;
}
}
/*
=====================
CL_NextDemo
Called to play the next demo in the demo loop
=====================
*/
void
CL_NextDemo(void)
{
char str[1024];
if (cls.demonum == -1)
return; // don't play demos
SCR_BeginLoadingPlaque();
if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) {
cls.demonum = 0;
if (!cls.demos[cls.demonum][0]) {
Con_Printf("No demos listed with startdemos\n");
cls.demonum = -1;
return;
}
}
sprintf(str, "playdemo %s\n", cls.demos[cls.demonum]);
Cbuf_InsertText(str);
cls.demonum++;
}
/*
==============
CL_PrintEntities_f
==============
*/
void
CL_PrintEntities_f(void)
{
entity_t *ent;
int i;
for (i = 0, ent = cl_entities; i < cl.num_entities; i++, ent++) {
Con_Printf("%3i:", i);
if (!ent->model) {
Con_Printf("EMPTY\n");
continue;
}
Con_Printf("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n",
ent->model->name, ent->frame, ent->origin[0],
ent->origin[1], ent->origin[2], ent->angles[0],
ent->angles[1], ent->angles[2]);
}
}
/*
===============
SetPal
Debugging tool, just flashes the screen
===============
*/
void
SetPal(int i)
{
#if 0
static int old;
byte pal[768];
int c;
if (i == old)
return;
old = i;
if (i == 0)
VID_SetPalette(host_basepal);
else if (i == 1) {
for (c = 0; c < 768; c += 3) {
pal[c] = 0;
pal[c + 1] = 255;
pal[c + 2] = 0;
}
VID_SetPalette(pal);
} else {
for (c = 0; c < 768; c += 3) {
pal[c] = 0;
pal[c + 1] = 0;
pal[c + 2] = 255;
}
VID_SetPalette(pal);
}
#endif
}
/*
===============
CL_AllocDlight
===============
*/
dlight_t *
CL_AllocDlight(int key)
{
int i;
dlight_t *dl;
// first look for an exact key match
if (key) {
dl = cl_dlights;
for (i = 0; i < MAX_DLIGHTS; i++, dl++) {
if (dl->key == key) {
memset(dl, 0, sizeof(*dl));
dl->key = key;
return dl;
}
}
}
// then look for anything else
dl = cl_dlights;
for (i = 0; i < MAX_DLIGHTS; i++, dl++) {
if (dl->die < cl.time) {
memset(dl, 0, sizeof(*dl));
dl->key = key;
return dl;
}
}
dl = &cl_dlights[0];
memset(dl, 0, sizeof(*dl));
dl->key = key;
return dl;
}
/*
===============
CL_DecayLights
===============
*/
void
CL_DecayLights(void)
{
int i;
dlight_t *dl;
float time;
time = cl.time - cl.oldtime;
dl = cl_dlights;
for (i = 0; i < MAX_DLIGHTS; i++, dl++) {
if (dl->die < cl.time || !dl->radius)
continue;
dl->radius -= time * dl->decay;
if (dl->radius < 0)
dl->radius = 0;
}
}
/*
===============
CL_LerpPoint
Determines the fraction between the last two messages that the objects
should be put at.
===============
*/
float
CL_LerpPoint(void)
{
float f, frac;
f = cl.mtime[0] - cl.mtime[1];
if (!f || cl_nolerp.value || cls.timedemo || sv.active) {
cl.time = cl.mtime[0];
return 1;
}
if (f > 0.1) { // dropped packet, or start of demo
cl.mtime[1] = cl.mtime[0] - 0.1;
f = 0.1;
}
frac = (cl.time - cl.mtime[1]) / f;
//Con_Printf ("frac: %f\n",frac);
if (frac < 0) {
if (frac < -0.01) {
SetPal(1);
cl.time = cl.mtime[1];
// Con_Printf ("low frac\n");
}
frac = 0;
} else if (frac > 1) {
if (frac > 1.01) {
SetPal(2);
cl.time = cl.mtime[0];
// Con_Printf ("high frac\n");
}
frac = 1;
} else
SetPal(0);
return frac;
}
/*
===============
CL_RelinkEntities
===============
*/
void
CL_RelinkEntities(void)
{
entity_t *ent;
int i, j;
float frac, f, d;
vec3_t delta;
float bobjrotate;
vec3_t oldorg;
dlight_t *dl;
// determine partial update time
frac = CL_LerpPoint();
cl_numvisedicts = 0;
//
// interpolate player info
//
for (i = 0; i < 3; i++)
cl.velocity[i] = cl.mvelocity[1][i] +
frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]);
if (cls.demoplayback) {
// interpolate the angles
for (j = 0; j < 3; j++) {
d = cl.mviewangles[0][j] - cl.mviewangles[1][j];
if (d > 180)
d -= 360;
else if (d < -180)
d += 360;
cl.viewangles[j] = cl.mviewangles[1][j] + frac * d;
}
}
bobjrotate = anglemod(100 * cl.time);
// start on the entity after the world
for (i = 1, ent = cl_entities + 1; i < cl.num_entities; i++, ent++) {
if (!ent->model) { // empty slot
if (ent->forcelink)
R_RemoveEfrags(ent); // just became empty
continue;
}
// if the object wasn't included in the last packet, remove it
if (ent->msgtime != cl.mtime[0]) {
ent->model = NULL;
continue;
}
VectorCopy(ent->origin, oldorg);
if (ent->forcelink) { // the entity was not updated in the last message
// so move to the final spot
VectorCopy(ent->msg_origins[0], ent->origin);
VectorCopy(ent->msg_angles[0], ent->angles);
} else { // if the delta is large, assume a teleport and don't lerp
f = frac;
for (j = 0; j < 3; j++) {
delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
if (delta[j] > 100 || delta[j] < -100)
f = 1; // assume a teleportation, not a motion
}
// interpolate the origin and angles
for (j = 0; j < 3; j++) {
ent->origin[j] = ent->msg_origins[1][j] + f * delta[j];
d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
if (d > 180)
d -= 360;
else if (d < -180)
d += 360;
ent->angles[j] = ent->msg_angles[1][j] + f * d;
}
}
// rotate binary objects locally
if (ent->model->flags & EF_ROTATE)
ent->angles[1] = bobjrotate;
if (ent->effects & EF_BRIGHTFIELD)
R_EntityParticles(ent);
if (ent->effects & EF_MUZZLEFLASH) {
vec3_t fv, rv, uv;
dl = CL_AllocDlight(i);
VectorCopy(ent->origin, dl->origin);
dl->origin[2] += 16;
AngleVectors(ent->angles, fv, rv, uv);
VectorMA(dl->origin, 18, fv, dl->origin);
dl->radius = 200 + (rand() & 31);
dl->minlight = 32;
dl->die = cl.time + 0.1;
}
if (ent->effects & EF_BRIGHTLIGHT) {
dl = CL_AllocDlight(i);
VectorCopy(ent->origin, dl->origin);
dl->origin[2] += 16;
dl->radius = 400 + (rand() & 31);
dl->die = cl.time + 0.001;
}
if (ent->effects & EF_DIMLIGHT) {
dl = CL_AllocDlight(i);
VectorCopy(ent->origin, dl->origin);
dl->radius = 200 + (rand() & 31);
dl->die = cl.time + 0.001;
}
if (ent->model->flags & EF_GIB)
R_RocketTrail(oldorg, ent->origin, 2);
else if (ent->model->flags & EF_ZOMGIB)
R_RocketTrail(oldorg, ent->origin, 4);
else if (ent->model->flags & EF_TRACER)
R_RocketTrail(oldorg, ent->origin, 3);
else if (ent->model->flags & EF_TRACER2)
R_RocketTrail(oldorg, ent->origin, 5);
else if (ent->model->flags & EF_ROCKET) {
R_RocketTrail(oldorg, ent->origin, 0);
dl = CL_AllocDlight(i);
VectorCopy(ent->origin, dl->origin);
dl->radius = 200;
dl->die = cl.time + 0.01;
} else if (ent->model->flags & EF_GRENADE)
R_RocketTrail(oldorg, ent->origin, 1);
else if (ent->model->flags & EF_TRACER3)
R_RocketTrail(oldorg, ent->origin, 6);
ent->forcelink = false;
if (i == cl.viewentity && !chase_active.value)
continue;
if (cl_numvisedicts < MAX_VISEDICTS) {
cl_visedicts[cl_numvisedicts] = ent;
cl_numvisedicts++;
}
}
}
/*
===============
CL_ReadFromServer
Read all incoming data from the server
===============
*/
int
CL_ReadFromServer(void)
{
int ret;
cl.oldtime = cl.time;
cl.time += host_frametime;
do {
ret = CL_GetMessage();
if (ret == -1)
Host_Error("CL_ReadFromServer: lost server connection");
if (!ret)
break;
cl.last_received_message = realtime;
CL_ParseServerMessage();
} while (ret && cls.state >= ca_connected);
if (cl_shownet.value)
Con_Printf("\n");
CL_RelinkEntities();
CL_UpdateTEnts();
//
// bring the links up to date
//
return 0;
}
/*
=================
CL_SendCmd
=================
*/
void
CL_SendCmd(void)
{
usercmd_t cmd;
if (cls.state < ca_connected)
return;
if (cls.state == ca_active) {
// get basic movement from keyboard
CL_BaseMove(&cmd);
// allow mice or other external controllers to add to the move
IN_Move(&cmd);
// send the unreliable message
CL_SendMove(&cmd);
}
if (cls.demoplayback) {
SZ_Clear(&cls.message);
return;
}
// send the reliable message
if (!cls.message.cursize)
return; // no message at all
if (!NET_CanSendMessage(cls.netcon)) {
Con_DPrintf("CL_WriteToServer: can't send\n");
return;
}
if (NET_SendMessage(cls.netcon, &cls.message) == -1)
Host_Error("CL_WriteToServer: lost server connection");
SZ_Clear(&cls.message);
}
/*
=================
CL_Init
=================
*/
void
CL_Init(void)
{
SZ_Alloc(&cls.message, 1024);
CL_InitInput();
CL_InitTEnts();
//
// register our commands
//
Cvar_RegisterVariable(&cl_name);
Cvar_RegisterVariable(&cl_color);
Cvar_RegisterVariable(&cl_upspeed);
Cvar_RegisterVariable(&cl_forwardspeed);
Cvar_RegisterVariable(&cl_backspeed);
Cvar_RegisterVariable(&cl_sidespeed);
Cvar_RegisterVariable(&cl_movespeedkey);
Cvar_RegisterVariable(&cl_yawspeed);
Cvar_RegisterVariable(&cl_pitchspeed);
Cvar_RegisterVariable(&cl_anglespeedkey);
Cvar_RegisterVariable(&cl_shownet);
Cvar_RegisterVariable(&cl_nolerp);
Cvar_RegisterVariable(&lookspring);
Cvar_RegisterVariable(&lookstrafe);
Cvar_RegisterVariable(&sensitivity);
Cvar_RegisterVariable(&m_pitch);
Cvar_RegisterVariable(&m_yaw);
Cvar_RegisterVariable(&m_forward);
Cvar_RegisterVariable(&m_side);
// Cvar_RegisterVariable (&cl_autofire);
Cmd_AddCommand("entities", CL_PrintEntities_f);
Cmd_AddCommand("disconnect", CL_Disconnect_f);
Cmd_AddCommand("record", CL_Record_f);
Cmd_AddCommand("stop", CL_Stop_f);
Cmd_AddCommand("playdemo", CL_PlayDemo_f);
Cmd_AddCommand("timedemo", CL_TimeDemo_f);
}

967
NQ/cl_parse.c Normal file
View File

@ -0,0 +1,967 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cl_parse.c -- parse a message received from the server
#include "quakedef.h"
#include "host.h"
#include "client.h"
#include "protocol.h"
#include "sound.h"
#include "server.h"
#include "net.h"
#include "sys.h"
#include "console.h"
#include "sbar.h"
#include "screen.h"
#include "cdaudio.h"
#include "cmd.h"
#ifdef GLQUAKE
# include "glquake.h"
# include "gl_model.h"
#else
# include "model.h"
#endif
char *svc_strings[] = {
"svc_bad",
"svc_nop",
"svc_disconnect",
"svc_updatestat",
"svc_version", // [long] server version
"svc_setview", // [short] entity number
"svc_sound", // <see code>
"svc_time", // [float] server time
"svc_print", // [string] null terminated string
"svc_stufftext", // [string] stuffed into client's console buffer
// the string should be \n terminated
"svc_setangle", // [vec3] set the view angle to this absolute value
"svc_serverinfo", // [long] version
// [string] signon string
// [string]..[0]model cache [string]...[0]sounds cache
// [string]..[0]item cache
"svc_lightstyle", // [byte] [string]
"svc_updatename", // [byte] [string]
"svc_updatefrags", // [byte] [short]
"svc_clientdata", // <shortbits + data>
"svc_stopsound", // <see code>
"svc_updatecolors", // [byte] [byte]
"svc_particle", // [vec3] <variable>
"svc_damage", // [byte] impact [byte] blood [vec3] from
"svc_spawnstatic",
"OBSOLETE svc_spawnbinary",
"svc_spawnbaseline",
"svc_temp_entity", // <variable>
"svc_setpause",
"svc_signonnum",
"svc_centerprint",
"svc_killedmonster",
"svc_foundsecret",
"svc_spawnstaticsound",
"svc_intermission",
"svc_finale", // [string] music [string] text
"svc_cdtrack", // [byte] track [byte] looptrack
"svc_sellscreen",
"svc_cutscene"
};
//=============================================================================
/*
===============
CL_EntityNum
This error checks and tracks the total number of entities
===============
*/
entity_t *
CL_EntityNum(int num)
{
if (num >= cl.num_entities) {
if (num >= MAX_EDICTS)
Host_Error("CL_EntityNum: %i is an invalid number", num);
while (cl.num_entities <= num) {
cl_entities[cl.num_entities].colormap = vid.colormap;
cl.num_entities++;
}
}
return &cl_entities[num];
}
/*
==================
CL_ParseStartSoundPacket
==================
*/
void
CL_ParseStartSoundPacket(void)
{
vec3_t pos;
int channel, ent;
int sound_num;
int volume;
int field_mask;
float attenuation;
int i;
field_mask = MSG_ReadByte();
if (field_mask & SND_VOLUME)
volume = MSG_ReadByte();
else
volume = DEFAULT_SOUND_PACKET_VOLUME;
if (field_mask & SND_ATTENUATION)
attenuation = MSG_ReadByte() / 64.0;
else
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
channel = MSG_ReadShort();
sound_num = MSG_ReadByte();
ent = channel >> 3;
channel &= 7;
if (ent > MAX_EDICTS)
Host_Error("CL_ParseStartSoundPacket: ent = %i", ent);
for (i = 0; i < 3; i++)
pos[i] = MSG_ReadCoord();
S_StartSound(ent, channel, cl.sound_precache[sound_num], pos,
volume / 255.0, attenuation);
}
/*
==================
CL_KeepaliveMessage
When the client is taking a long time to load stuff, send keepalive messages
so the server doesn't disconnect.
==================
*/
void
CL_KeepaliveMessage(void)
{
float time;
static float lastmsg;
int ret;
sizebuf_t old;
byte olddata[8192];
if (sv.active)
return; // no need if server is local
if (cls.demoplayback)
return;
// read messages from server, should just be nops
old = net_message;
memcpy(olddata, net_message.data, net_message.cursize);
do {
ret = CL_GetMessage();
switch (ret) {
default:
Host_Error("CL_KeepaliveMessage: CL_GetMessage failed");
case 0:
break; // nothing waiting
case 1:
Host_Error("CL_KeepaliveMessage: received a message");
break;
case 2:
if (MSG_ReadByte() != svc_nop)
Host_Error("CL_KeepaliveMessage: datagram wasn't a nop");
break;
}
} while (ret);
net_message = old;
memcpy(net_message.data, olddata, net_message.cursize);
// check time
time = Sys_DoubleTime();
if (time - lastmsg < 5)
return;
lastmsg = time;
// write out a nop
Con_Printf("--> client to server keepalive\n");
MSG_WriteByte(&cls.message, clc_nop);
NET_SendMessage(cls.netcon, &cls.message);
SZ_Clear(&cls.message);
}
/*
==================
CL_ParseServerInfo
==================
*/
void
CL_ParseServerInfo(void)
{
char *str;
int i;
int nummodels, numsounds;
char model_precache[MAX_MODELS][MAX_QPATH];
char sound_precache[MAX_SOUNDS][MAX_QPATH];
Con_DPrintf("Serverinfo packet received.\n");
//
// wipe the client_state_t struct
//
CL_ClearState();
// parse protocol version number
i = MSG_ReadLong();
if (i != PROTOCOL_VERSION) {
Con_Printf("Server returned version %i, not %i\n", i,
PROTOCOL_VERSION);
return;
}
// parse maxclients
cl.maxclients = MSG_ReadByte();
if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD) {
Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients);
return;
}
cl.scores = Hunk_AllocName(cl.maxclients * sizeof(*cl.scores), "scores");
// parse gametype
cl.gametype = MSG_ReadByte();
// parse signon message
str = MSG_ReadString();
strncpy(cl.levelname, str, sizeof(cl.levelname) - 1);
// seperate the printfs so the server message can have a color
Con_Printf
("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
Con_Printf("%c%s\n", 2, str);
//
// first we go through and touch all of the precache data that still
// happens to be in the cache, so precaching something else doesn't
// needlessly purge it
//
// precache models
memset(cl.model_precache, 0, sizeof(cl.model_precache));
for (nummodels = 1;; nummodels++) {
str = MSG_ReadString();
if (!str[0])
break;
if (nummodels == MAX_MODELS) {
Con_Printf("Server sent too many model precaches\n");
return;
}
strcpy(model_precache[nummodels], str);
Mod_TouchModel(str);
}
// precache sounds
memset(cl.sound_precache, 0, sizeof(cl.sound_precache));
for (numsounds = 1;; numsounds++) {
str = MSG_ReadString();
if (!str[0])
break;
if (numsounds == MAX_SOUNDS) {
Con_Printf("Server sent too many sound precaches\n");
return;
}
strcpy(sound_precache[numsounds], str);
S_TouchSound(str);
}
//
// now we try to load everything else until a cache allocation fails
//
for (i = 1; i < nummodels; i++) {
cl.model_precache[i] = Mod_ForName(model_precache[i], false);
if (cl.model_precache[i] == NULL) {
Con_Printf("Model %s not found\n", model_precache[i]);
return;
}
CL_KeepaliveMessage();
}
S_BeginPrecaching();
for (i = 1; i < numsounds; i++) {
cl.sound_precache[i] = S_PrecacheSound(sound_precache[i]);
CL_KeepaliveMessage();
}
S_EndPrecaching();
// local state
cl_entities[0].model = cl.worldmodel = cl.model_precache[1];
R_NewMap();
Hunk_Check(); // make sure nothing is hurt
noclip_anglehack = false; // noclip is turned off at start
}
/*
==================
CL_ParseUpdate
Parse an entity update message from the server
If an entities model or origin changes from frame to frame, it must be
relinked. Other attributes can change without relinking.
==================
*/
// FIXME - is this 16 == MAX_CLIENTS?
int bitcounts[16];
void
CL_ParseUpdate(int bits)
{
int i;
model_t *model;
int modnum;
qboolean forcelink;
entity_t *ent;
int num;
// FIXME - do this cleanly...
#ifdef GLQUAKE
int skin;
#endif
if (cls.state == ca_firstupdate) {
// first update is the final signon stage
cls.signon = SIGNONS;
CL_SignonReply();
}
if (bits & U_MOREBITS) {
i = MSG_ReadByte();
bits |= (i << 8);
}
if (bits & U_LONGENTITY)
num = MSG_ReadShort();
else
num = MSG_ReadByte();
ent = CL_EntityNum(num);
for (i = 0; i < 16; i++)
if (bits & (1 << i))
bitcounts[i]++;
if (ent->msgtime != cl.mtime[1])
forcelink = true; // no previous frame to lerp from
else
forcelink = false;
ent->msgtime = cl.mtime[0];
if (bits & U_MODEL) {
modnum = MSG_ReadByte();
if (modnum >= MAX_MODELS)
Host_Error("CL_ParseModel: bad modnum");
} else
modnum = ent->baseline.modelindex;
model = cl.model_precache[modnum];
if (model != ent->model) {
ent->model = model;
// automatic animation (torches, etc) can be either all together
// or randomized
if (model) {
if (model->synctype == ST_RAND)
ent->syncbase = (float)(rand() & 0x7fff) / 0x7fff;
else
ent->syncbase = 0.0;
} else
forcelink = true; // hack to make null model players work
#ifdef GLQUAKE
if (num > 0 && num <= cl.maxclients)
R_TranslatePlayerSkin(num - 1);
#endif
}
if (bits & U_FRAME)
ent->frame = MSG_ReadByte();
else
ent->frame = ent->baseline.frame;
if (bits & U_COLORMAP)
i = MSG_ReadByte();
else
i = ent->baseline.colormap;
if (!i)
ent->colormap = vid.colormap;
else {
if (i > cl.maxclients)
Sys_Error("i >= cl.maxclients");
ent->colormap = cl.scores[i - 1].translations;
}
#ifdef GLQUAKE
if (bits & U_SKIN)
skin = MSG_ReadByte();
else
skin = ent->baseline.skinnum;
if (skin != ent->skinnum) {
ent->skinnum = skin;
if (num > 0 && num <= cl.maxclients)
R_TranslatePlayerSkin(num - 1);
}
#else
if (bits & U_SKIN)
ent->skinnum = MSG_ReadByte();
else
ent->skinnum = ent->baseline.skinnum;
#endif
if (bits & U_EFFECTS)
ent->effects = MSG_ReadByte();
else
ent->effects = ent->baseline.effects;
// shift the known values for interpolation
VectorCopy(ent->msg_origins[0], ent->msg_origins[1]);
VectorCopy(ent->msg_angles[0], ent->msg_angles[1]);
if (bits & U_ORIGIN1)
ent->msg_origins[0][0] = MSG_ReadCoord();
else
ent->msg_origins[0][0] = ent->baseline.origin[0];
if (bits & U_ANGLE1)
ent->msg_angles[0][0] = MSG_ReadAngle();
else
ent->msg_angles[0][0] = ent->baseline.angles[0];
if (bits & U_ORIGIN2)
ent->msg_origins[0][1] = MSG_ReadCoord();
else
ent->msg_origins[0][1] = ent->baseline.origin[1];
if (bits & U_ANGLE2)
ent->msg_angles[0][1] = MSG_ReadAngle();
else
ent->msg_angles[0][1] = ent->baseline.angles[1];
if (bits & U_ORIGIN3)
ent->msg_origins[0][2] = MSG_ReadCoord();
else
ent->msg_origins[0][2] = ent->baseline.origin[2];
if (bits & U_ANGLE3)
ent->msg_angles[0][2] = MSG_ReadAngle();
else
ent->msg_angles[0][2] = ent->baseline.angles[2];
if (bits & U_NOLERP)
ent->forcelink = true;
if (forcelink) { // didn't have an update last message
VectorCopy(ent->msg_origins[0], ent->msg_origins[1]);
VectorCopy(ent->msg_origins[0], ent->origin);
VectorCopy(ent->msg_angles[0], ent->msg_angles[1]);
VectorCopy(ent->msg_angles[0], ent->angles);
ent->forcelink = true;
}
}
/*
==================
CL_ParseBaseline
==================
*/
void
CL_ParseBaseline(entity_t *ent)
{
int i;
ent->baseline.modelindex = MSG_ReadByte();
ent->baseline.frame = MSG_ReadByte();
ent->baseline.colormap = MSG_ReadByte();
ent->baseline.skinnum = MSG_ReadByte();
for (i = 0; i < 3; i++) {
ent->baseline.origin[i] = MSG_ReadCoord();
ent->baseline.angles[i] = MSG_ReadAngle();
}
}
/*
==================
CL_ParseClientdata
Server information pertaining to this client only
==================
*/
void
CL_ParseClientdata(int bits)
{
int i, j;
if (bits & SU_VIEWHEIGHT)
cl.viewheight = MSG_ReadChar();
else
cl.viewheight = DEFAULT_VIEWHEIGHT;
if (bits & SU_IDEALPITCH)
cl.idealpitch = MSG_ReadChar();
else
cl.idealpitch = 0;
VectorCopy(cl.mvelocity[0], cl.mvelocity[1]);
for (i = 0; i < 3; i++) {
if (bits & (SU_PUNCH1 << i))
cl.punchangle[i] = MSG_ReadChar();
else
cl.punchangle[i] = 0;
if (bits & (SU_VELOCITY1 << i))
cl.mvelocity[0][i] = MSG_ReadChar() * 16;
else
cl.mvelocity[0][i] = 0;
}
// [always sent] if (bits & SU_ITEMS)
i = MSG_ReadLong();
if (cl.items != i) { // set flash times
Sbar_Changed();
for (j = 0; j < 32; j++)
if ((i & (1 << j)) && !(cl.items & (1 << j)))
cl.item_gettime[j] = cl.time;
cl.items = i;
}
cl.onground = (bits & SU_ONGROUND) != 0;
cl.inwater = (bits & SU_INWATER) != 0;
if (bits & SU_WEAPONFRAME)
cl.stats[STAT_WEAPONFRAME] = MSG_ReadByte();
else
cl.stats[STAT_WEAPONFRAME] = 0;
if (bits & SU_ARMOR)
i = MSG_ReadByte();
else
i = 0;
if (cl.stats[STAT_ARMOR] != i) {
cl.stats[STAT_ARMOR] = i;
Sbar_Changed();
}
if (bits & SU_WEAPON)
i = MSG_ReadByte();
else
i = 0;
if (cl.stats[STAT_WEAPON] != i) {
cl.stats[STAT_WEAPON] = i;
Sbar_Changed();
}
i = MSG_ReadShort();
if (cl.stats[STAT_HEALTH] != i) {
cl.stats[STAT_HEALTH] = i;
Sbar_Changed();
}
i = MSG_ReadByte();
if (cl.stats[STAT_AMMO] != i) {
cl.stats[STAT_AMMO] = i;
Sbar_Changed();
}
for (i = 0; i < 4; i++) {
j = MSG_ReadByte();
if (cl.stats[STAT_SHELLS + i] != j) {
cl.stats[STAT_SHELLS + i] = j;
Sbar_Changed();
}
}
i = MSG_ReadByte();
if (standard_quake) {
if (cl.stats[STAT_ACTIVEWEAPON] != i) {
cl.stats[STAT_ACTIVEWEAPON] = i;
Sbar_Changed();
}
} else {
if (cl.stats[STAT_ACTIVEWEAPON] != (1 << i)) {
cl.stats[STAT_ACTIVEWEAPON] = (1 << i);
Sbar_Changed();
}
}
}
/*
=====================
CL_NewTranslation
=====================
*/
void
CL_NewTranslation(int slot)
{
int i, j;
int top, bottom;
byte *dest, *source;
if (slot > cl.maxclients)
Sys_Error("%s: slot > cl.maxclients", __func__);
dest = cl.scores[slot].translations;
source = vid.colormap;
memcpy(dest, vid.colormap, sizeof(cl.scores[slot].translations));
top = cl.scores[slot].colors & 0xf0;
bottom = (cl.scores[slot].colors & 15) << 4;
#ifdef GLQUAKE
R_TranslatePlayerSkin(slot);
#endif
for (i = 0; i < VID_GRADES; i++, dest += 256, source += 256) {
if (top < 128) // the artists made some backwards ranges. sigh.
memcpy(dest + TOP_RANGE, source + top, 16);
else
for (j = 0; j < 16; j++)
dest[TOP_RANGE + j] = source[top + 15 - j];
if (bottom < 128)
memcpy(dest + BOTTOM_RANGE, source + bottom, 16);
else
for (j = 0; j < 16; j++)
dest[BOTTOM_RANGE + j] = source[bottom + 15 - j];
}
}
/*
=====================
CL_ParseStatic
=====================
*/
void
CL_ParseStatic(void)
{
entity_t *ent;
int i;
i = cl.num_statics;
if (i >= MAX_STATIC_ENTITIES)
Host_Error("Too many static entities");
ent = &cl_static_entities[i];
cl.num_statics++;
CL_ParseBaseline(ent);
// copy it to the current state
ent->model = cl.model_precache[ent->baseline.modelindex];
ent->frame = ent->baseline.frame;
ent->colormap = vid.colormap;
ent->skinnum = ent->baseline.skinnum;
ent->effects = ent->baseline.effects;
VectorCopy(ent->baseline.origin, ent->origin);
VectorCopy(ent->baseline.angles, ent->angles);
R_AddEfrags(ent);
}
/*
===================
CL_ParseStaticSound
===================
*/
void
CL_ParseStaticSound(void)
{
vec3_t org;
int sound_num, vol, atten;
int i;
for (i = 0; i < 3; i++)
org[i] = MSG_ReadCoord();
sound_num = MSG_ReadByte();
vol = MSG_ReadByte();
atten = MSG_ReadByte();
S_StaticSound(cl.sound_precache[sound_num], org, vol, atten);
}
/* helper function (was a macro, hence the CAPS) */
static void
SHOWNET(char *msg)
{
if (cl_shownet.value == 2)
Con_Printf("%3i:%s\n", msg_readcount - 1, msg);
}
/*
=====================
CL_ParseServerMessage
=====================
*/
void
CL_ParseServerMessage(void)
{
int cmd;
int i;
//
// if recording demos, copy the message out
//
if (cl_shownet.value == 1)
Con_Printf("%i ", net_message.cursize);
else if (cl_shownet.value == 2)
Con_Printf("------------------\n");
cl.onground = false; // unless the server says otherwise
//
// parse the message
//
MSG_BeginReading();
while (1) {
if (msg_badread)
Host_Error("CL_ParseServerMessage: Bad server message");
cmd = MSG_ReadByte();
if (cmd == -1) {
SHOWNET("END OF MESSAGE");
return; // end of message
}
// if the high bit of the command byte is set, it is a fast update
if (cmd & 128) {
SHOWNET("fast update");
CL_ParseUpdate(cmd & 127);
continue;
}
SHOWNET(svc_strings[cmd]);
// other commands
switch (cmd) {
default:
Host_Error("CL_ParseServerMessage: Illegible server message");
break;
case svc_nop:
// Con_Printf ("svc_nop\n");
break;
case svc_time:
cl.mtime[1] = cl.mtime[0];
cl.mtime[0] = MSG_ReadFloat();
break;
case svc_clientdata:
i = MSG_ReadShort();
CL_ParseClientdata(i);
break;
case svc_version:
i = MSG_ReadLong();
if (i != PROTOCOL_VERSION)
Host_Error("CL_ParseServerMessage: "
"Server is protocol %i instead of %i",
i, PROTOCOL_VERSION);
break;
case svc_disconnect:
Host_EndGame("Server disconnected\n");
case svc_print:
Con_Printf("%s", MSG_ReadString());
break;
case svc_centerprint:
SCR_CenterPrint(MSG_ReadString());
break;
case svc_stufftext:
Cbuf_AddText(MSG_ReadString());
break;
case svc_damage:
V_ParseDamage();
break;
case svc_serverinfo:
CL_ParseServerInfo();
vid.recalc_refdef = true; // leave intermission full screen
break;
case svc_setangle:
for (i = 0; i < 3; i++)
cl.viewangles[i] = MSG_ReadAngle();
break;
case svc_setview:
cl.viewentity = MSG_ReadShort();
break;
case svc_lightstyle:
i = MSG_ReadByte();
if (i >= MAX_LIGHTSTYLES)
Sys_Error("svc_lightstyle > MAX_LIGHTSTYLES");
strcpy(cl_lightstyle[i].map, MSG_ReadString());
cl_lightstyle[i].length = strlen(cl_lightstyle[i].map);
break;
case svc_sound:
CL_ParseStartSoundPacket();
break;
case svc_stopsound:
i = MSG_ReadShort();
S_StopSound(i >> 3, i & 7);
break;
case svc_updatename:
Sbar_Changed();
i = MSG_ReadByte();
if (i >= cl.maxclients)
Host_Error
("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD");
strcpy(cl.scores[i].name, MSG_ReadString());
break;
case svc_updatefrags:
Sbar_Changed();
i = MSG_ReadByte();
if (i >= cl.maxclients)
Host_Error
("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD");
cl.scores[i].frags = MSG_ReadShort();
break;
case svc_updatecolors:
Sbar_Changed();
i = MSG_ReadByte();
if (i >= cl.maxclients)
Host_Error
("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD");
cl.scores[i].colors = MSG_ReadByte();
CL_NewTranslation(i);
break;
case svc_particle:
R_ParseParticleEffect();
break;
case svc_spawnbaseline:
i = MSG_ReadShort();
// must use CL_EntityNum() to force cl.num_entities up
CL_ParseBaseline(CL_EntityNum(i));
break;
case svc_spawnstatic:
CL_ParseStatic();
break;
case svc_temp_entity:
CL_ParseTEnt();
break;
case svc_setpause:
{
cl.paused = MSG_ReadByte();
if (cl.paused) {
CDAudio_Pause();
#ifdef _WIN32
VID_HandlePause(true);
#endif
} else {
CDAudio_Resume();
#ifdef _WIN32
VID_HandlePause(false);
#endif
}
}
break;
case svc_signonnum:
i = MSG_ReadByte();
if (i <= cls.signon)
Host_Error("Received signon %i when at %i", i, cls.signon);
cls.signon = i;
CL_SignonReply();
break;
case svc_killedmonster:
cl.stats[STAT_MONSTERS]++;
break;
case svc_foundsecret:
cl.stats[STAT_SECRETS]++;
break;
case svc_updatestat:
i = MSG_ReadByte();
if (i < 0 || i >= MAX_CL_STATS)
Sys_Error("svc_updatestat: %i is invalid", i);
cl.stats[i] = MSG_ReadLong();;
break;
case svc_spawnstaticsound:
CL_ParseStaticSound();
break;
case svc_cdtrack:
cl.cdtrack = MSG_ReadByte();
cl.looptrack = MSG_ReadByte();
if ((cls.demoplayback || cls.demorecording)
&& (cls.forcetrack != -1))
CDAudio_Play((byte)cls.forcetrack, true);
else
CDAudio_Play((byte)cl.cdtrack, true);
break;
case svc_intermission:
cl.intermission = 1;
cl.completed_time = cl.time;
vid.recalc_refdef = true; // go to full screen
break;
case svc_finale:
cl.intermission = 2;
cl.completed_time = cl.time;
vid.recalc_refdef = true; // go to full screen
SCR_CenterPrint(MSG_ReadString());
break;
case svc_cutscene:
cl.intermission = 3;
cl.completed_time = cl.time;
vid.recalc_refdef = true; // go to full screen
SCR_CenterPrint(MSG_ReadString());
break;
case svc_sellscreen:
Cmd_ExecuteString("help", src_command);
break;
}
}
}

355
NQ/cl_tent.c Normal file
View File

@ -0,0 +1,355 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cl_tent.c -- client side temporary entities
#include "quakedef.h"
#include "client.h"
#include "sound.h"
#include "protocol.h"
#include "console.h"
#include "sys.h"
#ifdef GLQUAKE
# include "gl_model.h"
#else
# include "model.h"
#endif
int num_temp_entities;
entity_t cl_temp_entities[MAX_TEMP_ENTITIES];
beam_t cl_beams[MAX_BEAMS];
sfx_t *cl_sfx_wizhit;
sfx_t *cl_sfx_knighthit;
sfx_t *cl_sfx_tink1;
sfx_t *cl_sfx_ric1;
sfx_t *cl_sfx_ric2;
sfx_t *cl_sfx_ric3;
sfx_t *cl_sfx_r_exp3;
/*
=================
CL_ParseTEnt
=================
*/
void
CL_InitTEnts(void)
{
cl_sfx_wizhit = S_PrecacheSound("wizard/hit.wav");
cl_sfx_knighthit = S_PrecacheSound("hknight/hit.wav");
cl_sfx_tink1 = S_PrecacheSound("weapons/tink1.wav");
cl_sfx_ric1 = S_PrecacheSound("weapons/ric1.wav");
cl_sfx_ric2 = S_PrecacheSound("weapons/ric2.wav");
cl_sfx_ric3 = S_PrecacheSound("weapons/ric3.wav");
cl_sfx_r_exp3 = S_PrecacheSound("weapons/r_exp3.wav");
}
/*
=================
CL_ParseBeam
=================
*/
void
CL_ParseBeam(model_t *m)
{
int ent;
vec3_t start, end;
beam_t *b;
int i;
ent = MSG_ReadShort();
start[0] = MSG_ReadCoord();
start[1] = MSG_ReadCoord();
start[2] = MSG_ReadCoord();
end[0] = MSG_ReadCoord();
end[1] = MSG_ReadCoord();
end[2] = MSG_ReadCoord();
// override any beam with the same entity
for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++)
if (b->entity == ent) {
b->entity = ent;
b->model = m;
b->endtime = cl.time + 0.2;
VectorCopy(start, b->start);
VectorCopy(end, b->end);
return;
}
// find a free beam
for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) {
if (!b->model || b->endtime < cl.time) {
b->entity = ent;
b->model = m;
b->endtime = cl.time + 0.2;
VectorCopy(start, b->start);
VectorCopy(end, b->end);
return;
}
}
Con_Printf("beam list overflow!\n");
}
/*
=================
CL_ParseTEnt
=================
*/
void
CL_ParseTEnt(void)
{
int type;
vec3_t pos;
dlight_t *dl;
int rnd;
int colorStart, colorLength;
type = MSG_ReadByte();
switch (type) {
case TE_WIZSPIKE: // spike hitting wall
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 20, 30);
S_StartSound(-1, 0, cl_sfx_wizhit, pos, 1, 1);
break;
case TE_KNIGHTSPIKE: // spike hitting wall
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 226, 20);
S_StartSound(-1, 0, cl_sfx_knighthit, pos, 1, 1);
break;
case TE_SPIKE: // spike hitting wall
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 0, 10);
if (rand() % 5)
S_StartSound(-1, 0, cl_sfx_tink1, pos, 1, 1);
else {
rnd = rand() & 3;
if (rnd == 1)
S_StartSound(-1, 0, cl_sfx_ric1, pos, 1, 1);
else if (rnd == 2)
S_StartSound(-1, 0, cl_sfx_ric2, pos, 1, 1);
else
S_StartSound(-1, 0, cl_sfx_ric3, pos, 1, 1);
}
break;
case TE_SUPERSPIKE: // super spike hitting wall
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 0, 20);
if (rand() % 5)
S_StartSound(-1, 0, cl_sfx_tink1, pos, 1, 1);
else {
rnd = rand() & 3;
if (rnd == 1)
S_StartSound(-1, 0, cl_sfx_ric1, pos, 1, 1);
else if (rnd == 2)
S_StartSound(-1, 0, cl_sfx_ric2, pos, 1, 1);
else
S_StartSound(-1, 0, cl_sfx_ric3, pos, 1, 1);
}
break;
case TE_GUNSHOT: // bullet hitting wall
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 0, 20);
break;
case TE_EXPLOSION: // rocket explosion
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_ParticleExplosion(pos);
dl = CL_AllocDlight(0);
VectorCopy(pos, dl->origin);
dl->radius = 350;
dl->die = cl.time + 0.5;
dl->decay = 300;
S_StartSound(-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
case TE_TAREXPLOSION: // tarbaby explosion
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_BlobExplosion(pos);
S_StartSound(-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
case TE_LIGHTNING1: // lightning bolts
CL_ParseBeam(Mod_ForName("progs/bolt.mdl", true));
break;
case TE_LIGHTNING2: // lightning bolts
CL_ParseBeam(Mod_ForName("progs/bolt2.mdl", true));
break;
case TE_LIGHTNING3: // lightning bolts
CL_ParseBeam(Mod_ForName("progs/bolt3.mdl", true));
break;
// PGM 01/21/97
case TE_BEAM: // grappling hook beam
CL_ParseBeam(Mod_ForName("progs/beam.mdl", true));
break;
// PGM 01/21/97
case TE_LAVASPLASH:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_LavaSplash(pos);
break;
case TE_TELEPORT:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_TeleportSplash(pos);
break;
case TE_EXPLOSION2: // color mapped explosion
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
colorStart = MSG_ReadByte();
colorLength = MSG_ReadByte();
R_ParticleExplosion2(pos, colorStart, colorLength);
dl = CL_AllocDlight(0);
VectorCopy(pos, dl->origin);
dl->radius = 350;
dl->die = cl.time + 0.5;
dl->decay = 300;
S_StartSound(-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
default:
Sys_Error("%s: bad type", __func__);
}
}
/*
=================
CL_NewTempEntity
=================
*/
entity_t *
CL_NewTempEntity(void)
{
entity_t *ent;
if (cl_numvisedicts == MAX_VISEDICTS)
return NULL;
if (num_temp_entities == MAX_TEMP_ENTITIES)
return NULL;
ent = &cl_temp_entities[num_temp_entities];
memset(ent, 0, sizeof(*ent));
num_temp_entities++;
cl_visedicts[cl_numvisedicts] = ent;
cl_numvisedicts++;
ent->colormap = vid.colormap;
return ent;
}
/*
=================
CL_UpdateTEnts
=================
*/
void
CL_UpdateTEnts(void)
{
int i;
beam_t *b;
vec3_t dist, org;
float d;
entity_t *ent;
float yaw, pitch;
float forward;
num_temp_entities = 0;
// update lightning
for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) {
if (!b->model || b->endtime < cl.time)
continue;
// if coming from the player, update the start position
if (b->entity == cl.viewentity) {
VectorCopy(cl_entities[cl.viewentity].origin, b->start);
}
// calculate pitch and yaw
VectorSubtract(b->end, b->start, dist);
if (dist[1] == 0 && dist[0] == 0) {
yaw = 0;
if (dist[2] > 0)
pitch = 90;
else
pitch = 270;
} else {
yaw = (int)(atan2(dist[1], dist[0]) * 180 / M_PI);
if (yaw < 0)
yaw += 360;
forward = sqrt(dist[0] * dist[0] + dist[1] * dist[1]);
pitch = (int)(atan2(dist[2], forward) * 180 / M_PI);
if (pitch < 0)
pitch += 360;
}
// add new entities for the lightning
VectorCopy(b->start, org);
d = VectorNormalize(dist);
while (d > 0) {
ent = CL_NewTempEntity();
if (!ent)
return;
VectorCopy(org, ent->origin);
ent->model = b->model;
ent->angles[0] = pitch;
ent->angles[1] = yaw;
ent->angles[2] = rand() % 360;
for (i = 0; i < 3; i++)
org[i] += dist[i] * 30;
d -= 30;
}
}
}

359
NQ/client.h Normal file
View File

@ -0,0 +1,359 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef CLIENT_H
#define CLIENT_H
#include "common.h"
#include "mathlib.h"
#include "quakedef.h"
#include "render.h"
#include "vid.h"
//
// client_state_t should hold all pieces of the client state
//
typedef struct {
vec3_t viewangles;
// intended velocities
float forwardmove;
float sidemove;
float upmove;
} usercmd_t;
#define MAX_DLIGHTS 32
typedef struct {
int key; // so entities can reuse same entry
vec3_t origin;
float radius;
float die; // stop lighting after this time
float decay; // drop this each second
float minlight; // don't add when contributing less
} dlight_t;
typedef struct {
int length;
char map[MAX_STYLESTRING];
} lightstyle_t;
#define MAX_SCOREBOARDNAME 32
typedef struct {
char name[MAX_SCOREBOARDNAME];
float entertime;
int frags;
int colors; // two 4 bit fields
byte translations[VID_GRADES * 256];
} scoreboard_t;
typedef struct {
int destcolor[3];
int percent; // 0-256
} cshift_t;
#define CSHIFT_CONTENTS 0
#define CSHIFT_DAMAGE 1
#define CSHIFT_BONUS 2
#define CSHIFT_POWERUP 3
#define NUM_CSHIFTS 4
#define MAX_BEAMS 24
typedef struct {
int entity;
struct model_s *model;
float endtime;
vec3_t start, end;
} beam_t;
#define MAX_EFRAGS 640
#define MAX_MAPSTRING 2048
#define MAX_DEMOS 8
#define MAX_DEMONAME 16
#define SIGNONS 4 // signon messages to receive before connected
typedef enum {
ca_dedicated, // a dedicated server with no ability to start a client
ca_disconnected, // full screen console with no connection
ca_connected, // valid netcon, doing signons...
ca_firstupdate, // waiting for first update (final signon stage)
ca_active // signons complete, frames can be rendered
} cactive_t;
//
// the client_static_t structure is persistant through an arbitrary number
// of server connections
//
typedef struct {
cactive_t state;
// personalization data sent to server
char mapstring[MAX_QPATH];
char spawnparms[MAX_MAPSTRING]; // to restart a level
// demo loop control
int demonum; // -1 = don't play demos
char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing
// demo recording info must be here, because record is started before
// entering a map (and clearing client_state_t)
qboolean demorecording;
qboolean demoplayback;
qboolean timedemo;
int forcetrack; // -1 = use normal cd track
FILE *demofile;
int td_lastframe; // to meter out one message a frame
int td_startframe; // host_framecount at start
float td_starttime; // realtime at second frame of timedemo
// connection information
int signon; // 0 to SIGNONS
struct qsocket_s *netcon;
sizebuf_t message; // writing buffer to send to server
} client_static_t;
extern client_static_t cls;
//
// the client_state_t structure is wiped completely at every
// server signon
//
typedef struct {
int movemessages; // since connecting to this server
// throw out the first couple, so the player
// doesn't accidentally do something the
// first frame
usercmd_t cmd; // last command sent to the server
// information for local display
int stats[MAX_CL_STATS]; // health, etc
int items; // inventory bit flags
float item_gettime[32]; // cl.time of aquiring item, for blinking
float faceanimtime; // use anim frame if cl.time < this
cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups
cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types
// the client maintains its own idea of view angles, which are
// sent to the server each frame. The server sets punchangle when
// the view is temporarliy offset, and an angle reset commands at the start
// of each level and after teleporting.
vec3_t mviewangles[2]; // during demo playback viewangles is lerped
// between these
vec3_t viewangles;
vec3_t mvelocity[2]; // update by server, used for lean+bob
// (0 is newest)
vec3_t velocity; // lerped between mvelocity[0] and [1]
vec3_t punchangle; // temporary offset
// pitch drifting vars
float idealpitch;
float pitchvel;
qboolean nodrift;
float driftmove;
double laststop;
float viewheight;
float crouch; // local amount for smoothing stepups
qboolean paused; // send over by server
qboolean onground;
qboolean inwater;
int intermission; // don't change view angle, full screen, etc
int completed_time; // latched at intermission start
double mtime[2]; // the timestamp of last two messages
double time; // clients view of time, should be between
// servertime and oldservertime to generate
// a lerp point for other data
double oldtime; // previous cl.time, time-oldtime is used
// to decay light values and smooth step ups
float last_received_message; // (realtime) for net trouble icon
//
// information that is static for the entire time connected to a server
//
struct model_s *model_precache[MAX_MODELS];
struct sfx_s *sound_precache[MAX_SOUNDS];
char levelname[40]; // for display on solo scoreboard
int viewentity; // cl_entitites[cl.viewentity] = player
int maxclients;
int gametype;
// refresh related state
struct model_s *worldmodel; // cl_entitites[0].model
struct efrag_s *free_efrags;
int num_entities; // held in cl_entities array
int num_statics; // held in cl_staticentities array
entity_t viewent; // the gun model
int cdtrack, looptrack; // cd audio
// frag scoreboard
scoreboard_t *scores; // [cl.maxclients]
} client_state_t;
//
// cvars
//
extern cvar_t cl_name;
extern cvar_t cl_color;
extern cvar_t cl_upspeed;
extern cvar_t cl_forwardspeed;
extern cvar_t cl_backspeed;
extern cvar_t cl_sidespeed;
extern cvar_t cl_movespeedkey;
extern cvar_t cl_yawspeed;
extern cvar_t cl_pitchspeed;
extern cvar_t cl_anglespeedkey;
extern cvar_t cl_autofire;
extern cvar_t cl_shownet;
extern cvar_t cl_nolerp;
extern cvar_t cl_pitchdriftspeed;
extern cvar_t lookspring;
extern cvar_t lookstrafe;
extern cvar_t sensitivity;
extern cvar_t m_pitch;
extern cvar_t m_yaw;
extern cvar_t m_forward;
extern cvar_t m_side;
#define MAX_TEMP_ENTITIES 64 // lightning bolts, etc
#define MAX_STATIC_ENTITIES 128 // torches, etc
extern client_state_t cl;
// FIXME, allocate dynamically
extern efrag_t cl_efrags[MAX_EFRAGS];
extern entity_t cl_entities[MAX_EDICTS];
extern entity_t cl_static_entities[MAX_STATIC_ENTITIES];
extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
extern dlight_t cl_dlights[MAX_DLIGHTS];
extern entity_t cl_temp_entities[MAX_TEMP_ENTITIES];
extern beam_t cl_beams[MAX_BEAMS];
//=============================================================================
//
// cl_main
//
dlight_t *CL_AllocDlight(int key);
void CL_DecayLights(void);
void CL_Init(void);
void CL_EstablishConnection(char *host);
void CL_Signon1(void);
void CL_Signon2(void);
void CL_Signon3(void);
void CL_Signon4(void);
void CL_Disconnect(void);
void CL_Disconnect_f(void);
void CL_NextDemo(void);
#define MAX_VISEDICTS 256
extern int cl_numvisedicts;
extern entity_t *cl_visedicts[MAX_VISEDICTS];
//
// cl_input
//
typedef struct {
int down[2]; // key nums holding it down
int state; // low bit is down state
} kbutton_t;
extern kbutton_t in_mlook, in_klook;
extern kbutton_t in_strafe;
extern kbutton_t in_speed;
void CL_InitInput(void);
void CL_SendCmd(void);
void CL_SendMove(usercmd_t *cmd);
void CL_ParseTEnt(void);
void CL_UpdateTEnts(void);
void CL_ClearState(void);
int CL_ReadFromServer(void);
void CL_WriteToServer(usercmd_t *cmd);
void CL_BaseMove(usercmd_t *cmd);
char *Key_KeynumToString(int keynum);
//
// cl_demo.c
//
void CL_StopPlayback(void);
int CL_GetMessage(void);
void CL_Stop_f(void);
void CL_Record_f(void);
void CL_PlayDemo_f(void);
void CL_TimeDemo_f(void);
//
// cl_parse.c
//
void CL_ParseServerMessage(void);
//
// view.c
//
void V_StartPitchDrift(void);
void V_StopPitchDrift(void);
void V_RenderView(void);
void V_UpdatePalette(void);
void V_Register(void);
void V_ParseDamage(void);
void V_SetContentsColor(int contents);
//
// cl_tent
//
void CL_InitTEnts(void);
void CL_SignonReply(void);
#endif /* CLIENT_H */

673
NQ/cmd.c Normal file
View File

@ -0,0 +1,673 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cmd.c -- Quake script command processing module
#include "client.h"
#include "cmd.h"
#include "common.h"
#include "console.h"
#include "host.h"
#include "protocol.h"
#include "quakedef.h"
#include "shell.h"
#include "sys.h"
#include "zone.h"
void Cmd_ForwardToServer(void);
#define MAX_ALIAS_NAME 32
typedef struct cmdalias_s {
struct cmdalias_s *next;
char name[MAX_ALIAS_NAME];
char *value;
} cmdalias_t;
cmdalias_t *cmd_alias;
qboolean cmd_wait;
//=============================================================================
/*
============
Cmd_Wait_f
Causes execution of the remainder of the command buffer to be delayed until
next frame. This allows commands like:
bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
============
*/
void
Cmd_Wait_f(void)
{
cmd_wait = true;
}
/*
=============================================================================
COMMAND BUFFER
=============================================================================
*/
static sizebuf_t cmd_text;
/*
============
Cbuf_Init
============
*/
void
Cbuf_Init(void)
{
SZ_Alloc(&cmd_text, 8192); // space for commands and script files
}
/*
============
Cbuf_AddText
Adds command text at the end of the buffer
============
*/
void
Cbuf_AddText(char *text)
{
int l = strlen(text);
if (cmd_text.cursize + l < cmd_text.maxsize)
SZ_Write(&cmd_text, text, l);
else
Con_Printf("Cbuf_AddText: overflow\n");
}
/*
============
Cbuf_InsertText
Adds command text immediately after the current command
Adds a \n to the text
FIXME: actually change the command buffer to do less copying
============
*/
void
Cbuf_InsertText(char *text)
{
char *temp;
int templen;
// copy off any commands still remaining in the exec buffer
templen = cmd_text.cursize;
if (templen) {
temp = Z_Malloc(templen);
memcpy(temp, cmd_text.data, templen);
SZ_Clear(&cmd_text);
} else
temp = NULL; // shut up compiler
// add the entire text of the file
Cbuf_AddText(text);
SZ_Write(&cmd_text, "\n", 1);
// add the copied off data
if (templen) {
SZ_Write(&cmd_text, temp, templen);
Z_Free(temp);
}
}
/*
============
Cbuf_Execute
============
*/
void
Cbuf_Execute(void)
{
int i;
char *text;
char line[1024];
int quotes;
while (cmd_text.cursize) {
/* find a \n or ; line break */
text = (char *)cmd_text.data;
quotes = 0;
for (i = 0; i < cmd_text.cursize; i++) {
if (text[i] == '"')
quotes++;
if (!(quotes & 1) && text[i] == ';')
break; /* don't break if inside a quoted string */
if (text[i] == '\n')
break;
}
memcpy(line, text, i);
line[i] = 0;
/*
* delete the text from the command buffer and move remaining commands
* down this is necessary because commands (exec, alias) can insert
* data at the beginning of the text buffer
*/
if (i == cmd_text.cursize)
cmd_text.cursize = 0;
else {
i++;
cmd_text.cursize -= i;
memcpy(text, text + i, cmd_text.cursize);
}
/* execute the command line */
Cmd_ExecuteString(line, src_command);
if (cmd_wait) {
/*
* skip out while text still remains in buffer, leaving it for
* next frame
*/
cmd_wait = false;
break;
}
}
}
/*
==============================================================================
SCRIPT COMMANDS
==============================================================================
*/
/*
===============
Cmd_StuffCmds_f
Adds command line parameters as script statements
Commands lead with a +, and continue until a - or another +
quake +prog jctest.qp +cmd amlev1
quake -nosound +cmd amlev1
===============
*/
void
Cmd_StuffCmds_f(void)
{
int i, j;
int s;
char *text, *build, c;
if (Cmd_Argc() != 1) {
Con_Printf("stuffcmds : execute command line parameters\n");
return;
}
// build the combined string to parse from
s = 0;
for (i = 1; i < com_argc; i++) {
if (!com_argv[i])
continue; // NEXTSTEP nulls out -NXHost
s += strlen(com_argv[i]) + 1;
}
if (!s)
return;
text = Z_Malloc(s + 1);
text[0] = 0;
for (i = 1; i < com_argc; i++) {
if (!com_argv[i])
continue; // NEXTSTEP nulls out -NXHost
strcat(text, com_argv[i]);
if (i != com_argc - 1)
strcat(text, " ");
}
// pull out the commands
build = Z_Malloc(s + 1);
build[0] = 0;
for (i = 0; i < s - 1; i++) {
if (text[i] == '+') {
i++;
for (j = i;
(text[j] != '+') && (text[j] != '-') && (text[j] != 0); j++);
c = text[j];
text[j] = 0;
strcat(build, text + i);
strcat(build, "\n");
text[j] = c;
i = j - 1;
}
}
if (build[0])
Cbuf_InsertText(build);
Z_Free(text);
Z_Free(build);
}
/*
===============
Cmd_Exec_f
===============
*/
void
Cmd_Exec_f(void)
{
char *f;
int mark;
if (Cmd_Argc() != 2) {
Con_Printf("exec <filename> : execute a script file\n");
return;
}
// FIXME: is this safe freeing the hunk here???
mark = Hunk_LowMark();
f = (char *)COM_LoadHunkFile(Cmd_Argv(1));
if (!f) {
Con_Printf("couldn't exec %s\n", Cmd_Argv(1));
return;
}
Con_Printf("execing %s\n", Cmd_Argv(1));
Cbuf_InsertText(f);
Hunk_FreeToLowMark(mark);
}
/*
===============
Cmd_Echo_f
Just prints the rest of the line to the console
===============
*/
void
Cmd_Echo_f(void)
{
int i;
for (i = 1; i < Cmd_Argc(); i++)
Con_Printf("%s ", Cmd_Argv(i));
Con_Printf("\n");
}
/*
===============
Cmd_Alias_f
Creates a new command that executes a command string (possibly ; seperated)
===============
*/
char *
CopyString(char *in)
{
char *out;
out = Z_Malloc(strlen(in) + 1);
strcpy(out, in);
return out;
}
void
Cmd_Alias_f(void)
{
cmdalias_t *a;
char cmd[1024];
int i, c;
char *s;
if (Cmd_Argc() == 1) {
Con_Printf("Current alias commands:\n");
for (a = cmd_alias; a; a = a->next)
Con_Printf("%s : %s\n", a->name, a->value);
return;
}
s = Cmd_Argv(1);
if (strlen(s) >= MAX_ALIAS_NAME) {
Con_Printf("Alias name is too long\n");
return;
}
// if the alias already exists, reuse it
for (a = cmd_alias; a; a = a->next) {
if (!strcmp(s, a->name)) {
Z_Free(a->value);
break;
}
}
if (!a) {
a = Z_Malloc(sizeof(cmdalias_t));
a->next = cmd_alias;
cmd_alias = a;
strcpy(a->name, s);
insert_alias_completion(a->name);
}
// copy the rest of the command line
cmd[0] = 0; // start out with a null string
c = Cmd_Argc();
for (i = 2; i < c; i++) {
strcat(cmd, Cmd_Argv(i));
if (i != c)
strcat(cmd, " ");
}
strcat(cmd, "\n");
a->value = CopyString(cmd);
}
/*
=============================================================================
COMMAND EXECUTION
=============================================================================
*/
typedef struct cmd_function_s {
struct cmd_function_s *next;
char *name;
xcommand_t function;
} cmd_function_t;
#define MAX_ARGS 80
static int cmd_argc;
static char *cmd_argv[MAX_ARGS];
static char *cmd_null_string = "";
static char *cmd_args = NULL;
cmd_source_t cmd_source;
static cmd_function_t *cmd_functions; // possible commands to execute
/*
============
Cmd_Init
============
*/
void
Cmd_Init(void)
{
//
// register our commands
//
Cmd_AddCommand("stuffcmds", Cmd_StuffCmds_f);
Cmd_AddCommand("exec", Cmd_Exec_f);
Cmd_AddCommand("echo", Cmd_Echo_f);
Cmd_AddCommand("alias", Cmd_Alias_f);
Cmd_AddCommand("cmd", Cmd_ForwardToServer);
Cmd_AddCommand("wait", Cmd_Wait_f);
}
/*
============
Cmd_Argc
============
*/
int
Cmd_Argc(void)
{
return cmd_argc;
}
/*
============
Cmd_Argv
============
*/
char *
Cmd_Argv(int arg)
{
if (arg >= cmd_argc)
return cmd_null_string;
return cmd_argv[arg];
}
/*
============
Cmd_Args
Returns a single string containing argv(1) to argv(argc()-1)
============
*/
char *
Cmd_Args(void)
{
// FIXME - check necessary?
if (!cmd_args)
return "";
return cmd_args;
}
/*
============
Cmd_TokenizeString
Parses the given string into command line tokens.
============
*/
void
Cmd_TokenizeString(char *text)
{
int i;
// clear the args from the last string
for (i = 0; i < cmd_argc; i++)
Z_Free(cmd_argv[i]);
cmd_argc = 0;
cmd_args = NULL;
while (1) {
// skip whitespace up to a /n
while (*text && *text <= ' ' && *text != '\n') {
text++;
}
if (*text == '\n') { // a newline seperates commands in the buffer
text++;
break;
}
if (!*text)
return;
if (cmd_argc == 1)
cmd_args = text;
text = COM_Parse(text);
if (!text)
return;
if (cmd_argc < MAX_ARGS) {
cmd_argv[cmd_argc] = Z_Malloc(strlen(com_token) + 1);
strcpy(cmd_argv[cmd_argc], com_token);
cmd_argc++;
}
}
}
// ---------------------------------------------------------------------------
/*
============
Cmd_AddCommand
============
*/
void
Cmd_AddCommand(char *cmd_name, xcommand_t function)
{
cmd_function_t *cmd;
if (host_initialized) // because hunk allocation would get stomped
Sys_Error("%s: called after host_initialized", __func__);
// fail if the command is a variable name
if (Cvar_VariableString(cmd_name)[0]) {
Con_Printf("%s: %s already defined as a var\n", __func__, cmd_name);
return;
}
// fail if the command already exists
for (cmd = cmd_functions; cmd; cmd = cmd->next) {
if (!strcmp(cmd_name, cmd->name)) {
Con_Printf("%s: %s already defined\n", __func__, cmd_name);
return;
}
}
cmd = Hunk_Alloc(sizeof(cmd_function_t));
cmd->name = cmd_name;
cmd->function = function;
cmd->next = cmd_functions;
cmd_functions = cmd;
insert_command_completion(cmd_name);
}
/*
============
Cmd_Exists
============
*/
qboolean
Cmd_Exists(char *cmd_name)
{
cmd_function_t *cmd;
for (cmd = cmd_functions; cmd; cmd = cmd->next) {
if (!strcmp(cmd_name, cmd->name))
return true;
}
return false;
}
/*
===================
Cmd_ForwardToServer
Sends the entire command line over to the server
===================
*/
void
Cmd_ForwardToServer(void)
{
if (cls.state < ca_connected) {
Con_Printf("Can't \"%s\", not connected\n", Cmd_Argv(0));
return;
}
if (cls.demoplayback)
return; // not really connected
MSG_WriteByte(&cls.message, clc_stringcmd);
if (strcasecmp(Cmd_Argv(0), "cmd") != 0) {
SZ_Print(&cls.message, Cmd_Argv(0));
SZ_Print(&cls.message, " ");
}
if (Cmd_Argc() > 1)
SZ_Print(&cls.message, Cmd_Args());
else
SZ_Print(&cls.message, "\n");
}
/*
============
Cmd_ExecuteString
A complete command line has been parsed, so try to execute it
FIXME: lookupnoadd the token to speed search?
============
*/
void
Cmd_ExecuteString(char *text, cmd_source_t src)
{
cmd_function_t *cmd;
cmdalias_t *a;
cmd_source = src;
Cmd_TokenizeString(text);
// execute the command line
if (!Cmd_Argc())
return; // no tokens
// check functions
for (cmd = cmd_functions; cmd; cmd = cmd->next) {
if (!strcasecmp(cmd_argv[0], cmd->name)) {
cmd->function();
return;
}
}
// check alias
for (a = cmd_alias; a; a = a->next) {
if (!strcasecmp(cmd_argv[0], a->name)) {
Cbuf_InsertText(a->value);
return;
}
}
// check cvars
if (!Cvar_Command())
Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(0));
}
/*
================
Cmd_CheckParm
Returns the position (1 to argc-1) in the command's argument list
where the given parameter apears, or 0 if not present
================
*/
int
Cmd_CheckParm(char *parm)
{
int i;
if (!parm)
Sys_Error("Cmd_CheckParm: NULL");
for (i = 1; i < Cmd_Argc(); i++)
if (!strcasecmp(parm, Cmd_Argv(i)))
return i;
return 0;
}

136
NQ/cmd.h Normal file
View File

@ -0,0 +1,136 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef CMD_H
#define CMD_H
#include "qtypes.h"
// cmd.h -- Command buffer and command execution
//===========================================================================
/*
Any number of commands can be added in a frame, from several different sources.
Most commands come from either keybindings or console line input, but remote
servers can also send across commands and entire text files can be execed.
The + command line options are also added to the command buffer.
The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute ();
*/
void Cbuf_Init(void);
// allocates an initial text buffer that will grow as needed
void Cbuf_AddText(char *text);
// as new commands are generated from the console or keybindings,
// the text is added to the end of the command buffer.
void Cbuf_InsertText(char *text);
// when a command wants to issue other commands immediately, the text is
// inserted at the beginning of the buffer, before any remaining unexecuted
// commands.
void Cbuf_Execute(void);
// Pulls off \n terminated lines of text from the command buffer and sends
// them through Cmd_ExecuteString. Stops when the buffer is empty.
// Normally called once per frame, but may be explicitly invoked.
// Do not call inside a command function!
//===========================================================================
/*
Command execution takes a null terminated string, breaks it into tokens,
then searches for a command or variable that matches the first token.
// FIXME - this comment section and enum below not in QW (different behaviour)
// - comment in QW explains it grep for QW_CMD_SOURCE
Commands can come from three sources, but the handler functions may choose
to dissallow the action or forward it to a remote server if the source is
not apropriate.
*/
typedef void (*xcommand_t) (void);
typedef enum {
src_client, // came in over a net connection as a clc_stringcmd
// host_client will be valid during this state.
src_command // from the command buffer
} cmd_source_t;
extern cmd_source_t cmd_source;
void Cmd_Init(void);
void Cmd_AddCommand(char *cmd_name, xcommand_t function);
// called by the init functions of other parts of the program to
// register commands and functions to call for them.
// The cmd_name is referenced later, so it should not be in temp memory
qboolean Cmd_Exists(char *cmd_name);
// used by the cvar code to check for cvar / command name overlap
int Cmd_Argc(void);
char *Cmd_Argv(int arg);
char *Cmd_Args(void);
// The functions that execute commands get their parameters with these
// functions. Cmd_Argv () will return an empty string, not a NULL
// if arg > argc, so string operations are allways safe.
int Cmd_CheckParm(char *parm);
// Returns the position (1 to argc-1) in the command's argument list
// where the given parameter apears, or 0 if not present
void Cmd_TokenizeString(char *text);
// Takes a null terminated string. Does not need to be /n terminated.
// breaks the string up into arg tokens.
void Cmd_ExecuteString(char *text, cmd_source_t src);
// Parses a single line of text into arguments and tries to execute it.
// The text can come from the command buffer, a remote client, or stdin.
void Cmd_ForwardToServer(void);
// adds the current command line as a clc_stringcmd to the client message.
// things like godmode, noclip, etc, are commands directed to the server,
// so when they are typed in at the console, they will need to be forwarded.
void Cmd_Print(char *text);
// used by command functions to send output to either the graphics console or
// passed as a print message to the client
#endif /* CMD_H */

1680
NQ/common.c Normal file

File diff suppressed because it is too large Load Diff

173
NQ/common.h Normal file
View File

@ -0,0 +1,173 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// common.h -- general definitions
// FIXME - split this up better?
#ifndef COMMON_H
#define COMMON_H
#include <stdio.h>
#include "quakedef.h"
#include "qtypes.h"
#define stringify__(x) #x
#define stringify(x) stringify__(x)
//============================================================================
typedef struct sizebuf_s {
qboolean allowoverflow; // if false, do a Sys_Error
qboolean overflowed; // set to true if the buffer size failed
byte *data;
int maxsize;
int cursize;
} sizebuf_t;
void SZ_Alloc(sizebuf_t *buf, int startsize);
void SZ_Free(sizebuf_t *buf);
void SZ_Clear(sizebuf_t *buf);
void *SZ_GetSpace(sizebuf_t *buf, int length);
void SZ_Write(sizebuf_t *buf, void *data, int length);
void SZ_Print(sizebuf_t *buf, char *data); // strcats onto the sizebuf
//============================================================================
typedef struct link_s {
struct link_s *prev, *next;
} link_t;
void ClearLink(link_t *l);
void RemoveLink(link_t *l);
void InsertLinkBefore(link_t *l, link_t *before);
void InsertLinkAfter(link_t *l, link_t *after);
// (type *)STRUCT_FROM_LINK(link_t *link, type, member)
// ent = STRUCT_FROM_LINK(link,entity_t,order)
// FIXME: remove this mess!
#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m)))
//============================================================================
#ifndef NULL
#define NULL ((void *)0)
#endif
#define Q_MAXCHAR ((char)0x7f)
#define Q_MAXSHORT ((short)0x7fff)
#define Q_MAXINT ((int)0x7fffffff)
#define Q_MAXLONG ((int)0x7fffffff)
#define Q_MAXFLOAT ((int)0x7fffffff)
#define Q_MINCHAR ((char)0x80)
#define Q_MINSHORT ((short)0x8000)
#define Q_MININT ((int)0x80000000)
#define Q_MINLONG ((int)0x80000000)
#define Q_MINFLOAT ((int)0x7fffffff)
//============================================================================
extern qboolean bigendien;
extern short (*BigShort) (short l);
extern short (*LittleShort) (short l);
extern int (*BigLong) (int l);
extern int (*LittleLong) (int l);
extern float (*BigFloat) (float l);
extern float (*LittleFloat) (float l);
//============================================================================
void MSG_WriteChar(sizebuf_t *sb, int c);
void MSG_WriteByte(sizebuf_t *sb, int c);
void MSG_WriteShort(sizebuf_t *sb, int c);
void MSG_WriteLong(sizebuf_t *sb, int c);
void MSG_WriteFloat(sizebuf_t *sb, float f);
void MSG_WriteString(sizebuf_t *sb, char *s);
void MSG_WriteCoord(sizebuf_t *sb, float f);
void MSG_WriteAngle(sizebuf_t *sb, float f);
extern int msg_readcount;
extern qboolean msg_badread; // set if a read goes beyond end of message
void MSG_BeginReading(void);
int MSG_ReadChar(void);
int MSG_ReadByte(void);
int MSG_ReadShort(void);
int MSG_ReadLong(void);
float MSG_ReadFloat(void);
char *MSG_ReadString(void);
float MSG_ReadCoord(void);
float MSG_ReadAngle(void);
//============================================================================
int Q_atoi(const char *str);
float Q_atof(const char *str);
//============================================================================
extern char com_token[1024];
extern qboolean com_eof;
char *COM_Parse(char *data);
extern unsigned com_argc;
extern char **com_argv;
unsigned COM_CheckParm(char *parm);
void COM_Init(char *path);
void COM_InitArgv(int argc, char **argv);
char *COM_SkipPath(char *pathname);
void COM_StripExtension(char *in, char *out);
void COM_FileBase(const char *in, char *out);
void COM_DefaultExtension(char *path, char *extension);
char *va(char *format, ...);
// does a varargs printf into a temp buffer
//============================================================================
extern int com_filesize;
struct cache_user_s;
extern char com_gamedir[MAX_OSPATH];
void COM_WriteFile(const char *filename, const void *data, int len);
int COM_OpenFile(const char *filename, int *hndl);
int COM_FOpenFile(const char *filename, FILE **file);
void COM_CloseFile(int h);
byte *COM_LoadStackFile(const char *path, void *buffer, int bufsize,
unsigned long *length);
byte *COM_LoadTempFile(const char *path);
byte *COM_LoadHunkFile(const char *path);
void COM_LoadCacheFile(const char *path, struct cache_user_s *cu);
extern struct cvar_s registered;
extern qboolean standard_quake, rogue, hipnotic;
#endif /* COMMON_H */

353
NQ/conproc.c Normal file
View File

@ -0,0 +1,353 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//
// conproc.c
//
// Stuff for the Win32 console (dedicated server only I guess)
//
#include <windows.h>
#include <ctype.h>
#include "console.h"
#include "conproc.h"
#include "quakedef.h"
HANDLE heventDone;
HANDLE hfileBuffer;
HANDLE heventChildSend;
HANDLE heventParentSend;
HANDLE hStdout;
HANDLE hStdin;
DWORD RequestProc(DWORD dwNichts);
LPVOID GetMappedBuffer(HANDLE hfileBuffer);
void ReleaseMappedBuffer(LPVOID pBuffer);
BOOL GetScreenBufferLines(int *piLines);
BOOL SetScreenBufferLines(int iLines);
BOOL ReadText(LPTSTR pszText, int iBeginLine, int iEndLine);
BOOL WriteText(LPCTSTR szText);
int CharToCode(char c);
BOOL SetConsoleCXCY(HANDLE hStdout, int cx, int cy);
void
InitConProc(HANDLE hFile, HANDLE heventParent, HANDLE heventChild)
{
DWORD dwID;
// ignore if we don't have all the events.
if (!hFile || !heventParent || !heventChild)
return;
hfileBuffer = hFile;
heventParentSend = heventParent;
heventChildSend = heventChild;
// so we'll know when to go away.
heventDone = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!heventDone) {
Con_SafePrintf("Couldn't create heventDone\n");
return;
}
if (!CreateThread(NULL,
0, (LPTHREAD_START_ROUTINE) RequestProc, 0, 0, &dwID)) {
CloseHandle(heventDone);
Con_SafePrintf("Couldn't create QHOST thread\n");
return;
}
// save off the input/output handles.
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
// force 80 character width, at least 25 character height
SetConsoleCXCY(hStdout, 80, 25);
}
void
DeinitConProc(void)
{
if (heventDone)
SetEvent(heventDone);
}
DWORD
RequestProc(DWORD dwNichts)
{
int *pBuffer;
DWORD dwRet;
HANDLE heventWait[2];
int iBeginLine, iEndLine;
heventWait[0] = heventParentSend;
heventWait[1] = heventDone;
while (1) {
dwRet = WaitForMultipleObjects(2, heventWait, FALSE, INFINITE);
// heventDone fired, so we're exiting.
if (dwRet == WAIT_OBJECT_0 + 1)
break;
pBuffer = (int *)GetMappedBuffer(hfileBuffer);
// hfileBuffer is invalid. Just leave.
if (!pBuffer) {
Con_SafePrintf("Invalid hfileBuffer\n");
break;
}
switch (pBuffer[0]) {
case CCOM_WRITE_TEXT:
// Param1 : Text
pBuffer[0] = WriteText((LPCTSTR)(pBuffer + 1));
break;
case CCOM_GET_TEXT:
// Param1 : Begin line
// Param2 : End line
iBeginLine = pBuffer[1];
iEndLine = pBuffer[2];
pBuffer[0] = ReadText((LPTSTR)(pBuffer + 1), iBeginLine,
iEndLine);
break;
case CCOM_GET_SCR_LINES:
// No params
pBuffer[0] = GetScreenBufferLines(&pBuffer[1]);
break;
case CCOM_SET_SCR_LINES:
// Param1 : Number of lines
pBuffer[0] = SetScreenBufferLines(pBuffer[1]);
break;
}
ReleaseMappedBuffer(pBuffer);
SetEvent(heventChildSend);
}
return 0;
}
LPVOID
GetMappedBuffer(HANDLE hfileBuffer)
{
LPVOID pBuffer;
pBuffer = MapViewOfFile(hfileBuffer,
FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
return pBuffer;
}
void
ReleaseMappedBuffer(LPVOID pBuffer)
{
UnmapViewOfFile(pBuffer);
}
BOOL
GetScreenBufferLines(int *piLines)
{
CONSOLE_SCREEN_BUFFER_INFO info;
BOOL bRet;
bRet = GetConsoleScreenBufferInfo(hStdout, &info);
if (bRet)
*piLines = info.dwSize.Y;
return bRet;
}
BOOL
SetScreenBufferLines(int iLines)
{
return SetConsoleCXCY(hStdout, 80, iLines);
}
BOOL
ReadText(LPTSTR pszText, int iBeginLine, int iEndLine)
{
COORD coord;
DWORD dwRead;
BOOL bRet;
coord.X = 0;
coord.Y = iBeginLine;
bRet = ReadConsoleOutputCharacter(hStdout,
pszText,
80 * (iEndLine - iBeginLine + 1),
coord, &dwRead);
// Make sure it's null terminated.
if (bRet)
pszText[dwRead] = '\0';
return bRet;
}
BOOL
WriteText(LPCTSTR szText)
{
DWORD dwWritten;
INPUT_RECORD rec;
char upper, *sz;
sz = (LPTSTR)szText;
while (*sz) {
// 13 is the code for a carriage return (\n) instead of 10.
if (*sz == 10)
*sz = 13;
upper = toupper(*sz);
rec.EventType = KEY_EVENT;
rec.Event.KeyEvent.bKeyDown = TRUE;
rec.Event.KeyEvent.wRepeatCount = 1;
rec.Event.KeyEvent.wVirtualKeyCode = upper;
rec.Event.KeyEvent.wVirtualScanCode = CharToCode(*sz);
rec.Event.KeyEvent.uChar.AsciiChar = *sz;
rec.Event.KeyEvent.uChar.UnicodeChar = *sz;
rec.Event.KeyEvent.dwControlKeyState = isupper(*sz) ? 0x80 : 0x0;
WriteConsoleInput(hStdin, &rec, 1, &dwWritten);
rec.Event.KeyEvent.bKeyDown = FALSE;
WriteConsoleInput(hStdin, &rec, 1, &dwWritten);
sz++;
}
return TRUE;
}
int
CharToCode(char c)
{
char upper;
upper = toupper(c);
switch (c) {
case 13:
return 28;
default:
break;
}
if (isalpha(c))
return (30 + upper - 65);
if (isdigit(c))
return (1 + upper - 47);
return c;
}
BOOL
SetConsoleCXCY(HANDLE hStdout, int cx, int cy)
{
CONSOLE_SCREEN_BUFFER_INFO info;
COORD coordMax;
coordMax = GetLargestConsoleWindowSize(hStdout);
if (cy > coordMax.Y)
cy = coordMax.Y;
if (cx > coordMax.X)
cx = coordMax.X;
if (!GetConsoleScreenBufferInfo(hStdout, &info))
return FALSE;
// height
info.srWindow.Left = 0;
info.srWindow.Right = info.dwSize.X - 1;
info.srWindow.Top = 0;
info.srWindow.Bottom = cy - 1;
if (cy < info.dwSize.Y) {
if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
return FALSE;
info.dwSize.Y = cy;
if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
return FALSE;
} else if (cy > info.dwSize.Y) {
info.dwSize.Y = cy;
if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
return FALSE;
if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
return FALSE;
}
if (!GetConsoleScreenBufferInfo(hStdout, &info))
return FALSE;
// width
info.srWindow.Left = 0;
info.srWindow.Right = cx - 1;
info.srWindow.Top = 0;
info.srWindow.Bottom = info.dwSize.Y - 1;
if (cx < info.dwSize.X) {
if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
return FALSE;
info.dwSize.X = cx;
if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
return FALSE;
} else if (cx > info.dwSize.X) {
info.dwSize.X = cx;
if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
return FALSE;
if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
return FALSE;
}
return TRUE;
}

44
NQ/conproc.h Normal file
View File

@ -0,0 +1,44 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef CONPROC_H
#define CONPROC_H
#include <windows.h>
// conproc.h
#define CCOM_WRITE_TEXT 0x2
// Param1 : Text
#define CCOM_GET_TEXT 0x3
// Param1 : Begin line
// Param2 : End line
#define CCOM_GET_SCR_LINES 0x4
// No params
#define CCOM_SET_SCR_LINES 0x5
// Param1 : Number of lines
void InitConProc(HANDLE hFile, HANDLE heventParent, HANDLE heventChild);
void DeinitConProc(void);
#endif /* CONPROC_H */

1074
NQ/d_polyse.c Normal file

File diff suppressed because it is too large Load Diff

487
NQ/data/COMEXP.TXT Normal file
View File

@ -0,0 +1,487 @@
COMMERCIAL EXPLOITATION LICENSE AGREEMENT FOR QUAKE
This Commercial Exploitation License Agreement for QUAKE
(the "Agreement") is between Id Software, Inc., a Texas
Corporation, (hereinafter "Id Software") and Licensee (as described
on the signature page hereof) and is made effective beginning on
the date of last signature hereto (the "Effective Date").
R E C I T A L S
WHEREAS, Id Software is the owner and developer of the
computer software game entitled QUAKE;
WHEREAS, Id Software desires to license certain
non-exclusive rights regarding QUAKE to Licensee; and
WHEREAS, Licensee desires to receive a license for such
rights.
T E R M S A N D C O N D I T I O N S
NOW, THEREFORE, for and in consideration of the mutual
premises contained herein and for other good and valuable
consideration, the receipt and sufficiency of which is hereby
acknowledged, the undersigned parties do hereby agree as follows:
1. DEFINITIONS. As used in this Agreement, the parties
hereto agree the words set forth below shall have the specified
meanings:
a. "Authorized Copy" shall mean one (1) copy of the
Subject Game actually purchased by Licensee from an
Id Software approved retailer; and
b. "Subject Game" shall mean the full registered
version of QUAKE on a CD-ROM and shall not mean the
shareware or any other version.
2. GRANT OF RIGHTS. Id Software hereby grants to
Licensee and Licensee hereby accepts, subject to the provisions and
conditions hereof, a world-wide (except as otherwise provided
herein), non-exclusive, non-transferable, and non-assignable
license to:
a. publicly display an Authorized Copy in exchange for
rental payment;
b. run the Authorized Copy so that it will accept
network/modem connections in exchange for payments
from end-users who also must have actually purchased
an Authorized Copy; and
c. otherwise commercially exploit an Authorized Copy,
except that Licensee shall not copy, reproduce,
manufacture or distribute the Authorized Copy.
3. RESERVATION OF RIGHTS AND PROHIBITIONS. Id Software
expressly reserves all rights not granted herein. Under no
circumstances shall Licensee copy, reproduce, manufacture or
distribute (free of charge or otherwise) the Authorized Copy or the
Subject Game. Licensee shall not reverse engineer, decompile,
disassemble, modify or alter the Authorized Copy. Licensee is not
receiving any rights hereunder regarding the Trademark or any
artwork, sound, music or other element of the Subject Game.
4. OWNERSHIP. Title to and all ownership rights in and
to the Subject Game, and the QUAKE Trademark (the "Trademark") and
the copyrights, trademarks, patents and other intellectual property
rights related thereto shall remain with Id Software which shall have
the exclusive right to protect the same by copyright or otherwise.
Licensee shall have no ownership rights in or to the Subject Game or
the Trademark and Licensee shall not own any intellectual property
rights regarding the Authorized Copy, including, without limitation,
the copyright regarding the Authorized Copy. Licensee acknowledges
that it only has a limited license to use the Authorized Copy, as
specified in that certain QUAKE Enduser License contained within the
Authorized Copy and as specified in this Agreement.
5. TERM AND TERMINATION.
a. The term of this Agreement and the license granted
herein begins on the Effective Date and shall expire on a date one
(1) calendar year from the Effective Date.
b. Either party may terminate this Agreement, for any
reason or no reason, on thirty (30) days written notice to the
other party. Termination will be effective on the thirtieth (30th)
day following delivery of the described notice. Notwithstanding
anything to the contrary herein, this Agreement shall immediately
terminate, without the requirement of any notice from Id Software
to Licensee, upon the occurrence of any of the following: (a) if
Licensee shall file a petition in bankruptcy or make an assignment
for the benefit of creditors, or if any bankruptcy proceeding or
assignment for benefit of creditors, shall be commenced against
Licensee and not be dismissed within sixty (60) days after the date
of its commencement; (b) the insolvency of Licensee; (c) the
cessation by Licensee of its business; or (d) the cessation by
Licensee, without the prior written consent of Id Software of the
distribution, manufacture, and sale responsibilities embodied
herein. Further, Id Software may elect to terminate this Agreement
upon the occurrence of any of the following: (1) if Licensee's
business operations are interrupted for forty (40) consecutive
calendar days; or (2) if each of two Id Software audit inspections
during any eighteen (18) month period demonstrates an
understatement by Licensee of Royalty payments due Id Software for
the six (6) month period immediately preceding each such inspection
of five percent (5%) or more. Upon the occurrence of such
terminating event, and the election of Id Software, if necessary,
to cause such termination, this Agreement and any and all rights
thereunder shall terminate without prejudice to any rights or
claims Id Software may have, and all rights hereunder shall
thereupon terminate, revert to and be vested in Id Software.
6. EFFECT OF TERMINATION OR EXPIRATION. Termination or
expiration of this Agreement, either by Id Software or
automatically, shall not create any liability against Id Software.
Upon expiration or earlier termination of this Agreement, Licensee
shall have no further right to exercise the rights licensed
hereunder or otherwise acquired in relation to this Agreement.
7. INDEMNIFICATION. Licensee hereby agrees to
indemnify, hold harmless and defend Id Software and Id Software's
predecessors, successors, assigns, officers, directors,
shareholders, employees, agents, representatives, licensees,
sublicensees, distributors, attorneys and accountants
(collectively, the "Id Related Parties") from and against any and
all damages, claims, losses, causes of action, liabilities,
lawsuits, judgments and expenses (including, without limitation,
reasonable attorneys' fees and expenses) arising from, relating to
or in connection with a breach of this Agreement by Licensee and
arising from, relating to or in connection with the Licensee's use
or non-use of the Authorized Copy (collectively, the "Claims"). Id
Software agrees to notify Licensee of any such Claims within a
reasonable time after Id Software learns of same. Licensee, at its
own expense, shall defend Id Software and the Id Related Parties
from any and all Claims. Id Software and the Id Related Parties
reserve the right to participate in any defense of the Claims with
counsel of their choice, and at their own expense. In the event
Licensee fails to provide a defense, then Licensee shall be
responsible for paying the attorneys' fees and expenses incurred by
Id Software and the Id Related Parties regarding the defense of the
Claims. Id Software and the Id Related Parties, as applicable,
agree to reasonably assist in the defense of the Claims. No
settlement by Licensee of any Claims shall be valid unless Licensee
receives the prior written consent of Id Software and the Id
Related Parties, as applicable, to any such settlement.
8. CONFIDENTIALITY. It is understood and agreed that
any proprietary information of Id Software that may from time to
time be made available or become known to Licensee is to be treated
as confidential, is to be used solely in connection with Licensee's
performance under this Agreement, and is to be disclosed only to
employees of Licensee who have a need for access. Such proprietary
information shall include, but not be limited to, trade secrets,
release information, financial information, personnel information,
and the like. Reasonable measures shall be taken by Licensee to
protect the confidentiality of Id Software's proprietary
information and any memoranda or papers containing proprietary
information of Id Software's that Licensee may receive are to be
returned to Id Software upon request. Licensee's obligations and
duties under this paragraph shall survive expiration or earlier
termination of this Agreement. Licensee shall obtain from its
employees an undertaking in a form which may be supplied by Id
Software, and which is subject to Id Software's prior written
approval, not to use or disclose to any third party any information
or knowledge concerning the business of Id Software which may be
communicated to such employees.
9. LIMITATION OF LIABILITY. ID SOFTWARE EXPRESSLY
DISCLAIMS ALL WARRANTIES NOT PROVIDED BY ID SOFTWARE HEREUNDER.
UNDER NO CIRCUMSTANCES SHALL ID SOFTWARE BE LIABLE TO LICENSEE FOR
ACTUAL, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES OR
ANY OTHER DAMAGES, WHETHER OR NOT ID SOFTWARE RECEIVES NOTICE OF
ANY SUCH DAMAGES.
10. COMPLIANCE WITH APPLICABLE LAWS. In performing
under this Agreement, Licensee agrees to comply with all applicable
laws, [including, without limitation, 22 U.S.C., 2778 and 22
U.S.C. C.F.R. Parts 120-130 (1995)] regulations, ordinances and
statutes, including, but not limited to, the import/export laws and
regulations of the United States and its governmental and
regulatory agencies (including, without limitation, the Bureau of
Export Administration and the U.S. Department of Commerce) and all
applicable international treaties and laws. Further, Licensee
shall defend, indemnify and hold harmless Id Software from any and
all sales tax, tariffs and/or duties in connection with Licensee's
performance hereunder.
11. SPECIFIC UNDERTAKINGS BY LICENSEE. In addition to
the obligations of Licensee otherwise set forth in this Agreement,
during the term of this Agreement, and thereafter where specified,
Licensee agrees that:
a. It will not attack the title of Id Software to the
Subject Game or the Trademark and any copyright, patent or
trademark or other intellectual property right related thereto and
it will not attack the validity of the license granted hereunder
during the term hereof or thereafter; and
b. It will promptly inform Id Software of any
unauthorized use of the Authorized Copy, the Subject Game and the
Trademark and any portions thereof and reasonably assist Id
Software in the enforcement of any rights Id Software may have
against such unauthorized users.
12. FINANCIAL OBLIGATIONS AND ACCOUNTING.
a. Payment of Royalties. Licensee agrees to pay Id
Software a royalty ("Royalty") at the rate of twelve and one-half
percent (12.5%) of Net Income. The term "Net Income" shall mean
all revenue received by Licensee from the commercial use of the
Authorized Copy, less only Licensee's actual, documented costs
relating directly to such use. A Royalty shall only be due for
those months in which Licensee's gross revenue from QUAKE
distribution exceeds U.S. Five Thousand Dollars ($5,000.00) and in
such months Licensee shall pay a full Royalty on all revenue
received.
b. Rendition of Statements. Licensee shall account to
Id Software with regard to transactions hereunder within forty-five
(45) days following the conclusion of each calendar quarter.
Licensee hereby represents and warrants that such statements of
account to be prepared shall be true and correct. The accounts
shall show in summary form the appropriate calculations relating to
the computation of Royalties, if any. The statements shall also
show the gross revenue received by Licensee per month. The
Royalties payable to Id Software hereunder shall be remitted with
the particular statement indicating such amount to be due. All
statements hereunder shall be deemed rendered when deposited,
postage prepaid, in the United States mail, addressed to Id
Software at Id Software's address set forth on the signature page
hereof.
c. Books of Account and Audits. Licensee shall keep
books of account relating to the commercial use of the Authorized
Copy on the basis of generally accepted accounting principles and
shall maintain such books of account for a period of at least two
(2) years after the expiration or earlier termination of this
Agreement; provided, however, that Licensee shall not be required
to keep such records longer than seven (7) years from their date of
origination. Id Software may, upon reasonable notice and at its
own expense, audit the applicable records at Licensee's office, in
order to verify statements rendered hereunder. Any such audit
shall take place during reasonable business hours and in such
manner so as not to interfere with Licensee's normal business
activities. Id Software agrees that such information inspected
and/or copied on behalf of Id Software hereunder shall be used only
for the purpose of determining the accuracy of the statements, and
shall be revealed only to such officers, directors, employees,
agents and/or representatives of Id Software as necessary to verify
the accuracy of the statements. If in an audit of Licensee's books
and records it is determined that there is a short fall of ten
percent (10%) or more in Royalties reported for any calendar
quarter, in addition to payment of such short fall and interest as
may be due, as provided herein, Licensee shall reimburse Id
Software for the full out-of-pocket costs of the audit including
reasonable travel costs and expenses; provided, however, that the
amount of reimbursement paid by Licensee shall not exceed U.S.
Fifteen Thousand Dollars ($15,000.00) for any audit.
d. Payment of the Royalty. Licensee assumes all risks
associated with fluctuations in foreign currency exchange rates.
Licensee shall pay and agrees to pay all sums due Id Software in
United States Dollars. With respect to Royalties due for
commercial use outside the United States, other currencies shall be
exchanged at the expense of Licensee into United States Dollars
using the bid price quoted at the Citibank, N.A. of New York, New
York, for the purchase of United States Dollars at the close of
business on the last day of the calendar quarter during which any
amounts accrue. Payment of the Royalties shall be made in Dallas
County, Texas.
e. Interest. If Id Software does not receive the
applicable Royalty payment on or before the due date of such
payment, Licensee agrees to pay and shall pay interest on Royalties
owed to Id Software from such date as specified in the following
sentence at a rate per annum equal to the Index Rate. For purposes
of clarification, the interest will begin to accrue on the first
(1st) day following the due date of the Royalty payment, unless the
Royalty payment is paid timely. The "Index Rate" shall be the
prime rate as published in The Wall Street Journal's "Money Rates"
table. If multiple prime rates are quoted in the table, then the
highest prime rate will be the Index Rate. In the event that the
prime rate is no longer published in the "Money Rates" table, then
Id Software will choose a substitute Index Rate which is based upon
comparable information. The applicable interest rate will be
determined and take effect on the first day of each month.
NOTHING HEREIN SHALL BE CONSTRUED AS A REQUEST OR DEMAND BY
ID SOFTWARE OF INTEREST AT A RATE HIGHER THAN ALLOWED BY APPLICABLE
LAW. IT IS THE INTENT OF THE PARTIES HERETO THAT NO INTEREST BE
CHARGED HEREUNDER WHICH EXCEEDS THE MAXIMUM RATE ALLOWED BY
APPLICABLE LAW. IF THE RATE REFERENCED ABOVE EXCEEDS THE MAXIMUM
RATE ALLOWED BY APPLICABLE LAW, THEN THE INTEREST RATE MADE
APPLICABLE HEREIN SHALL BE THE MAXIMUM RATE ALLOWED BY APPLICABLE
LAW.
13. SUBLICENSE. Licensee shall not be entitled to
sublicense any of its rights under this Agreement.
14. GOODWILL. Licensee recognizes the great value of
the goodwill associated with the Subject Game and the Trademark,
and acknowledges that such goodwill, now existing and hereafter
created, exclusively belongs to Id Software and that the Trademark
has acquired a secondary meaning in the mind of the public.
15. REMEDIES. In the event of a breach of this
Agreement by Id Software, Licensee's sole remedy shall be to
terminate this Agreement. In the event of a breach by Licensee of
this Agreement, Id Software may pursue the remedies to which it is
entitled under applicable law, including, but not limited to,
termination of this Agreement. Licensee agrees that its failure to
comply with the terms of this Agreement upon expiration or earlier
termination hereof or Licensee's unauthorized use of the Authorized
Copy may result in immediate and irreparable damage to Id Software
for which there is no adequate remedy at law, and in the event of
such failure by Licensee, Id Software shall be entitled to
injunctive relief. Pursuit of any remedy by Id Software shall not
constitute a waiver of any other right or remedy of Id Software
under this Agreement or under applicable law. Termination of this
Agreement shall not be a pre-condition to Id Software pursuing its
other remedies for breach.
16. LICENSEE'S WARRANTIES. Licensee warrants and
represents that it has full legal rights to enter into this
Agreement and to perform its obligations hereunder and that it will
comply, at all times during the terms of this Agreement, with all
applicable laws, as set forth hereinabove.
17. BANKRUPTCY. If Licensee's liabilities exceed its
assets, or if Licensee becomes unable to pay its debts as they
become due or if Licensee files for voluntary bankruptcy, or is
placed in bankruptcy which is not dissolved or dismissed after
thirty (30) days from the petition filing date, or if Licensee
becomes insolvent, or makes an assignment for the benefit of its
creditors or an arrangement pursuant to any bankruptcy laws or if
Licensee discontinues its business or if a receiver is appointed
for its business, this Agreement shall automatically terminate,
without notice, and become null and void; provided, however, all
duties of Licensee upon termination or expiration of this Agreement
shall continue in full force and effect.
18. ENTIRE AGREEMENT AND ASSIGNMENT. This Agreement
constitutes the entire understanding between Licensee and Id
Software regarding the Subject Game. Each and every clause of this
Agreement is severable from the whole and shall survive unless the
entire Agreement is declared unenforceable. No prior or present
agreements or representations shall be binding upon any of the
parties hereto unless incorporated in this Agreement. No
modification or change in this Agreement shall be valid or binding
upon the parties unless in writing, executed by the parties to be
bound thereby. This Agreement shall bind and inure to the benefit
of Id Software, its successors and assigns, and Id Software may
assign its rights hereunder, in Id Software's sole discretion.
This Agreement is personal to Licensee, and Licensee shall not
sublicense, assign, transfer, convey nor franchise its rights
granted hereunder.
19. CHOICE OF LAW, VENUE AND SERVICE OF PROCESS. This
Agreement shall be construed in accordance with the laws of the
State of Texas and applicable U.S. federal law and all claims
and/or lawsuits in connection with this Agreement must be brought
in Dallas County, Texas. Licensee hereby agrees that service of
process by certified mail to the address set forth below, with
return receipt requested, shall constitute valid service of process
upon Licensee. If for any reason Licensee has moved or cannot be
validly served, then Licensee appoints the Secretary of State of
the state of Texas to accept service of process on Licensee's
behalf.
20. EXCUSED PERFORMANCE. Neither party shall be deemed
to be in default of any provision of this Agreement nor be liable
for any delay, failure in performance or interruption of service,
resulting directly or indirectly from acts of God, civil or
military authority, civil disturbance, military action, war,
strikes, other catastrophes or any other similar cause beyond its
reasonable control. Written notice to the non-affected party of any
such condition shall be given by the affected party within ten (10)
days of the event.
21. DELIVERY OF NOTICES, AND DELIVERY OF PAYMENTS.
Unless otherwise directed in writing by the parties, all notices
given hereunder and all payments made hereunder shall be sent to
the addresses set forth on the signature page hereof. All
notices, requests, consents and other communications under this
Agreement shall be in writing and shall be deemed to have been
delivered on the date personally delivered or on the date deposited
in the United States Postal Service, postage prepaid, by certified
mail, return receipt requested, or telegraphed and confirmed, or
delivered by electronic facsimile and confirmed. Any notice to Id
Software shall also be sent to its counsel: D. Wade Cloud, Jr.,
Hiersche, Martens, Hayward, Drakeley & Urbach, P.C., 15303 Dallas
Parkway, Suite 700, LB 17, Dallas, Texas 75248.
22. NO PARTNERSHIP, ETC. This Agreement does not
constitute and shall not be construed as constituting a partnership
or joint venture between Id Software and Licensee. Neither party
shall have any right to obligate or bind the other party in any
manner whatsoever, and nothing herein contained shall give, or is
intended to give, any rights of any kind to any third persons.
23. COUNTERPARTS. This Agreement may be executed in
several counterparts, each of which will be deemed to be an
original, and each of which alone and all of which together, shall
constitute one and the same instrument, but in making proof of this
Agreement it shall not be necessary to produce or account for each
copy of any counterpart other than the counterpart signed by the
party against whom this Agreement is to be enforced. This
Agreement may be transmitted by facsimile, and it is the intent of
the parties for the facsimile of any autograph printed by a
receiving facsimile machine to be an original signature and for the
facsimile and any complete photocopy of the Agreement to be deemed
an original counterpart.
24. MEDIATION. If a dispute arises out of or relates to
this Agreement, or a breach of this Agreement, and if the dispute
cannot be settled through direct discussion, then the parties agree
to endeavor to settle the dispute in an amicable manner by
mediation, under the applicable provisions of Section 154.00 et
seq., Texas Civil Practices and Remedies Code, as supplemented by
the rules of the Association of Attorney Mediators.
25. SURVIVAL. The following provisions shall survive
the expiration or earlier termination of this Agreement:
paragraphs 4., 7., 8., and the audit rights of Id Software in
paragraph 12.c.
26. MISCELLANEOUS.
a. All captions in this Agreement are intended solely
for the convenience of the parties, and none shall effect the
meaning or construction of any provision.
b. The terms and conditions of this Agreement have been
negotiated fully and freely among the parties. Accordingly, the
preparation of this Agreement by counsel for a given party will not
be material to the construction hereof, and the terms of this
Agreement shall not be strictly construed against such party.
By signing in the spaces provided below, the parties have
agreed to all of the terms and conditions set forth in this
Agreement.
AGREED:
LICENSEE:
Signed:_______________________________
Printed Name:_________________________
Title:________________________________
Address:______________________________
______________________________________
______________________________________
Telephone #: _________________________
Fax #:________________________________
E-Mail Address:_______________________
Date: ________________________________
AGREED:
ID SOFTWARE, INC.
Signed:_______________________________
Printed Name:_________________________
Title:________________________________
Address:______________________________
______________________________________
______________________________________
Telephone #: _________________________
Fax #:________________________________
E-Mail Address:_______________________
Date: ________________________________
June 10, 1996
COMMERCIAL EXPLOITATION LICENSE AGREEMENT FOR QUAKE
(DWC:dw:3406.0299:dwc\doc:5017)

119
NQ/data/HELP.TXT Normal file
View File

@ -0,0 +1,119 @@
TECH SUPPORT
Any of the information listed below could change. Check the id software
Web Site, at www.idsoftware.com, for updates.
A. Tech Support Options
id Software does charge for technical support, but we strive to offer
this service at the lowest cost possible. Because volume on the support
lines dictate costs, we periodically adjust our rates for Voice Tech
Support. Check our web site for current pricing.
Paying for Voice or Automated Support
1 -- You can get Voice Support using a major credit card for a one-time
shot. The system asks for your credit card number and expiration date,
then pre-authorizes your credit card for the tech support call. You will
only be billed for the number of minutes actually used.
2 -- You can assign yourself a rechargeable PIN account. The system prompts
you for your credit card information, and assigns you a PIN account number.
You can use the PIN to access Voice Support, Automated Support and the
Game Hints Line. Once your account runs out, you can charge it up again.
3 -- You may also charge up a PIN account using the number 1 (900) call-2-id.
Then call back at 1(800)ID-GAMES (1(800)434-3627), and use your new PIN to
receive all the support and hints you wish.
4 -- id Software's Game Hints Line is accessible either using a PIN account
via 1 (800) ID-GAMES (see above), or by calling 1 (900) CALL2-ID, which
places the call on your phone bill.
1. Voice Support
Telephone -- 1 (800) id-games
Lines Open from 12 noon to 10pm Central Time, 7 Days a
week ($1.75 per minute). Closed some holidays
Please have the following information handy.
1. Game title and version number. (The version
number can be found on the end text screen.)
2. Your operating system, processor, processor
speed and amount of RAM.
3. If you are having a sound, video or modem
problem, we need to know the device brand name
and model.
2. Automated Support
Telephone -- 1 (800) id-games
Lines Open 24 hours a day, 365 days a year, or 366 days
in Leap years ($0.25 per minute)
Please have pencil and paper handy.
3. E-mail Support
Just send your e-mail to support@idsoftware.com
We will do our best to respond within 48 hours after
receiving your e-mail.
When sending e-mail, cut and paste the following into your
e-mail message and fill in the blanks:
Date:
Name:
Phone number:
E-mail address: (please include this, we redirect tons of mail)
Game Title:
Version #:
Operating system (eg., DOS 6.0 or Windows 95):
Computer type:
Processor type:
Processor speed:
Video card brand and model: (only if video problem)
Audio card brand and model: (only if audio problem)
Modem brand and model: (only if modem problem)
Network card brand and model: (only if netgame problem)
Network configuration (eg., NET.CFG file): (only if netgame problem)
Drivers, protocol stacks, and versions: (eg., lsl v2.14, exp16odi
v2.33, and ipxodi v3.01) (only if netgame problem)
If there were any error messages or fault information, report them
here:
Please state the problem you encountered:
Please state how to reproduce the problem:
4. Web Support
Found at www.idsoftware.com
Our web support pages provide the same information that's
available via Automated Support, except it's free!
5. News Sites
For information, FAQ, or announcements:
rec.games.computer.quake.announce
For editing and hecking Quake-related files:
rec.games.computer.quake.editing
For general Quake discussion:
rec.games.computer.quake.misc
6. Game Hints Line
Telephone -- 1 (800) id-games or 1 (900) call-2-id
Lines Open 24 hours a day, 365 days a year, or 366 days
in Leap years ($0.85 per minute)
You must be 18 years of age or have parental permission
to call 1 (900) call-2-id.
B. In Europe
The help lines in Europe are open 7:30am - 5:00pm GMT,
Monday - Friday.
English: +44 01923 209145
German: +44 (0)1923 209151
French: +44 (0)1923 209148
C. Problems
If you have an unfavorable experience using our services, please
send e-mail to 911@idsoftware.com. We would also like to hear
from you if you have something positive to share with us. Kindly
include your full name, address, phone number, and the problem
encountered or information you'd like to tell us about.

97
NQ/data/LICINFO.TXT Normal file
View File

@ -0,0 +1,97 @@
Here is a brief explanation of all the legal mumbo jumbo contained in the
various license agreements that may or may not be part of this package.
(This document was designed to be a quick overview of our license terms.
You must refer to the full text of the license for a complete listing of
terms and conditions.)
QUAKE SHAREWARE END USER LICENSE (slicnse.txt) or
What You Can and Cannot Do With the Shareware Version of Quake.
CAN DO:
-- Play & Enjoy the single player game
-- Setup a shareware version based server on a not-for-profit basis
CANNOT DO:
-- Run the game with user developed levels.
-- You may not commercially exploit the shareware version in any way
This specifically excludes retail distribution of the shareware
version. Do not call or e-mail to ask if you can be a retail
distributor of the shareware version -- the answer is no!
-- Commercially exploit any id copyrighted and/or trademarked property.
Example: Game names, logos, graphics, etc.
QUAKE REGISTERED VERSION END USER LICENSE (rlicnse.txt) or
What You Can and Cannot Do With the Registered Version of Quake.
CAN DO:
-- Play & Enjoy the single player game
-- Setup a registered version based server on a not-for-profit basis
-- Develop new levels and/or level creation utilities.
-- Play the game and/or setup a Registered Version based server using
a user-developed level.
CANNOT DO:
-- Commercially exploit the Registered Version of Quake in any way;
see commercially exploitation license info below.
-- Commercially exploit any id copyrighted and/or trademarked
property.
Example: Game names, logos, game graphics, etc.
-- Sell user-developed levels and/or tools
COMMERCIAL EXPLOITATION LICENSE (comexp.txt -- accompanies Quake
registered version only)
If you are interested in trying to make money using the registered version
of Quake (this sort of thing is not allowed using the shareware version) you
must sign our easy-to-digest Commercial Exploitation License.
This is a royalty free license that allows you to run Quake for a profit
through a certain monthly gross profit range. If your Quake-related business
becomes successful the agreement brings id into the revenue stream.
Basic terms of the commercial exploitation license:
-- License grants a royalty free commercial exploitation right for the
registered version of Quake as a whole so long as Quake's monthly gross
revenue is below $5,000.00
-- License provides for a 12.5% royalty to be paid to id Software in months
where the licensee's Quake related monthly gross revenue is above $5,000.00
-- Royalty is based off net income. Net income is defined as Quake-related
gross income less Quake-related expenses.
-- License expressly prohibits commercial exploitation via the sale (retail
or otherwise) of the shareware or registered versions of Quake.
-- License expressly prohibits advertising/marketing use of our copyrighted
and/or trademarked properties.
To get into bed with us on this deal you must print two (2) copies of the
document named comexp.txt. (You should find comexp.txt somewhere on the
registered version CD.) Sign/fill in the blanks of both copies where
indicated and mail both to:
id Software
18601 LBJ #666
Mesquite, TX 75150
Attn: ComExp License
We will then countersign the documents and mail one back to you.
Two items worth noting here:
1. It is VERY IMPORTANT that the information you enter in the signature
block be legible. We prefer it if you enter the info into the blanks before
printing your two copies. If we cannot read your information we will not be
able to return the documents to you.
2. The terms of this document are not subject to negotiation. If you cannot
live with the terms spelled out in the agreement do not engage in any
commercial exploitation of Quake and do not sign the document.

1030
NQ/data/MANUAL.TXT Normal file

File diff suppressed because it is too large Load Diff

103
NQ/data/ORDER.TXT Normal file
View File

@ -0,0 +1,103 @@
ORDERING INFO
To order the full version of Quake (or any other id Software
product) in North America, call our fulfillment center at 1-800-idgames
(1-800-434-3627). Except as noted by our operators, you can expect
Airborne Express afternoon delivery. The price for the full version
of Quake (available on PC CDROM only) is $45, plus $5 shipping, for a
total of $50. Our fulfillment center accepts Visa, Mastercard, and
American Express. You can also fax, mail, or email your order using
the attached forms. The fax number is (317) 361-3710 and the email
address is idsoftware@stream.com. To prepay and order with a check
by mail, send your check and the order form to:
id Software
P.O. Box 4500
Crawfordsville, IN 47933
To see an electronic catalog of our software, tshirts, hint books, and
other merchandise available, check out the Shopping Maul section of our
website at www.idsoftware.com.
INTERNATIONAL ORDERS
Quake is available worldwide as a full retail product. To find out
which local stores carry Quake and other id products, contact the
following international affiliates:
Europe Australia
GT Interactive Software Roadshow New Media
1712 583791 (U.K.) 1 902 962000
Taiwan Singapore
U.S. Summit Corporation Summit Co. (Singapore) Pte. Ltd.
706-0660 273-9988
Malaysia Honk Kong
Summit Co. (Malaysia) Sdn Bhd Tsun Tsun Trading Company
757-2244 571-4231
Thailand Israel/Jordan/Lebanon/Egypt
U.S. Summit Corp. (Overseas) Mirage Mulimedia
374-3956 972 3 510 5764
If you are in a territory that cannot access 1(800)idgames, and you
wish to order our products directly, you must place your order in
writing to the fax, mail, or email addresses listed above under
ORDERING INFO.
International phone orders will NOT be accepted. Unfortunately, due
to international shipping costs, all international orders are sent
out via US Mail. This means we cannot guarantee timeliness of delivery
due to customs and other delays inherent to international shipping
______________________________________________________________________
ORDER FORM -- USE THIS FORM TO FAX , MAIL OR EMAIL YOUR ORDER.
id Software Order Center Date ______________
PO BOX 4500 Phone: 1800 id games
Crawfordsville, IN 47933 Fax: (317) 361-3710
idsoftware@stream.com
Product List and Prices in U.S. Currency: (check items)
Quake (CD ROM only) $45 ____
The Ultimate DOOM (Mac version available must specify) $25 ____
DOOM II (Mac version available must specify) $40 ____
Master Levels for DOOM II (CD ROM only) $25 ____
Final DOOM (CD ROM only) $40 ____
DOOM Hint Book $15 ____
Original DOOM Tshirt (S,M.L.XL) $13 ____
The Ultimate DOOM Tshirt (XXL only) $13 ____
Final DOOM Tshirt $13 ____
Heretic:Shadow of the Serpent Riders (CD ROM only) $40 ____
Heretic Hint Book $15 ____
Hexen:Beyond Heretic (Mac version available must specify) $40 ____
Hexen:Deathkings of the Dark Citadel (CD ROM only) $25 ____
Hexen Hint Book $15 ____
Hexen Tshirt (XXL only) $13 ____
Wolfenstein 3D (PC CD only) $20 ____
Commander Keen (3.5 disk only) $15 ____
Order total: $______
Name: Age (optional):
Form of payment (check, money order, or credit card):
Credit card number: Expiration Date:
Exact mailing address:______________________________________
_______________________________________
_______________________________________
_______________________________________
Phone: Fax: Email:
Shipping: US orders-$5.00 first product/$2.00 each additional
(allow 3-5 business days)
International shipping for prepaid orders are via US Mail, and
we cannot guarantee the time it will take to arrive.
*Prices subject to change

456
NQ/data/README.TXT Normal file
View File

@ -0,0 +1,456 @@
Welcome to Quake!
This file details how to get Quake running on your system and what to do
if you have problems. We would like to thank Gandalf Technologies, Inc and
MPath Interactive for the use of their technology. We would also like to
thank Trent Reznor and Nine Inch Nails for their tremendous contributions
to Quake's entire audio portion.
The NIN logo is a Registered Trademark licensed to Nothing Interactive, Inc.
All Rights Reserved.
Quake System Requirements
-------------------------
IBM PC and Compatibles
Pentium processor or better
VGA Compatible Display or better
8MB RAM minimum, 16MB recommended (16 MB required for running under Win95)
CD-ROM drive Required
MS-DOS 5.0 or better or Windows 95 (does NOT run under Windows NT)
Hard Drive (30MB for Shareware, 80 MB for Registered)
*** IMPORTANT!: Quake requires a floating point processor.
Systems that do not have an FPU installed will not run Quake -- at all.
*** IMPORTANT Video Adapter Note! ***
On some ATI Mach32 cards, Quake can come up with a garbled video display.
This is due to a problem with the card in which 320x200 mode isn't
initialized correctly. Workarounds include:
1) If running from Windows, start Quake from an icon, or from a windowed
(not fullscreen) MS-DOS prompt. If Quake is already running and has
the garbled screen, press Alt-Enter twice to switch to the desktop and
back to fullscreen, and the screen will display properly.
2) If running from DOS, either put the line
vid_mode 1
in id1\autoexec.cfg, or, typing blind, press tilde ('~') to bring down
the console, type
vid_mode 1<enter>
and the screen will display properly.
========================================================================
Here are the text files included with the shareware release of Quake and
what they are:
README.TXT This file
TECHINFO.TXT Technical information on Quake's subsystems and
their advanced use.
MANUAL.TXT Text version of the printed game manual
LICINFO.TXT Info on the various license files included with Quake
SLICNSE.TXT Shareware Quake end-user license
ORDER.TXT How to order Quake
HELP.TXT How to get help with Quake
Here are the text files included with the registered version of Quake and
what they are:
README.TXT This file
TECHINFO.TXT Technical information on Quake's subsystems and
their advanced use.
MANUAL.TXT Text version of the printed game manual
LICINFO.TXT Info on the various license files included with Quake
RLICNSE.TXT Registered Quake end-user license
COMEXP.TXT Commercial exploitation agreement
ORDER.TXT How to order Quake
HELP.TXT How to get help with Quake
Running Quake
-------------
DOS: To launch Quake from the DOS Prompt, go to the Quake directory and
simply type "QUAKE" <ENTER>. (no quotes)
Windows 95: To launch Quake in single player mode, double click on the file
QUAKE.EXE From Windows Explorer. To run Quake in Multi-Player mode using
the TCP/IP protocol, first check your network settings to ensure the
protocol is installed, then double click on the Q95.BAT file to launch the
game. In this version (v0.91) there is a minor bug that will cause the
Q95.BAT file to exit the first time you run it, without running Quake.
Merely double-click on that file again and it will work.
Audio Setup
-----------
When using a Sound Card with Quake, there are a few setup steps which must
be taken. First, the "BLASTER" environment variable setting must be in your
autoexec.bat (or you can type it in manually from the MS-DOS command prompt).
Running the Sound Blaster utility diagnose.exe will automatically configure
your sound card and put this statement in your autoexec.bat file for you.
A typical blaster setting looks like this (although yours may vary):
SET BLASTER=A220 I5 D1 H5 P330 T6
If you want to play the audio track from the CD-ROM while playing Quake,
you must ensure that the audio cable from the CD-ROM is connected to the
sound card.
If you think your sound card is setup properly and it STILL doesn't work,
check to make sure that your BLASTER environment variable contains the
high DMA setting (H5 in the above example).
If you don't get sound while trying to play the audio track, check to see
if a small cable goes from the back of your CD-ROM player directly to your
sound card. If the CD-ROM audio cable is connected to your sound board (or
the motherboard in some cases) and you STILL don't hear CD Audio coming from
your speakers, make sure the MIXER program has the CD volume turned up.
You will also need to run the CD-ROM driver MSCDEX.EXE. Here is an example
of the files you should see (yours probably will vary) listed in your
CONFIG.SYS and AUTOEXEC.BAT (explanation is in parentheses):
CONFIG.SYS:
DEVICE=C:\PROSCSI\CDROM.SYS /D:PROCD01 (CD-ROM driver)
AUTOEXEC.BAT:
SET BLASTER=A220 I5 D1 H5 P330 T6 (sound environment variable setting)
C:\WINDOWS\COMMAND\MSCDEX.EXE /D:PROCD01 /L:D (CD-ROM driver)
===================================================
UltraSound MAX and UltraSound PnP Support for Quake
===================================================
Before running Quake, make sure that your sound card works and your
environment variables are set correctly.
Other UltraSound Cards (ACE & Classic)
--------------------------------------
These drivers are not for the UltraSound ACE or UltraSound Classic
sound cards. We have heard mixed reports that MegaEm or SBOS
have a chance of working with the UltraSound Classic but there is a
short sound F/X delay.
UltraSound PnP and PnP Pro
--------------------------
You must make sure that you do NOT have IWSBOS or MegaEm loaded.
Setup
-----
Quake will automatically detect that the UltraSound Max or PnP
are installed. It does this by looking at the SET INTERWAVE (PnP)
and SET ULTRA16 (Max) environment variables.
Quake will use the settings found on the SET ULTRASND/ULTRA16 (Max)
and in the IW.INI (PnP) file to determine what port settings to use.
Troubleshooting Windows 95 (DOS Box)
------------------------------------
We recommend that you restart your computer in MS-DOS Mode. DOS Box
may or may not work, so use at your own risk.
CD Audio Input
--------------
If you have not already enabled CD audio output by default you will
need to enable it. For the UltraSound MAX you can run "ULTRINIT -EC".
For the UltraSound PnP you will need to enable the CD audio output
in Win'95 and then restart your computer into MS-DOS.
===================================================
Mouse Setup
-----------
If you are going to use a mouse when playing Quake, you will need to load
your mouse driver. This should go in the AUTOEXEC.BAT file as well. Here
is an example:
C:\LOGITECH\MOUSE\MOUSE.EXE (mouse driver)
Booting Clean
-------------
If you are going to be running Quake with only 8 megabytes of RAM, it is best
to boot clean . You eliminate unwanted utilities or applications from taking
up valuable memory, without having to alter your regular AUTOEXEC.BAT and
CONFIG.SYS. Booting clean can be done in one of two ways. If you have
MS-DOS version 6.xx, booting clean is as simple a pressing the shift key
when you see the words "Starting MS-DOS". If you have MS-DOS ver 5.xx you
will need to make a system disk.
To make a boot disk, type the following from the MS-DOS command prompt:
FORMAT A: /S
1. Make sure that this is a disk you wish to erase.
2. This disk absolutely HAS to be formatted in the A: drive.
To use the system disk, place the disk in the A: drive and reset the
computer.
NOTE: If your sound card requires a driver to be loaded, or you will be
using a mouse, or you will be using Quake's CD audio feature, the system
disk will need to have a CONFIG.SYS and AUTOEXEC.BAT that load the
appropriate drivers.
Creating a Quake Shortcut
As an alternative to making a Boot Disk, Windows 95 users can create a
Quake Shortcut. By double clicking onthis shortcut, Windows 95 will reboot
in MS-DOS mode and install only the desired drivers, giving you the same
results as using a Boot Disk. To create a Quake Shortcut, do the following:
1. Using Explorer, right click and drag the file QUAKE.EXE, from the Quake
directory, to your desktop. Windows 95 will make an MS-DOS Icon titled
"Shortcut to quake".
2. Right click on the new icon, and from the menu that pops up, choose
"Properties". Then choose the "Program" tab at the top.
3. Now click on the "Advanced..." button near the bottom. The "Advanced
Program Settings" window should appear.
4. Select the "MS-DOS mode" check box and the "Specify a new MS-DOS
configuration" option button.
5. Now simply fill in the "CONFIG.SYS for MS-DOS mode:" and "AUTOEXEC.BAT
for MS-DOS mode:" boxes with the same sound, CD-ROM and mouse settings as
mentioned above in the Boot Disks section.
6. Click on "OK" when you are finished. If you wish, you can change your
Quake Shortcut Icon to something a little more exciting by clicking on
"Change Icon...".
7. To finish, click on "OK" again.
8. You can rename your Quake Shortcut by right clicking on the shortcut
icon, choosing "Rename" and typing in the new name.
======================================================
== Known Problems ==
======================================================
Problem: Zombies sometime get stuck on the ground and connot get back up.
(You can still hear them, but you cannot kill them. This bug makes it
impossible to get 100% kills on whatever level it occurs on.)
Solution: There is no workaround for this bug.
Problem: It is sometimes possible for the player to get stuck in a room or
in a wall.
Solution: If you get stuck, use the 'kill' console command. It is a good
idea to save your game often.
Problem: View centering problems. Sometimes during a game, the view will not
center properly. The end result is the player view looking up torwards the
ceiling while walking.
Solution: Exit to the next level or use the 'kill' console command..
======================================================
== Troubleshooting ==
======================================================
If Quake fails to start up, or has problems not addressed elsewhere in the
documentation, try the -safe command line switch, which disables a number
of parts of Quake that can be problems if there are hardware or configuration
problems. The -safe command line switch is equivalent to -stdvid, -nosound,
-nonet, and -nocdaudio together. Those four switches do the following:
-stdvid: disables VESA video modes.
-nosound: disables sound card support.
-nonet: disables network card support.
-nocdaudio: disables CD audio support.
If -safe makes the problem go away, try using each of the switches
individually to isolate the area in which you're experiencing the problem,
then either correct the configuration or hardware problem or play Quake with
that functionality disabled.
If you still have problems, try booting clean in conjunction with
the -safe command line parameter. For information on booting clean, refer
to the "Booting Clean" section above.
If you experience page faults while running Quarterdeck's QDPMI DPMI server,
this is caused by a bug in QDPMI. Workarounds: Remove QDPMI from CONFIG.SYS,
issue the command QDPMI OFF before running QUAKE, or get the update patch
for QDPMI from Quarterdeck. You may be running QDPMI without knowing it if
you have QEMM installed, because it can be installed as part of the QEMM
installation.
Technical Support
-----------------
If you are having trouble installing or running Quake you can receive
technical support by sending e-mailing to support@idsoftware.com. You can
also refer to our web page, www.idsoftware.com, or call 1-800-idgames.
When sending support e-mail, cut and paste the following into your e-mail
message and fill in the blanks:
Date:
Name:
Phone number:
E-mail address: (please include this, we redirect tons of mail)
Game Title:
Version #:
Operating system (i.e., DOS 6.0 or Windows 95):
Computer type:
BIOS date:
BIOS version:
Processor type:
Processor speed:
Do you program at school/work?
Do you provide tech. support at school/work?
Please state the problem you encountered:
Please state how to reproduce the problem:
If program crashed with nasty undecipherable techno-garbage, please
look for the eight-digit hex number which comes after "eip="
and write it down here:
** NOTE: If you are sending a bug report, PLEASE refer to the TECHINFO.TXT
file for the correct form and procedures.
======================================================
== Version History ==
======================================================
v1.01 -- Bugs fixed
------------------------------------------------------
* Fixed modem code
* Fixed fraglimit & timelimit
* Added NOEXIT cvar (so no one can exit a level)
------------------------------------------------------
v1.00 -- Bugs fixed
------------------------------------------------------
* Gravis Ultrasound audio support (still has bugs)
* More deathmatch start spots on E1M6 and END
* Print server version and PROG CRC on connect
* -dedicated starts start.map if nothing else specified
* fixed lookspring function during net game
* fixed rare crash during long running dedicated server
------------------------------------------------------
v0.94 -- Bugs fixed / Features added -- LIMITED BETA VERSION
------------------------------------------------------
* Totally rewritten menus
* New lighting model with overbrighting
* Parsed lowercase BLASTER parms
* Better Sound Blaster shutdown code
* Rewrote BLASTER initialization
* Fixed DMA channel 0 bugs
* Added SBPro 8 stereo setup
* Fix delayed sound on 8 bit Sound Blasters
* Fixed speed key affecting angle-turning from keyboard
* Fixed "no such Alias frame" bugs
* Fixed Zombie not getting up bug
* Checked for very high joystick values, signalling a failed read
* Unstuck jumping Fiends and Spawn
* Fixed large BModels blinking out in complex areas
* Fixed s_localsound with no sound started
* Saved spawn parms in savegame
* Fixed screenshot save location
* Bind with no arguments no longer clears value
* Allow console in intermission / finale
* Fixed false gib messages
* Full-screen TAB scoreboard in DeathMatch
* Fixed "+playdemo <demo>" from command line
* Trapped overflow in sizebuf messages
* Moveup / movedown in water!
* Fixed-up Talk command
* Added unsupported crosshair option ("crosshair 1" from console)
* Colored chat messages with notify sound
* Fixed "connect during intermission" bug
* Changelevel while demos running no longer crashes
* Fixed changelevel with no map left up loading screen
* Fixed long names entered from the console causing crash
* Stopped demos changing while in the menus
* Fixed modem initialization from menu
* Fixed serial reliable stream getting stalled
* Serial/modem code fixes
16550a lost transmit buffer empty interrupts
fixed sometimes processing interrupts from com1 when using com2
added com3/com4 support from menus
fixed first character of modem init not getting sent
saved serial/modem settings in config.cfg
* Fixed name and colors not always sent to server at startup
* Fixed "stopdemo" crashing the system when there wasn't a demo playing
* Added server's TCP/IP and IPX addresses (if available) to status command
* In 0.92, an additional check for a usable VESA video mode was added;
the numpages field was verified to be greater than 0, and no mode was
supported that had numpages set to 0 (which indicates that there's not
enough video memory for that mode). ATI's VESA driver, m64vbe,
reports 0 for numpages, so VESA video modes that were available in 0.91
were no longer available in 0.92. This extra numpages check has
been removed.
-----------------------------------------------------------------------
v0.93 -- Never officially released; internal testing only.
-----------------------------------------------------------------------
v0.92 -- Bugs fixed
-----------------------------------------------------------------------
Typing long strings in the hostname or modem init field in the menus caused
crashes.
Under Win95 IPX was detected but not functional, resulting in the game
exiting to DOS.
If -nosound, got "S_LocalSound: can't cache" on every keypress in the menu.
When vid_nopageflip was set to 1 in VESA modes, going underwater resulted in
only the upper left corner of the drawing area being updated.
The single player scoreboard (tab) printed text incorrectly in all modes
greater than 320 pixels wide.
On network connections that dropped packets, the reliable message stream
could get stopped up, resulting in frag counts and talk messages no longer
being delivered, although game movement continued.
The com port settings from the menu were getting saved & restored but
not used.
Direct serial connections did not work with slist.
Quake now checks the vesa information for hardware incabable of page-flipping.
Menu sound sometimes didn't play.
Q95 (qlaunch.exe) frequently failed to execute on the first attempt.
Q95 (quakeudp.dll) was running out of buffers when running a server.
Teams were not being set according to pants colors.
Joystick notes
--------------
Your joystick must be plugged in when Quake is launched.
If you have a joystick plugged in, but don't want to use it in Quake
(it slows the game down a few percent), or you have weird hardware that
doesn't like being tested as a joystick add "-nojoy" to your Quake
command line.
You can turn off joystick reading during the game by typing "joystick 0" at
the Quake command console.
You MUST configure your buttons from the configure keys menu before they will
work. There is no default configuration.
If your joystick or interface card improperly sets the third or fourth
joystick buttons, type "joybuttons 2" at the quake console or in your
.CFG file.
The "mlook" button command now lets the joystick as well as the mouse control
pitch angles.
The "sidestep" buttom command works on joysticks as with mice and keyboard
movement.
The "invert mouse up/down" menu option also inverts the joystick pitch
direction.

204
NQ/data/RLICNSE.TXT Normal file
View File

@ -0,0 +1,204 @@
REGISTERED VERSION: QUAKE
LIMITED USE SOFTWARE LICENSE AGREEMENT
This Limited Use Software License Agreement (the
"Agreement") is a legal agreement between you, the end-user, and Id
Software, Inc. ("ID"). By continuing the installation of this game
program, by loading or running the game, or by placing or copying
the game program onto your computer hard drive, you are agreeing to
be bound by the terms of this Agreement. If you do not agree to
the terms of this Agreement, promptly return the game program and
the accompanying items (including all written materials), along
with your receipt to the place from where you obtained them for a
full refund.
ID SOFTWARE LICENSE
1. Grant of License. ID grants to you the limited
right to use one (1) copy of the enclosed or foregoing game program
(the "Software") on a single computer. You have no ownership or
proprietary rights in or to the Software or the written materials
accompanying the Software. For purposes of this section, "use"
means loading the Software into RAM, as well as installation on a
hard disk or other storage device. You may create a map editor,
modify maps and make your own maps (collectively referenced as the
"Permitted Derivative Works") for the Software. Permitted
Derivative Works may not be sold, whether by you or by any other
person or entity, but you may exchange the Permitted Derivative
Works at no charge amongst other end-users. The Software, together
with any archive copy thereof, shall be either returned to ID or
destroyed when no longer used in accordance with this Agreement, or
when the right to use the Software is terminated. You agree that
the Software will not be shipped, transferred or exported into any
country in violation of the U.S. Export Administration Act (or any
other law governing such matters) and that you will not utilize, in
any other manner, the Software in violation of any applicable law.
2. Commercial Use is Prohibited. Except as provided in
paragraph 5. hereinbelow in regard to the Software, under no
circumstances shall you, the end-user, be permitted, allowed or
authorized to commercially exploit the Software, any data
comprising the Software. Neither you nor anyone at your direction
shall do any of the following acts (any such acts shall be deemed
void and a breach of this Agreement) with regard to the Software,
or any portion thereof, such as a screen display or a screenshot:
a. Rent the Software;
b. Sell the Software;
c. Lease or lend the Software;
d. Offer the Software on a pay-per-play basis;
e. Distribute, by electronic means or otherwise, the
Software for money or any other consideration; or
f. In any other manner and through any medium
whatsoever commercially exploit the Software or use
the Software for any commercial purpose.
3. Additional Prohibited Uses. Neither you nor anyone
at your direction shall take the following action in regard to the
Software, or any portion thereof, such as a screen display or a
screenshot:
a. Modify, disassemble, reverse engineer or decompile
the Software;
b. Translate the Software;
c. Reproduce the Software;
d. Publicly display the Software;
e. Prepare derivative works based upon the Software
(except Permitted Derivative Works); or
f. Distribute, by electronic means or otherwise, the
Software.
4. Use of Other Material is Prohibited. Use, in any manner, of
the trademarks, such as Quake(tm) and the NIN(r) logo, logos, symbols,
art work, images, screen displays or screenshots, sound effects, music,
and other such material contained within, generated by or relating to
the Software is prohibited.
5. To Receive Permission to Commercially Exploit. If
you desire to commercially exploit the Software, you may execute
the Commercial Exploitation License Agreement for QUAKE (the
"License") contained within the QUAKE install package and forward
the original License to Id Software at the address noted therein.
Please note that ID may refuse your request and not sign the
License in ID's sole discretion.
6. Restrictions Apply to Third Parties. The
prohibitions and restrictions described herein apply to anyone in
possession of the Software and/or Permitted Derivative Works.
7. Copyright. The Software and all copyrights related
thereto (including all characters and other images generated by the
Software or depicted in the Software) is owned by ID and is protected
by United States copyright laws and international treaty provisions.
You must treat the Software like any other copyrighted material,
except that you may either (a) make one copy of the Software solely
for back-up or archival purposes, or (b) transfer the Software to a
single hard disk provided you keep the original solely for back-up or
archival purposes. You may not otherwise reproduce, copy or disclose
to others, in whole or in any part, the Software. You may not copy
the written materials accompanying the Software. The same
restrictions and prohibitions regarding your use of the Software as
provided in this Agreement apply to your use of the written materials
accompanying the Software. The written materials are owned by ID and
are protected by United States copyright laws and international
treaties. You agree to use your best efforts to see that any user of
the Software licensed hereunder complies with this Agreement.
8. Limited Warranty. ID warrants that if properly
installed and operated on a computer for which it is designed, the
Software will perform substantially in accordance with the
accompanying written materials for a period of ninety (90) days
from the date of purchase of the Software. ID's entire liability
and your exclusive remedy shall be, at ID's option, either (a)
return of the price paid or (b) repair or replacement of the
Software that does not meet ID's Limited Warranty. To make a
warranty claim, return the Software to the point of purchase,
accompanied by proof of purchase, your name, your address, and a
statement of defect, or return the Software with the above
information to ID. This Limited Warranty is void if failure of the
Software has resulted in whole or in part from accident, abuse,
misapplication or violation of this Agreement. Any replacement
Software will be warranted for the remainder of the original
warranty period or thirty (30) days from your receipt of the
replacement software, whichever is longer. This warranty allocates
risks of product failure between Licensee and ID. ID's product
pricing reflects this allocation of risk and the limitations of
liability contained in this warranty.
9. NO OTHER WARRANTIES. ID DISCLAIMS ALL OTHER
WARRANTIES, BOTH EXPRESS IMPLIED, INCLUDING BUT NOT LIMITED TO,
IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
PURPOSE WITH RESPECT TO THE SOFTWARE AND THE ACCOMPANYING WRITTEN
MATERIALS. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL RIGHTS.
YOU MAY HAVE OTHER RIGHTS WHICH VARY FROM JURISDICTION TO
JURISDICTION. ID DOES NOT WARRANT THAT THE OPERATION OF THE
SOFTWARE WILL BE UNINTERRUPTED, ERROR FREE OR MEET LICENSEE'S
SPECIFIC REQUIREMENTS. THE WARRANTY SET FORTH ABOVE IS IN LIEU OF
ALL OTHER EXPRESS WARRANTIES WHETHER ORAL OR WRITTEN. THE AGENTS,
EMPLOYEES, DISTRIBUTORS, AND DEALERS OF ID ARE NOT AUTHORIZED TO
MAKE MODIFICATIONS TO THIS WARRANTY, OR ADDITIONAL WARRANTIES ON
BEHALF OF ID. ADDITIONAL STATEMENTS SUCH AS DEALER ADVERTISING OR
PRESENTATIONS, WHETHER ORAL OR WRITTEN, DO NOT CONSTITUTE
WARRANTIES BY ID AND SHOULD NOT BE RELIED UPON.
10. Exclusive Remedies. You agree that your exclusive
remedy against ID, its affiliates, contractors, suppliers, and
agents for loss or damage caused by any defect or failure in the
Software regardless of the form of action, whether in contract,
tort, including negligence, strict liability or otherwise, shall be
the return of the purchase price paid or replacement of the
Software. This Agreement shall be construed in accordance with and
governed by the laws of the State of Texas. Copyright and other
proprietary matters will be governed by United States laws and
international treaties. IN ANY CASE, ID SHALL NOT BE LIABLE FOR
LOSS OF DATA, LOSS OF PROFITS, LOST SAVINGS, SPECIAL, INCIDENTAL,
CONSEQUENTIAL, INDIRECT OR OTHER SIMILAR DAMAGES ARISING FROM
BREACH OF WARRANTY, BREACH OF CONTRACT, NEGLIGENCE, OR OTHER LEGAL
THEORY EVEN IF ID OR ITS AGENT HAS BEEN ADVISED OF THE POSSIBILITY
OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. Some
jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so the above limitation or
exclusion may not apply to you.
11. General Provisions. Neither this Agreement nor any
part or portion hereof shall be assigned, sublicensed or otherwise
transferred by you. Should any provision of this Agreement be held
to be void, invalid, unenforceable or illegal by a court, the
validity and enforceability of the other provisions shall not be
affected thereby. If any provision is determined to be
unenforceable, you agree to a modification of such provision to
provide for enforcement of the provision's intent, to the extent
permitted by applicable law. Failure of a party to enforce any
provision of this Agreement shall not constitute or be construed as
a waiver of such provision or of the right to enforce such
provision. If you fail to comply with any terms of this Agreement,
YOUR LICENSE IS AUTOMATICALLY TERMINATED.
YOU ACKNOWLEDGE THAT YOU HAVE READ THIS AGREEMENT, THAT YOU
UNDERSTAND THIS AGREEMENT, AND UNDERSTAND THAT BY CONTINUING THE
INSTALLATION OF THE SOFTWARE, BY LOADING OR RUNNING THE SOFTWARE, OR
BY PLACING OR COPYING THE SOFTWARE ONTO YOUR COMPUTER HARD DRIVE, YOU
AGREE TO BE BOUND BY THIS AGREEMENT'S TERMS AND CONDITIONS. YOU
FURTHER AGREE THAT, EXCEPT FOR WRITTEN SEPARATE AGREEMENTS BETWEEN ID
AND YOU, THIS AGREEMENT IS A COMPLETE AND EXCLUSIVE STATEMENT OF THE
RIGHTS AND LIABILITIES OF THE PARTIES. THIS AGREEMENT SUPERSEDES ALL
PRIOR ORAL AGREEMENTS, PROPOSALS OR UNDERSTANDINGS, AND ANY OTHER
COMMUNICATIONS BETWEEN ID AND YOU RELATING TO THE SUBJECT MATTER OF
THIS AGREEMENT.
June 21, 1996
REGISTERED VERSION: QUAKE LIMITED USE SOFTWARE LICENSE AGREEMENT Page 4
(DWC:dw:3406.0024:DWC\doc:1164)

175
NQ/data/SLICNSE.TXT Normal file
View File

@ -0,0 +1,175 @@
SHAREWARE VERSION: QUAKE
LIMITED USE SOFTWARE LICENSE AGREEMENT
This Limited Use Software License Agreement (the "Agreement") is a
legal agreement between you, the end-user, and id Software, Inc.
("ID"). By continuing the installation of this game program, by
loading or running the game, or by placing or copying the game
program onto your computer hard drive, you are agreeing to be bound
by the terms of this Agreement.
ID SOFTWARE LICENSE
1. Grant of License. ID grants to you the limited right to use
one (1) copy of the enclosed or foregoing Id Software game program
(the "Software"), which is the shareware version or episode one of
the game program. For purposes of this section, "use" means loading
the Software into RAM, as well as installation on a hard disk or
other storage device. You agree that the Software will not be
shipped, transferred or exported into any country in violation of
the U.S. Export Administration Act (or any other law governing such
matters) and that you will not utilize, in any other manner, the
Software in violation of any applicable law.
2. Commercial Use is Prohibited. Under no circumstances shall
you, the end-user, be permitted, allowed or authorized to
commercially exploit the Software, or any portion thereof, such
as a screen display or a screenshot. Neither you nor anyone at your
direction shall do any of the following acts:
a. Rent the Software;
b. Sell the Software;
c. Lease or lend the Software;
d. Offer the Software on a pay-per-play basis;
e. Distribute the Software for money or any other
consideration; or
f. In any other manner and through any medium
whatsoever commercially exploit the Software or use
the Software for any commercial purpose.
3. Additional Prohibited Uses. Neither you, nor anyone at your
direction, shall take the following action in regard to the
Software, or any portion thereof, such as a screen display or
a screenshot:
a. Modify, disassemble, reverse engineer or decompile
the Software;
b. Translate the Software;
c. Reproduce the Software;
d. Publicly display the Software; or
e. Prepare derivative works based upon the Software.
4. Use of Other Material is Prohibited. Use, in any manner, of
the trademarks, such as Quake(tm) and the NIN(r) logo, logos, symbols,
art work, images, screen displays or screenshots, sound effects, music,
and other such material contained within, generated by or relating to
the Software is prohibited.
5. Restrictions Apply to Third Parties. The prohibitions and
restrictions described herein apply to anyone in possession of
the Software.
6. Permitted Distribution. So long as this Agreement
accompanies the Software at all times, ID grants to Providers the
limited right to distribute, free of charge, except normal access
fees, and by electronic means only, the Software; provided, however,
the Software must be so electronically distributed only in a
compressed format. The term "Providers," as used in the foregoing
sentence, shall mean persons whose business it is to provide
services on the Internet, on commercial online networks, or on the
BBS. Anyone who receives the Software from a Provider shall be
limited to all the terms and conditions of this Agreement. Further,
ID grants to you, the end-user, the limited right to distribute,
free of charge only, the Software as a whole.
7. Copyright. The Software is owned by ID and is protected by
United States copyright laws and international treaty provisions.
You must treat the Software like any other copyrighted material,
except that you may make copies of the Software to give to other
persons. You may not charge or receive any consideration from any
other person for the receipt or use of the Software. You agree to
use your best efforts to see that any user of the Software licensed
hereunder complies with this Agreement.
8. Limited Warranty. ID warrants that if properly installed and
operated on a computer for which it is designed, the Software will
perform substantially in accordance with its designed purpose for a
period of ninety (90) days from the date the Software is first
obtained by an end-user. ID's entire liability and your exclusive
remedy shall be, at ID's option, either (a) return of the retail
price paid, if any, or (b) repair or replacement of the Software
that does not meet ID's Limited Warranty. To make a warranty claim,
return the Software to the point of purchase, accompanied by proof
of purchase, your name, your address, and a statement of defect, or
return the Software with the above information to ID. This Limited
Warranty is void if failure of the Software has resulted in whole
or in part from accident, abuse, misapplication or violation of this
Agreement. Any replacement Software will be warranted for the
remainder of the original warranty period or thirty (30) days,
whichever is longer. This warranty allocates risks of product
failure between Licensee and ID. ID's product pricing reflects this
allocation of risk and the limitations of liability contained in
this warranty.
9. NO OTHER WARRANTIES. ID DISCLAIMS ALL OTHER WARRANTIES,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A pARTICULAR PURPOSE
WITH RESPECT TO THE SOFTWARE AND THE ACCOMPANYING WRITTEN MATERIALS,
IF ANY. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL RIGHTS. YOU
MAY HAVE OTHERS WHICH VARY FROM JURISDICTION TO JURISDICTION. ID
DOES NOT WARRANT THAT THE OPERATION OF THE SOFTWARE WILL BE
UNINTERRUPTED, ERROR FREE OR MEET LICENSEE'S SPECIFIC REQUIREMENTS.
THE WARRANTY SET FORTH ABOVE IS IN LIEU OF ALL OTHER EXPRESS
WARRANTIES WHETHER ORAL OR WRITTEN. THE AGENTS, EMPLOYEES,
DISTRIBUTORS, AND DEALERS OF ID ARE NOT AUTHORIZED TO MAKE
MODIFICATIONS TO THIS WARRANTY, OR ADDITIONAL WARRANTIES ON BEHALF
OF ID. ADDITIONAL STATEMENTS SUCH AS DEALER ADVERTISING OR
PRESENTATIONS, WHETHER ORAL OR WRITTEN, DO NOT CONSTITUTE WARRANTIES
BY ID AND SHOULD NOT BE RELIED UPON.
10. Exclusive Remedies. You agree that your exclusive remedy
against ID, its affiliates, contractors, suppliers, and agents for
loss or damage caused by any defect or failure in the Software
regardless of the form of action, whether in contract,tort,
including negligence, strict liability or otherwise, shall be the
return of the retail purchase price paid, if any, or replacement of
the Software. This Agreement shall be construed in accordance with
and governed by the laws of the State of Texas. Copyright and other
proprietary matters will be governed by United States laws and
international treaties. IN ANY CASE, ID SHALL NOT BE LIABLE FOR LOSS
OF DATA, LOSS OF PROFITS, LOST SAVINGS, SPECIAL, INCIDENTAL,
CONSEQUENTIAL, INDIRECT OR OTHER SIMILAR DAMAGES ARISING FROM BREACH
OF WARRANTY, BREACH OF CONTRACT, NEGLIGENCE, OR OTHER LEGAL THEORY
EVEN IF ID OR ITS AGENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. Some jurisdictions do
not allow the exclusion or limitation of incidental or consequential
damages, so the above limitation or exclusion may not apply to you.
11. General Provisions. Neither this Agreement nor any part or
portion hereof shall be assigned or sublicensed, except as described
herein. Should any provision of this Agreement be held to be void,
invalid, unenforceable or illegal by a court, the validity and
enforceability of the other provisions shall not be affected thereby.
If any provision is determined to be unenforceable, you agree to a
modification of such provision to provide for enforcement of the
provision's intent, to the extent permitted by applicable law. Failure
of a party to enforce any provision of this Agreement shall not
constitute or be construed as a waiver of such provision or of the
right to enforce such provision. If you fail to comply with any terms
of this Agreement, YOUR LICENSE IS AUTOMATICALLY TERMINATED.
YOU ACKNOWLEDGE THAT YOU HAVE READ THIS AGREEMENT, YOU UNDERSTAND
THIS AGREEMENT, AND UNDERSTAND THAT BY CONTINUING THE INSTALLATION
OF THE SOFTWARE, BY LOADING OR RUNNING THE SOFTWARE, OR BY PLACING
OR COPYING THE SOFTWARE ONTO YOUR COMPUTER HARD DRIVE, YOU AGREE TO
BE BOUND BY THIS AGREEMENT'S TERMS AND CONDITIONS. YOU FURTHER
AGREE THAT, EXCEPT FOR WRITTEN SEPARATE AGREEMENTS BETWEEN ID AND
YOU, THIS AGREEMENT IS A COMPLETE AND EXCLUSIVE STATEMENT OF THE
RIGHTS AND LIABILITIES OF THE PARTIES. THIS AGREEMENT SUPERSEDES
ALL PRIOR ORAL AGREEMENTS, PROPOSALS OR UNDERSTANDINGS, AND ANY
OTHER COMMUNICATIONS BETWEEN ID AND YOU RELATING TO THE SUBJECT
MATTER OF THIS AGREEMENT.
June 21, 1996
SHAREWARE VERSION: QUAKE LIMITED USE SOFTWARE LICENSE AGREEMENT
(DWC:dw:3406.0024:DWC\doc:1163)

1901
NQ/data/TECHINFO.TXT Normal file

File diff suppressed because it is too large Load Diff

177
NQ/docs/INSTALL Normal file
View File

@ -0,0 +1,177 @@
INSTALL for Linux Quake
-----------------------
Quake for Linux provides several different binary executables to support
different hardware and drivers.
Included with Linux Quake are:
- SVGALib Quake (squake)
This is a software renderer Quake that runs at the text console in Linux.
- GLQuake (glquake, glquake.glx and glquake.3dfxgl)
This is a hardware renderer Quake that runs using hardware 3D
acceleration.
- X11 Quake (quake.x11)
Software rendering in a window under X11.
Installation
------------
Mount the Quake CD as one would usually mount a CDROM, this can be
accomplished by using the command:
mount /dev/cdrom /mnt
As root. Once the CD is mounted, run the setup script on the CD as root.
$ su
Password:
# mount /dev/cdrom /mnt
# /bin/sh /mnt/setup
The script will ask some questions about what options you want to install
and automatically install the software into /usr/local/games/quake.
Requirements
------------
Requirements for SVGALib Quake:
- SVGALib 1.20 or later (/lib/libvga.so.1.2.10)
- libc 5.2.18 or later (5.0.9 will not work, /lib/libc.so.5.2.18)
or glibc (libc6) for the glibc version
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib supported mouse (usually if it works with X, it'll work with
squake).
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Requirements for GLQuake:
- 3DFX based card for the GLQuake version, VooDoo, VooDoo Rush or VooDoo2
at this writing. In order to use 3DFX hardware, you must have 3DFX's
GLIDE drivers installed. RPMs for these drivers are available at:
http://glide.xxedgexx.com/3DfxRPMS.html
- For the glX version, an OpenGL implementation that includes hardware
glX support.
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib compatible mouse for glquake or X11 for glquake.glx
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Requirements for X11 Quake:
- X11R5 later, only tested with XFree86, should work with most X Servers
- libc 5.2.18 or later (5.0.9 will not work, /lib/libc.so.5.2.18)
or glibc (libc6) for the glibc version
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib supported mouse (usually if it works with X, it'll work with
squake).
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Additional notes for SVGALib Quake
----------------------------------
SVGALib may not detect a 3-button mouse properly (it
will only use two buttons). Check your /etc/vga/libvga.config
and set it up for your mouse type.
Also, newer versions of SVGALib have an mouse_accel_type option. Most
users will want to set this to "off" in /etc/vga/libvga.config.
Additional notes for GLQuake
----------------------------
There are three different ways to execute GLQuake:
1. The binary "glquake" requires Mesa 3-D 2.5 or later installed and compiled
with 3DFX support (fxMesa..() function interface). It also requires
svgalib 1.3.0 or later for keyboard/mouse input. This binary is a console
application. Mesa 3-D requires GLIDE to be installed.
2. The shell script "glquake.3dfxgl" runs the "glquake" binary after
preloading the lib3dfxgl.so library. This is a port of 3DFX's Win32
OpenGL MCD (Mini Client Driver) to Linux. It is faster than Mesa 3-D
since it was written specifically with supporting GLQuake in mind.
lib3dfxgl.so requires that GLIDE be installed.
3. The binary "glquake.glx" is linked against standard OpenGL libraries.
It should run on many different hardward OpenGL implementations under
Linux and X11. This binary is an X11 application and must be run under
X11. It will work with Mesa 3-D as a standard glX based OpenGL
applications. If the Mesa 3-D library is compiled with 3DFX support,
you can have Mesa 3-D support 3DFX hardware under X11 by setting the
enviroment variable "MESA_GLX_FX" to "fullscreen" for fullscreen mode
and "window" for windowed mode, eg. "export MESA_GLX_FX=fullscreen" for sh
or "setenv MESA_GLX_FX fullscreen" for csh.
For glquake, you must also have SVGALib or later installed (1.3.0 or later
prefered). GLQuake uses SVGALib for mouse and keyboard handling.
If you have gpm and/or selection running, you will have to terminate them
before running GLQuake since they will not give up the mouse when GLQuake
attempts to run. You can kill gpm by typing 'killall gpm' as root.
You must run GLQuake as root or setuid root since it needs to access things
such as sound, keyboard, mouse and the 3DFX video. Future versions may not
require root permissions.
Additional notes for X11 Quake
------------------------------
This is a windowed version that is generic for X11. It runs in a window
and can be resized. You can specify a starting window size with:
-width <width>
-height <height>
-winsize <width> <height>
Default is 320x200. It works in 16bit modes, but it's slower (twice as many
bytes to copy).
No other video modes are supported (just runs windowed). Mouse is read, but
not "grabbed" by default. Go to the Options menu and turn on Use Mouse to grab
the mouse and use it in the game (or type "_windowed_mouse 1" at the console).
Command Line Options for Linux Quake
------------------------------------
-mem <mb>
Specify memory in megabytes to allocate (default is 8MB, which should be fine
for most needs).
-nostdout
Don't do any output to stdout
-mdev <device> (SVGALib based versions only)
Mouse device, default is /dev/mouse
-mrate <speed> (SVGALib based versions only)
Mouse baud rate, default is 1200
-cddev <device>
CD device, default is /dev/cdrom
-mode <modenum>
Use indicated video mode
-nokdb
Don't initialize keyboard
-sndbits <8 or 16>
Set sound bit sample size. Default is 16 if supported.
-sndspeed <speed>
Set sound speed. Usual values are 8000, 11025, 22051 and 44100.
Default is 11025.
-sndmono
Set mono sound
-sndstereo
Set stereo sound (default if supported)

177
NQ/docs/INSTALL.Quake Normal file
View File

@ -0,0 +1,177 @@
INSTALL for Linux Quake
-----------------------
Quake for Linux provides several different binary executables to support
different hardware and drivers.
Included with Linux Quake are:
- SVGALib Quake (squake)
This is a software renderer Quake that runs at the text console in Linux.
- GLQuake (glquake, glquake.glx and glquake.3dfxgl)
This is a hardware renderer Quake that runs using hardware 3D
acceleration.
- X11 Quake (quake.x11)
Software rendering in a window under X11.
Installation
------------
Mount the Quake CD as one would usually mount a CDROM, this can be
accomplished by using the command:
mount /dev/cdrom /mnt
As root. Once the CD is mounted, run the setup script on the CD as root.
$ su
Password:
# mount /dev/cdrom /mnt
# /bin/sh /mnt/setup
The script will ask some questions about what options you want to install
and automatically install the software into /usr/local/games/quake.
Requirements
------------
Requirements for SVGALib Quake:
- SVGALib 1.20 or later (/lib/libvga.so.1.2.10)
- libc 5.2.18 or later (5.0.9 will not work, /lib/libc.so.5.2.18)
or glibc (libc6) for the glibc version
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib supported mouse (usually if it works with X, it'll work with
squake).
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Requirements for GLQuake:
- 3DFX based card for the GLQuake version, VooDoo, VooDoo Rush or VooDoo2
at this writing. In order to use 3DFX hardware, you must have 3DFX's
GLIDE drivers installed. RPMs for these drivers are available at:
http://glide.xxedgexx.com/3DfxRPMS.html
- For the glX version, an OpenGL implementation that includes hardware
glX support.
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib compatible mouse for glquake or X11 for glquake.glx
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Requirements for X11 Quake:
- X11R5 later, only tested with XFree86, should work with most X Servers
- libc 5.2.18 or later (5.0.9 will not work, /lib/libc.so.5.2.18)
or glibc (libc6) for the glibc version
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib supported mouse (usually if it works with X, it'll work with
squake).
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Additional notes for SVGALib Quake
----------------------------------
SVGALib may not detect a 3-button mouse properly (it
will only use two buttons). Check your /etc/vga/libvga.config
and set it up for your mouse type.
Also, newer versions of SVGALib have an mouse_accel_type option. Most
users will want to set this to "off" in /etc/vga/libvga.config.
Additional notes for GLQuake
----------------------------
There are three different ways to execute GLQuake:
1. The binary "glquake" requires Mesa 3-D 2.5 or later installed and compiled
with 3DFX support (fxMesa..() function interface). It also requires
svgalib 1.3.0 or later for keyboard/mouse input. This binary is a console
application. Mesa 3-D requires GLIDE to be installed.
2. The shell script "glquake.3dfxgl" runs the "glquake" binary after
preloading the lib3dfxgl.so library. This is a port of 3DFX's Win32
OpenGL MCD (Mini Client Driver) to Linux. It is faster than Mesa 3-D
since it was written specifically with supporting GLQuake in mind.
lib3dfxgl.so requires that GLIDE be installed.
3. The binary "glquake.glx" is linked against standard OpenGL libraries.
It should run on many different hardward OpenGL implementations under
Linux and X11. This binary is an X11 application and must be run under
X11. It will work with Mesa 3-D as a standard glX based OpenGL
applications. If the Mesa 3-D library is compiled with 3DFX support,
you can have Mesa 3-D support 3DFX hardware under X11 by setting the
enviroment variable "MESA_GLX_FX" to "fullscreen" for fullscreen mode
and "window" for windowed mode, eg. "export MESA_GLX_FX=fullscreen" for sh
or "setenv MESA_GLX_FX fullscreen" for csh.
For glquake, you must also have SVGALib or later installed (1.3.0 or later
prefered). GLQuake uses SVGALib for mouse and keyboard handling.
If you have gpm and/or selection running, you will have to terminate them
before running GLQuake since they will not give up the mouse when GLQuake
attempts to run. You can kill gpm by typing 'killall gpm' as root.
You must run GLQuake as root or setuid root since it needs to access things
such as sound, keyboard, mouse and the 3DFX video. Future versions may not
require root permissions.
Additional notes for X11 Quake
------------------------------
This is a windowed version that is generic for X11. It runs in a window
and can be resized. You can specify a starting window size with:
-width <width>
-height <height>
-winsize <width> <height>
Default is 320x200. It works in 16bit modes, but it's slower (twice as many
bytes to copy).
No other video modes are supported (just runs windowed). Mouse is read, but
not "grabbed" by default. Go to the Options menu and turn on Use Mouse to grab
the mouse and use it in the game (or type "_windowed_mouse 1" at the console).
Command Line Options for Linux Quake
------------------------------------
-mem <mb>
Specify memory in megabytes to allocate (default is 8MB, which should be fine
for most needs).
-nostdout
Don't do any output to stdout
-mdev <device> (SVGALib based versions only)
Mouse device, default is /dev/mouse
-mrate <speed> (SVGALib based versions only)
Mouse baud rate, default is 1200
-cddev <device>
CD device, default is /dev/cdrom
-mode <modenum>
Use indicated video mode
-nokdb
Don't initialize keyboard
-sndbits <8 or 16>
Set sound bit sample size. Default is 16 if supported.
-sndspeed <speed>
Set sound speed. Usual values are 8000, 11025, 22051 and 44100.
Default is 11025.
-sndmono
Set mono sound
-sndstereo
Set stereo sound (default if supported)

157
NQ/docs/README Normal file
View File

@ -0,0 +1,157 @@
README for Linux Quake
----------------------
This README convers all versions of Quake for Linux:
- SVGALib Quake (squake)
- GLQuake (glquake, glquake.glx and glquake.3dfxgl)
- X11 Quake (quake.x11)
Requirements for SVGALib Quake:
- SVGALib 1.20 or later (/lib/libvga.so.1.2.10)
- libc 5.2.18 or later (5.0.9 will not work, /lib/libc.so.5.2.18)
or glibc (libc6) for the glibc version
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib supported mouse (usually if it works with X, it'll work with
squake).
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Requirements for GLQuake:
- 3DFX based card for the GLQuake version, VooDoo, VooDoo Rush or VooDoo2
at this writing. In order to use 3DFX hardware, you must have 3DFX's
GLIDE drivers installed. RPMs for these drivers are available at:
http://glide.xxedgexx.com/3DfxRPMS.html
- For the glX version, an OpenGL implementation that includes hardware
glX support.
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib compatible mouse for glquake or X11 for glquake.glx
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Requirements for X11 Quake:
- X11R5 later, only tested with XFree86, should work with most X Servers
- libc 5.2.18 or later (5.0.9 will not work, /lib/libc.so.5.2.18)
or glibc (libc6) for the glibc version
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib supported mouse (usually if it works with X, it'll work with
squake).
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Additional notes for SVGALib Quake
----------------------------------
SVGALib may not detect a 3-button mouse properly (it
will only use two buttons). Check your /etc/vga/libvga.config
and set it up for your mouse type.
Additional notes for GLQuake
----------------------------
There are three different ways to execute GLQuake:
1. The binary "glquake" requires Mesa 3-D 2.5 or later installed and compiled
with 3DFX support (fxMesa..() function interface). It also requires
svgalib 1.3.0 or later for keyboard/mouse input. This binary is a console
application. Mesa 3-D requires GLIDE to be installed.
2. The shell script "glquake.3dfxgl" runs the "glquake" binary after
preloading the lib3dfxgl.so library. This is a port of 3DFX's Win32
OpenGL MCD (Mini Client Driver) to Linux. It is faster than Mesa 3-D
since it was written specifically with supporting GLQuake in mind.
lib3dfxgl.so requires that GLIDE be installed.
3. The binary "glquake.glx" is linked against standard OpenGL libraries.
It should run on many different hardward OpenGL implementations under
Linux and X11. This binary is an X11 application and must be run under
X11. It will work with Mesa 3-D as a standard glX based OpenGL
applications. If the Mesa 3-D library is compiled with 3DFX support,
you can have Mesa 3-D support 3DFX hardware under X11 by setting the
enviroment variable "MESA_GLX_FX" to "fullscreen" for fullscreen mode
and "window" for windowed mode, eg. "export MESA_GLX_FX=fullscreen" for sh
or "setenv MESA_GLX_FX fullscreen" for csh.
For glquake, you must also have SVGALib or later installed (1.3.0 or later
prefered). GLQuake uses SVGALib for mouse and keyboard handling.
If you have gpm and/or selection running, you will have to terminate them
before running GLQuake since they will not give up the mouse when GLQuake
attempts to run. You can kill gpm by typing 'killall gpm' as root.
You must run GLQuake as root or setuid root since it needs to access things
such as sound, keyboard, mouse and the 3DFX video. Future versions may not
require root permissions.
Additional notes for X11 Quake
------------------------------
This is a windowed version that is generic for X11. It runs in a window
and can be resized. You can specify a starting window size with:
-width <width>
-height <height>
-winsize <width> <height>
Default is 320x200. It works in 16bit modes, but it's slower (twice as many
bytes to copy).
No other video modes are supported (just runs windowed). Mouse is read, but
not "grabbed" by default. Go to the Options menu and turn on Use Mouse to grab
the mouse and use it in the game (or type "_windowed_mouse 1" at the console).
Command Line Options for Linux Quake
------------------------------------
-mem <mb>
Specify memory in megabytes to allocate (default is 8MB, which should be fine
for most needs).
-nostdout
Don't do any output to stdout
-mdev <device> (SVGALib based versions only)
Mouse device, default is /dev/mouse
-mrate <speed> (SVGALib based versions only)
Mouse baud rate, default is 1200
-cddev <device>
CD device, default is /dev/cdrom
-mode <modenum>
Use indicated video mode
-nokdb
Don't initialize keyboard
-sndbits <8 or 16>
Set sound bit sample size. Default is 16 if supported.
-sndspeed <speed>
Set sound speed. Usual values are 8000, 11025, 22051 and 44100.
Default is 11025.
-sndmono
Set mono sound
-sndstereo
Set stereo sound (default if supported)
End Notes
---------
Linux Quake is *NOT* an officially supported product. Mail about it
will be deleted. Do not email id about this product. If you are having
technical difficultly, you can email me, but make sure you have the correct
kernel, libc, svgalib and other software versions before you email me.
Dave 'Zoid' Kirsch
zoid@idsoftware.com
Official Quake Unix Port Administrator

107
NQ/docs/README.X11 Normal file
View File

@ -0,0 +1,107 @@
README for Linux SVGALib Quake
------------------------------
Requirements:
- X11R5 later, only tested with XFree86, should work with most X Servers
- libc 5.2.18 or later (5.0.9 will not work, /lib/libc.so.5.2.18)
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib supported mouse (usually if it works with X, it'll work with
squake).
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
This is a windowed version that is generic for X11. It runs in a window
and can be resized. You can specify a starting window size with:
-width <width>
-height <height>
-winsize <width> <height>
Default is 320x200. It works in 16bit modes, but it's slower (twice as many
bytes to copy).
No other video modes are supported (just runs windowed). Mouse is read, but
not "grabbed" by default. Go to the Options menu and turn on Use Mouse to grab
the mouse and use it in the game. If you want to move the mouse out of
QWCL, you have to turn Use Mouse off.
Full sound support is included. The default sound rate is 16-bit stereo,
11KHz. You can change this in the options section below.
New Command Line Options for Linux SVGAlib Quake
------------------------------------------------
-mem <mb>
Specify memory in megabytes to allocate (default is 8MB, which should be fine
for most needs).
-nostdout
Don't do any output to stdout
-cddev <device>
CD device, default is /dev/cdrom
-sndbits <8 or 16>
Set sound bit sample size. Default is 16 if supported.
-sndspeed <speed>
Set sound speed. Usual values are 8000, 11025, 22051 and 44100.
Default is 11025.
-sndmono
Set mono sound
-sndstereo
Set stereo sound (default if supported)
Installation
------------
Boot DOS (I know, but you need it to run the Quake install program) and
install Quake from your Quake CD to a DOS parition.
Boot Linux and make a directory for Quake. Copy everything from the DOS Quake
directory into it. i.e.:
(cd /dos/quake; tar cf - .) | (cd ~/quake; tar xf -)
Quake for X11 does not need to be setuid root. Sound can fail if /dev/dsp is
not mode 666.
Quake may segfault if it tries to initialize your sound card and their isn't
one. Same with the CDROM. If it dies, try it with -nosound and/or
-nocdaudio. If you have a sound card it died on and you know it is
supported by USSLite (the driver that comes with the Linux kernel), let me
know and I'll take a look at it.
It should work with SCSI CDROMs, but is untested.
Full TCP/IP network support is in, including listen and dedicated server
modes.
All of the options described in TECHINFO.TXT and MANUAL.TXT from the Quake
distribution will work, 'cept for stuff with vid modes and stuff.
End Notes
---------
Linux Quake is *NOT* an officially supported product. Mail about it
will be deleted. Do not email id about this product. If you are having
technical difficultly, you can email me, but make sure you have the correct
kernel, libc, svgalib and other software versions before you email me.
Dave 'Zoid' Kirsch
zoid@idsoftware.com
Official Quake Unix Port Administrator
Acks
----
Greg Alexander <galexand@sietch.bloomington.in.us> for initial work in SVGALib
support.
Dave Taylor <ddt@crack.com> for basic Linux support.
id Software for Quake and making me port it. :)
Lots of people on #linux, #quake for testing.

162
NQ/docs/readme.glquake Normal file
View File

@ -0,0 +1,162 @@
Linux Glquake v0.98, Quake v1.09 release notes
Requirements
------------
For 3DFX based hardware, you must download and install Linux GLIDE from
http://glide.xxedgexx.com/3DfxRPMS.html and install as per the
instructions.
Running GLQuake
---------------
There are three different ways to execute GLQuake:
1. The binary "glquake" requires Mesa 3-D 2.5 or later installed and compiled
with 3DFX support (fxMesa..() function interface). It also requires
svgalib 1.3.0 or later for keyboard/mouse input. This binary is a console
application. Mesa 3-D requires GLIDE to be installed.
2. The shell script "glquake.3dfxgl" runs the "glquake" binary after
preloading the lib3dfxgl.so library. This is a port of 3DFX's Win32
OpenGL MCD (Mini Client Driver) to Linux. It is faster than Mesa 3-D
since it was written specifically with supporting GLQuake in mind.
lib3dfxgl.so requires that GLIDE be installed.
3. The binary "glquake.glx" is linked against standard OpenGL libraries.
It should run on many different hardward OpenGL implementations under
Linux and X11. This binary is an X11 application and must be run under
X11. It will work with Mesa 3-D as a standard glX based OpenGL
applications. If the Mesa 3-D library is compiled with 3DFX support,
you can have Mesa 3-D support 3DFX hardware under X11 by setting the
enviroment variable "MESA_GLX_FX" to "fullscreen" for fullscreen mode
and "window" for windowed mode.
You must also have SVGALib 1.3.0 or later installed. GLQuake uses SVGALib
for mouse and keyboard handling.
If you have gpm and/or selection running, you will have to terminate them
before running GLQuake since they will not give up the mouse when GLQuake
attempts to run. You can kill gpm by typing 'killall gpm' as root.
You must run GLQuake as root or setuid root since it needs to access things
such as sound, keyboard, mouse and the 3DFX video. Future versions may not
require root permissions.
resolution options
------------------
glquake -width 512 -height 384
Tries to run glquake at the specified resolution.
Only highend VooDoo cards support such high resolutions (most
cards on the market right now do not). Another popular and supported mode
is 512x384 (-width 512 -height 384) which can offer a faster speed than
the default 640x480.
You can also specify the resolution of the console independant of the screen
resolution.
glquake -conwidth 320
This will specify a console resolution of 320 by 240 (the height is
automatically determined by the default 4:3 aspect ratio, you can also
specify the height directly with -conheight).
In higher resolution modes such as 800x600 and 1024x768, glquake will default
to a 640x480 console, since the font becomes small enough at higher
resolutions to become unreadable. If do you wish to have a higher resolution
console and status bar, specify it as well, such as:
glquake -width 800 -height 600 -conwidth 800
texture options
---------------
The amount of textures used in the game can have a large impact on performance.
There are several options that let you trade off visual quality for better
performance.
There is no way to flush already loaded textures, so it is best to change
these options on the command line, or they will only take effect on some of
the textures when you change levels.
OpenGL only allows textures to repeat on power of two boundaries (32, 64,
128, etc), but software quake had a number of textures that repeated at 24
or 96 pixel boundaries. These need to be either stretched out to the next
higher size, or shrunk down to the next lower. By default, they are filtered
down to the smaller size, but you can cause it to use the larger size if you
really want by using:
glquake +gl_round_down 0
This will generally run well on a normal 4 MB 3dfx card, but for other cards
that have either worse texture management or slower texture swapping speeds,
there are some additional settings that can drastically lower the amount of
textures to be managed.
glquake +gl_picmip 1
This causes all textures to have one half the dimensions they otherwise would.
This makes them blurry, but very small. You can set this to 2 to make the
textures one quarter the resolution on each axis for REALLY blurry textures.
glquake +gl_playermip 1
This is similar to picmip, but is only used for other players in deathmatch.
Each player in a deathmatch requires an individual skin texture, so this can
be a serious problem for texture management. It wouldn't be unreasonable to
set this to 2 or even 3 if you are playing competatively (and don't care if
the other guys have smudged skins). If you change this during the game, it
will take effect as soon as a player changes their skin colors.
run time options
----------------
At the console, you can set these values to effect drawing.
gl_texturemode GL_NEAREST
Sets texture mapping to point sampled, which may be faster on some GL systems
(not on 3dfx).
gl_texturemode GL_LINEAR_MIPMAP
This is the default texture mode.
gl_texturemode GL_LINEAR_MIPMAP_LINEAR
This is the highest quality texture mapping (trilinear), but only very high
end hardware (intergraph intense 3D / realizm) supports it. Not that big of
a deal, actually.
gl_finish 0
This causes the game to not issue a glFinish() call each frame, which may make
some hardware run faster. If this is cleared, the 3dfx will back up a number
of frames and not be very playable.
gl_flashblend 0
By default, glquake just draws a shaded ball around objects that are emiting
light. Clearing this variable will cause it to properly relight the world
like normal quake, but it can be a significant speed hit on some systems.
gl_ztrick 0
Glquake uses a buffering method that avoids clearing the Z buffer, but some
hardware platforms don't like it. If the status bar and console are flashing
every other frame, clear this variable.
gl_keeptjunctions 0
If you clear this, glquake will remove colinear vertexes when it reloads the
level. This can give a few percent speedup, but it can leave a couple stray
blinking pixels on the screen.
novelty features
----------------
These are some rendering tricks that were easy to do in glquake. They aren't
very robust, but they are pretty cool to look at.
r_shadows 1
This causes every object to cast a shadow.
r_wateralpha 0.7
This sets the opacity of water textures, so you can see through it in properly
processed maps. 0.3 is very faint, almost like fog. 1 is completely solid
(the default). Unfortunately, the standard quake maps don't contain any
visibility information for seeing past water surfaces, so you can't just play
quake with this turned on. If you just want to see what it looks like, you
can set "r_novis 1", but that will make things go very slow. When I get a
chance, I will probably release some maps that have been processed properly
for this.
r_mirroralpha 0.3
This changes one particular texture (the stained glass texture in the EASY
start hall) into a mirror. The value is the opacity of the mirror surface.

127
NQ/docs/readme.squake Normal file
View File

@ -0,0 +1,127 @@
README for Linux SVGALib Quake
------------------------------
Requirements:
- SVGALib 1.20 or later (/lib/libvga.so.1.2.10)
- libc 5.2.18 or later (5.0.9 will not work, /lib/libc.so.5.2.18)
- CD-ROM for CDAudio
- Soundcard capable of mmap'd buffers. USSLite 3.5.4 was used to build squake
with. Works fine on SoundBlaster 16 and Gravis Ultrasound MAX.
- SVGALib supported mouse (usually if it works with X, it'll work with
squake).
- Kernel 2.0.24 or later
- untested with 2.1 kernels, your mileage may vary
Here's the release you've been waiting for. Linux squake supports
320x200x256, the various modeX modes (320x400, 360x400, etc) as well as high
res modes if your card is supported by SVGALib. Use the Quake console command
vid_describemodes to list supported modes and the command vid_mode <number> to
change modes.
Full sound support is included. The default sound rate is 16-bit stereo,
11KHz. You can change this in the options section below.
Mouse works great, but SVGALib may not detect a 3-button mouse properly (it
will only use two buttons). Check your /etc/libvga.config (or
/etc/vga/libvga.config for SlackWare users).
**Version 1.1 fixes some crash bugs with the mission packs.
New Command Line Options for Linux SVGAlib Quake
------------------------------------------------
-mem <mb>
Specify memory in megabytes to allocate (default is 8MB, which should be fine
for most needs).
-nostdout
Don't do any output to stdout
-mdev <device>
Mouse device, default is /dev/mouse
-mrate <speed>
Mouse baud rate, default is 1200
-cddev <device>
CD device, default is /dev/cdrom
-mode <modenum>
Use indicated video mode
-nokdb
Don't initialize keyboard
-sndbits <8 or 16>
Set sound bit sample size. Default is 16 if supported.
-sndspeed <speed>
Set sound speed. Usual values are 8000, 11025, 22051 and 44100.
Default is 11025.
-sndmono
Set mono sound
-sndstereo
Set stereo sound (default if supported)
Installation
------------
Boot DOS (I know, but you need it to run the Quake install program) and
install Quake from your Quake CD to a DOS parition.
Boot Linux and make a directory for Quake. Copy everything from the DOS Quake
directory into it. i.e.:
(cd /dos/quake; tar cf - .) | (cd ~/quake; tar xf -)
Place squake into your Quake directory. You must make it setuid root (since
Quake access stuff like direct video writes, the raw keyboard mode, CD, etc).
Quake will setuid back to the normal user as soon as it opens these files.
Make Quake suid root as follows:
chown root squake
chmod 4755 squake
Run squake. I don't recommend running it as root, since all the saved
config.cfg files will be then owned as root. Use your normal account, unless
you do everything as root, then your mileage will vary.
squake may segfault if it tries to initialize your sound card and their isn't
one. Same with the CDROM. If it dies, try it with -nosound and/or
-nocdaudio. If you have a sound card it died on and you know it is
supported by USSLite (the driver that comes with the Linux kernel), let me
know and I'll take a look at it.
It should work with SCSI CDROMs, but is untested.
Full TCP/IP network support is in, including listen and dedicated server
modes. squake makes a nice dedicated server as you don't need the X11
libraries kicking around.
All of the options described in TECHINFO.TXT and MANUAL.TXT from the Quake
distribution will work, 'cept for stuff with vid modes and stuff.
End Notes
---------
Linux SVGALib Quake is *NOT* an officially supported product. Mail about it
will be deleted. Do not email id about this product. If you are having
technical difficultly, you can email me, but make sure you have the correct
kernel, libc, svgalib and other software versions before you email me.
Dave 'Zoid' Kirsch
zoid@threewave.com
Official Quake Unix Port Administrator
Acks
----
Greg Alexander <galexand@sietch.bloomington.in.us> for initial work in SVGALib
support.
Dave Taylor <ddt@crack.com> for basic Linux support.
id Software for Quake and making me port it. :)
Lots of people on #linux, #quake for testing.

77
NQ/dosasm.S Normal file
View File

@ -0,0 +1,77 @@
#include "asm_i386.h"
.data
fpenv: .long 0, 0, 0, 0, 0, 0, 0, 0
.text
.globl C(StartMSRInterval)
C(StartMSRInterval):
movl $0x11,%ecx // read the CESR
.byte 0x0F
.byte 0x32 // RDMSR
andl $0xFE3FFE3F,%eax // stop both counters
.byte 0x0F
.byte 0x30 // WRMSR
movl 4(%esp),%eax // point counter 0 to desired event, with counters
andl $0x3F,%eax // still stopped
movl $0x11,%ecx
.byte 0x0F
.byte 0x30 // WRMSR
movl $0x12,%ecx // set counter 0 to the value 0
subl %eax,%eax
subl %edx,%edx
.byte 0x0F
.byte 0x30 // WRMSR
movl 4(%esp),%eax // restart counter 0 with selected event
andl $0x3F,%eax
subl %edx,%edx
orl $0xC0,%eax
movl $0x11,%ecx // control and event select
.byte 0x0F
.byte 0x30 // WRMSR
ret
.globl C(EndMSRInterval)
C(EndMSRInterval):
movl $0x12,%ecx // counter 0
.byte 0x0F
.byte 0x32 // RDMSR
ret // lower 32 bits of count in %eax
#if 0
.data
Lxxx: .long 0
.text
.globl C(setstackcheck)
C(setstackcheck):
movl %esp,%eax
subl $0x38000,%eax
movl $0x5A5A5A5A,(%eax)
movl %eax,Lxxx
ret
.globl C(dostackcheck)
C(dostackcheck):
movl Lxxx,%edx
movl $0,%eax
cmpl $0x5A5A5A5A,(%edx)
jz qqq
incl %eax
qqq:
ret
#endif

99
NQ/dosisms.h Normal file
View File

@ -0,0 +1,99 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef DOSISMS_H
#define DOSISMS_H
//
// dosisms.h: I'd call it dos.h, but the name's taken
//
int dos_lockmem(void *addr, int size);
int dos_unlockmem(void *addr, int size);
typedef union {
struct {
unsigned long edi;
unsigned long esi;
unsigned long ebp;
unsigned long res;
unsigned long ebx;
unsigned long edx;
unsigned long ecx;
unsigned long eax;
} d;
struct {
unsigned short di, di_hi;
unsigned short si, si_hi;
unsigned short bp, bp_hi;
unsigned short res, res_hi;
unsigned short bx, bx_hi;
unsigned short dx, dx_hi;
unsigned short cx, cx_hi;
unsigned short ax, ax_hi;
unsigned short flags;
unsigned short es;
unsigned short ds;
unsigned short fs;
unsigned short gs;
unsigned short ip;
unsigned short cs;
unsigned short sp;
unsigned short ss;
} x;
struct {
unsigned char edi[4];
unsigned char esi[4];
unsigned char ebp[4];
unsigned char res[4];
unsigned char bl, bh, ebx_b2, ebx_b3;
unsigned char dl, dh, edx_b2, edx_b3;
unsigned char cl, ch, ecx_b2, ecx_b3;
unsigned char al, ah, eax_b2, eax_b3;
} h;
} regs_t;
unsigned int ptr2real(void *ptr);
void *real2ptr(unsigned int real);
void *far2ptr(unsigned int farptr);
unsigned int ptr2far(void *ptr);
int dos_inportb(int port);
int dos_inportw(int port);
void dos_outportb(int port, int val);
void dos_outportw(int port, int val);
void dos_irqenable(void);
void dos_irqdisable(void);
void dos_registerintr(int intr, void (*handler) (void));
void dos_restoreintr(int intr);
int dos_int86(int vec);
void *dos_getmemory(int size);
void dos_freememory(void *ptr);
void dos_usleep(int usecs);
int dos_getheapsize(void);
extern regs_t regs;
#endif /* DOSISMS_H */

1075
NQ/gas2masm/gas2masm.c Normal file

File diff suppressed because it is too large Load Diff

418
NQ/gl_model.h Normal file
View File

@ -0,0 +1,418 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef GL_MODEL_H
#define GL_MODEL_H
#include "modelgen.h"
#include "spritegn.h"
#include "bspfile.h"
#include "render.h"
#include "zone.h"
/*
d*_t structures are on-disk representations
m*_t structures are in-memory
*/
// entity effects
#define EF_BRIGHTFIELD 1
#define EF_MUZZLEFLASH 2
#define EF_BRIGHTLIGHT 4
#define EF_DIMLIGHT 8
/*
==============================================================================
BRUSH MODELS
==============================================================================
*/
//
// in memory representation
//
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct {
vec3_t position;
} mvertex_t;
#define SIDE_FRONT 0
#define SIDE_BACK 1
#define SIDE_ON 2
// plane_t structure
// !!! if this is changed, it must be changed in asm_i386.h too !!!
typedef struct mplane_s {
vec3_t normal;
float dist;
byte type; // for texture axis selection and fast side tests
byte signbits; // signx + signy<<1 + signz<<1
byte pad[2];
} mplane_t;
typedef struct texture_s {
char name[16];
unsigned width, height;
int gl_texturenum;
struct msurface_s *texturechain; // for gl_texsort drawing
int anim_total; // total tenths in sequence ( 0 = no)
int anim_min, anim_max; // time for this frame min <=time< max
struct texture_s *anim_next; // in the animation sequence
struct texture_s *alternate_anims; // bmodels in frmae 1 use these
unsigned offsets[MIPLEVELS]; // four mip maps stored
} texture_t;
#define SURF_PLANEBACK 2
#define SURF_DRAWSKY 4
#define SURF_DRAWSPRITE 8
#define SURF_DRAWTURB 0x10
#define SURF_DRAWTILED 0x20
#define SURF_DRAWBACKGROUND 0x40
#define SURF_UNDERWATER 0x80
#define SURF_DONTWARP 0x100
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct {
unsigned short v[2];
unsigned int cachededgeoffset;
} medge_t;
typedef struct {
float vecs[2][4];
float mipadjust;
texture_t *texture;
int flags;
} mtexinfo_t;
#define VERTEXSIZE 7
typedef struct glpoly_s {
struct glpoly_s *next;
struct glpoly_s *chain;
int numverts;
int flags; // for SURF_UNDERWATER
float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2)
} glpoly_t;
typedef struct msurface_s {
int visframe; // should be drawn when node is crossed
mplane_t *plane;
int flags;
int firstedge; // look up in model->surfedges[], negative numbers
int numedges; // are backwards edges
short texturemins[2];
short extents[2];
int light_s, light_t; // gl lightmap coordinates
glpoly_t *polys; // multiple if warped
struct msurface_s *texturechain;
mtexinfo_t *texinfo;
// lighting info
int dlightframe;
unsigned dlightbits;
int lightmaptexturenum;
byte styles[MAXLIGHTMAPS];
int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap
qboolean cached_dlight; // true if dynamic light in cache
byte *samples; // [numstyles*surfsize]
} msurface_t;
typedef struct mnode_s {
// common with leaf
int contents; // 0, to differentiate from leafs
int visframe; // node needs to be traversed if current
float minmaxs[6]; // for bounding box culling
struct mnode_s *parent;
// node specific
mplane_t *plane;
struct mnode_s *children[2];
unsigned short firstsurface;
unsigned short numsurfaces;
} mnode_t;
typedef struct mleaf_s {
// common with node
int contents; // wil be a negative contents number
int visframe; // node needs to be traversed if current
float minmaxs[6]; // for bounding box culling
struct mnode_s *parent;
// leaf specific
byte *compressed_vis;
efrag_t *efrags;
msurface_t **firstmarksurface;
int nummarksurfaces;
int key; // BSP sequence number for leaf's contents
byte ambient_sound_level[NUM_AMBIENTS];
} mleaf_t;
// !!! if this is changed, it must be changed in asm_i386.h too !!!
typedef struct {
dclipnode_t *clipnodes;
mplane_t *planes;
int firstclipnode;
int lastclipnode;
vec3_t clip_mins;
vec3_t clip_maxs;
} hull_t;
/*
==============================================================================
SPRITE MODELS
==============================================================================
*/
// FIXME: shorten these?
typedef struct mspriteframe_s {
int width;
int height;
float up, down, left, right;
int gl_texturenum;
} mspriteframe_t;
typedef struct {
int numframes;
float *intervals;
mspriteframe_t *frames[1];
} mspritegroup_t;
typedef struct {
spriteframetype_t type;
mspriteframe_t *frameptr;
} mspriteframedesc_t;
typedef struct {
int type;
int maxwidth;
int maxheight;
int numframes;
float beamlength; // remove?
void *cachespot; // remove?
mspriteframedesc_t frames[1];
} msprite_t;
/*
==============================================================================
ALIAS MODELS
Alias models are position independent, so the cache manager can move them.
==============================================================================
*/
typedef struct {
int firstpose;
int numposes;
float interval;
trivertx_t bboxmin;
trivertx_t bboxmax;
int frame;
char name[16];
} maliasframedesc_t;
typedef struct {
trivertx_t bboxmin;
trivertx_t bboxmax;
int frame;
} maliasgroupframedesc_t;
typedef struct {
int numframes;
int intervals;
maliasgroupframedesc_t frames[1];
} maliasgroup_t;
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct mtriangle_s {
int facesfront;
int vertindex[3];
} mtriangle_t;
#define MAX_SKINS 32
typedef struct {
int ident;
int version;
vec3_t scale;
vec3_t scale_origin;
float boundingradius;
vec3_t eyeposition;
int numskins;
int skinwidth;
int skinheight;
int numverts;
int numtris;
int numframes;
synctype_t synctype;
int flags;
float size;
int numposes;
int poseverts;
int posedata; // numposes*poseverts trivert_t
int commands; // gl command list with embedded s/t
int gl_texturenum[MAX_SKINS][4];
int texels[MAX_SKINS]; // only for player skins
maliasframedesc_t frames[1]; // variable sized
} aliashdr_t;
#define MAXALIASVERTS 2048
#define MAXALIASFRAMES 512
#define MAXALIASTRIS 4096
extern aliashdr_t *pheader;
extern stvert_t stverts[MAXALIASVERTS];
extern mtriangle_t triangles[MAXALIASTRIS];
extern trivertx_t *poseverts[MAXALIASFRAMES];
//===================================================================
//
// Whole model
//
typedef enum { mod_brush, mod_sprite, mod_alias } modtype_t;
#define EF_ROCKET 1 // leave a trail
#define EF_GRENADE 2 // leave a trail
#define EF_GIB 4 // leave a trail
#define EF_ROTATE 8 // rotate (bonus items)
#define EF_TRACER 16 // green split trail
#define EF_ZOMGIB 32 // small blood trail
#define EF_TRACER2 64 // orange split trail + rotate
#define EF_TRACER3 128 // purple trail
typedef struct model_s {
char name[MAX_QPATH];
qboolean needload; // bmodels and sprites don't cache normally
modtype_t type;
int numframes;
synctype_t synctype;
int flags;
//
// volume occupied by the model graphics
//
vec3_t mins, maxs;
float radius;
//
// solid volume for clipping
//
qboolean clipbox;
vec3_t clipmins, clipmaxs;
//
// brush model
//
int firstmodelsurface, nummodelsurfaces;
int numsubmodels;
dmodel_t *submodels;
int numplanes;
mplane_t *planes;
int numleafs; // number of visible leafs, not counting 0
mleaf_t *leafs;
int numvertexes;
mvertex_t *vertexes;
int numedges;
medge_t *edges;
int numnodes;
mnode_t *nodes;
int numtexinfo;
mtexinfo_t *texinfo;
int numsurfaces;
msurface_t *surfaces;
int numsurfedges;
int *surfedges;
int numclipnodes;
dclipnode_t *clipnodes;
int nummarksurfaces;
msurface_t **marksurfaces;
hull_t hulls[MAX_MAP_HULLS];
int numtextures;
texture_t **textures;
byte *visdata;
byte *lightdata;
char *entities;
//
// additional model data
//
cache_user_t cache; // only access through Mod_Extradata
} model_t;
//============================================================================
void Mod_Init(void);
void Mod_ClearAll(void);
model_t *Mod_ForName(char *name, qboolean crash);
void *Mod_Extradata(model_t *mod); // handles caching
void Mod_TouchModel(char *name);
void Mod_Print(void);
mleaf_t *Mod_PointInLeaf(float *p, model_t *model);
byte *Mod_LeafPVS(mleaf_t *leaf, model_t *model);
// FIXME - surely this doesn't belong here?
texture_t *R_TextureAnimation(texture_t *base);
#endif /* GL_MODEL_H */

1182
NQ/gl_rmain.c Normal file

File diff suppressed because it is too large Load Diff

471
NQ/gl_rmisc.c Normal file
View File

@ -0,0 +1,471 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_misc.c
#include "cmd.h"
#include "console.h"
#include "glquake.h"
#include "protocol.h"
#include "quakedef.h"
#include "sys.h"
// FIXME - should only be needed in r_part.c or here, not both.
int particletexture;
/*
==================
R_InitTextures
==================
*/
void
R_InitTextures(void)
{
int x, y, m;
byte *dest;
// create a simple checkerboard texture for the default
r_notexture_mip =
Hunk_AllocName(sizeof(texture_t) + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2,
"notexture");
r_notexture_mip->width = r_notexture_mip->height = 16;
r_notexture_mip->offsets[0] = sizeof(texture_t);
r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16 * 16;
r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8 * 8;
r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4 * 4;
for (m = 0; m < 4; m++) {
dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m];
for (y = 0; y < (16 >> m); y++) {
for (x = 0; x < (16 >> m); x++) {
if ((y < (8 >> m)) ^ (x < (8 >> m)))
*dest++ = 0;
else
*dest++ = 0xff;
}
}
}
}
static byte dottexture[8][8] = {
{0, 1, 1, 0, 0, 0, 0, 0},
{1, 1, 1, 1, 0, 0, 0, 0},
{1, 1, 1, 1, 0, 0, 0, 0},
{0, 1, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
};
static void
R_InitParticleTexture(void)
{
int x, y;
byte data[8][8][4];
//
// particle texture
//
particletexture = texture_extension_number++;
GL_Bind(particletexture);
for (x = 0; x < 8; x++) {
for (y = 0; y < 8; y++) {
data[y][x][0] = 255;
data[y][x][1] = 255;
data[y][x][2] = 255;
data[y][x][3] = dottexture[x][y] * 255;
}
}
glTexImage2D(GL_TEXTURE_2D, 0, gl_alpha_format, 8, 8, 0, GL_RGBA,
GL_UNSIGNED_BYTE, data);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
/*
===============
R_Envmap_f
Grab six views for environment mapping tests
===============
*/
static void
R_Envmap_f(void)
{
byte buffer[256 * 256 * 4];
glDrawBuffer(GL_FRONT);
glReadBuffer(GL_FRONT);
envmap = true;
r_refdef.vrect.x = 0;
r_refdef.vrect.y = 0;
r_refdef.vrect.width = 256;
r_refdef.vrect.height = 256;
r_refdef.viewangles[0] = 0;
r_refdef.viewangles[1] = 0;
r_refdef.viewangles[2] = 0;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env0.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[1] = 90;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env1.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[1] = 180;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env2.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[1] = 270;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env3.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[0] = -90;
r_refdef.viewangles[1] = 0;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env4.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[0] = 90;
r_refdef.viewangles[1] = 0;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env5.rgb", buffer, sizeof(buffer));
envmap = false;
glDrawBuffer(GL_BACK);
glReadBuffer(GL_BACK);
GL_EndRendering();
}
// FIXME - locate somewhere else?
cvar_t r_lockpvs = { "r_lockpvs", "0" };
cvar_t r_lockfrustum = { "r_lockfrustum", "0" };
cvar_t r_drawflat = { "r_drawflat", "0" };
/*
===============
R_Init
===============
*/
void
R_Init(void)
{
Cmd_AddCommand("envmap", R_Envmap_f);
Cmd_AddCommand("pointfile", R_ReadPointFile_f);
Cmd_AddCommand("timerefresh", R_TimeRefresh_f);
Cvar_RegisterVariable(&r_speeds);
Cvar_RegisterVariable(&r_fullbright);
Cvar_RegisterVariable(&r_drawentities);
Cvar_RegisterVariable(&r_drawviewmodel);
Cvar_RegisterVariable(&r_drawflat);
Cvar_RegisterVariable(&r_lockpvs);
Cvar_RegisterVariable(&r_lockfrustum);
Cvar_RegisterVariable(&r_norefresh);
Cvar_RegisterVariable(&r_lightmap);
Cvar_RegisterVariable(&r_shadows);
Cvar_RegisterVariable(&r_mirroralpha);
Cvar_RegisterVariable(&r_wateralpha);
Cvar_RegisterVariable(&r_dynamic);
Cvar_RegisterVariable(&r_novis);
Cvar_RegisterVariable(&r_waterwarp);
Cvar_RegisterVariable(&gl_finish);
Cvar_RegisterVariable(&gl_clear);
Cvar_RegisterVariable(&gl_texsort);
Cvar_RegisterVariable(&_gl_lightmap_sort);
Cvar_RegisterVariable(&_gl_sky_mtex);
Cvar_RegisterVariable(&_gl_drawhull);
Cvar_RegisterVariable(&gl_cull);
Cvar_RegisterVariable(&gl_smoothmodels);
Cvar_RegisterVariable(&gl_affinemodels);
Cvar_RegisterVariable(&gl_polyblend);
Cvar_RegisterVariable(&gl_flashblend);
Cvar_RegisterVariable(&gl_playermip);
Cvar_RegisterVariable(&gl_nocolors);
Cvar_RegisterVariable(&gl_keeptjunctions);
Cvar_RegisterVariable(&gl_reporttjunctions);
Cvar_RegisterVariable(&gl_doubleeyes);
if (gl_mtexable)
Cvar_SetValue("gl_texsort", 0.0);
R_InitBubble();
R_InitParticles();
R_InitParticleTexture();
playertextures = texture_extension_number;
texture_extension_number += MAX_CLIENTS;
}
/*
===============
R_TranslatePlayerSkin
Translates a skin texture by the per-player color lookup
===============
*/
void
R_TranslatePlayerSkin(int playernum)
{
int top, bottom;
byte translate[256];
unsigned translate32[256];
int i, j, s;
model_t *model;
aliashdr_t *paliashdr;
byte *original;
unsigned pixels[512 * 256], *out;
unsigned scaled_width, scaled_height;
int inwidth, inheight;
byte *inrow;
unsigned frac, fracstep;
GL_DisableMultitexture();
top = cl.scores[playernum].colors & 0xf0;
bottom = (cl.scores[playernum].colors & 15) << 4;
for (i = 0; i < 256; i++)
translate[i] = i;
for (i = 0; i < 16; i++) {
if (top < 128) // the artists made some backwards ranges. sigh.
translate[TOP_RANGE + i] = top + i;
else
translate[TOP_RANGE + i] = top + 15 - i;
if (bottom < 128)
translate[BOTTOM_RANGE + i] = bottom + i;
else
translate[BOTTOM_RANGE + i] = bottom + 15 - i;
}
//
// locate the original skin pixels
//
currententity = &cl_entities[1 + playernum];
model = currententity->model;
if (!model)
return; // player doesn't have a model yet
if (model->type != mod_alias)
return; // only translate skins on alias models
paliashdr = (aliashdr_t *)Mod_Extradata(model);
s = paliashdr->skinwidth * paliashdr->skinheight;
if (currententity->skinnum < 0
|| currententity->skinnum >= paliashdr->numskins) {
Con_Printf("(%d): Invalid player skin #%d\n", playernum,
currententity->skinnum);
original = (byte *)paliashdr + paliashdr->texels[0];
} else
original =
(byte *)paliashdr + paliashdr->texels[currententity->skinnum];
if (s & 3)
Sys_Error("%s: s&3", __func__);
inwidth = paliashdr->skinwidth;
inheight = paliashdr->skinheight;
// because this happens during gameplay, do it fast
// instead of sending it through gl_upload 8
GL_Bind(playertextures + playernum);
#if 0
byte translated[320 * 200];
for (i = 0; i < s; i += 4) {
translated[i] = translate[original[i]];
translated[i + 1] = translate[original[i + 1]];
translated[i + 2] = translate[original[i + 2]];
translated[i + 3] = translate[original[i + 3]];
}
// don't mipmap these, because it takes too long
GL_Upload8(translated, paliashdr->skinwidth, paliashdr->skinheight,
false, false, true);
#else
// allow users to crunch sizes down
scaled_width = 512 >> (int)gl_playermip.value;
scaled_height = 256 >> (int)gl_playermip.value;
// make sure not still too big
scaled_width = gl_max_size.value < scaled_width
? gl_max_size.value : scaled_width;
scaled_height = gl_max_size.value < scaled_height
? gl_max_size.value : scaled_height;
if (VID_Is8bit()) { // 8bit texture upload
byte *out2;
out2 = (byte *)pixels;
memset(pixels, 0, sizeof(pixels));
fracstep = inwidth * 0x10000 / scaled_width;
for (i = 0; i < scaled_height; i++, out2 += scaled_width) {
inrow = original + inwidth * (i * inheight / scaled_height);
frac = fracstep >> 1;
for (j = 0; j < scaled_width; j += 4) {
out2[j] = translate[inrow[frac >> 16]];
frac += fracstep;
out2[j + 1] = translate[inrow[frac >> 16]];
frac += fracstep;
out2[j + 2] = translate[inrow[frac >> 16]];
frac += fracstep;
out2[j + 3] = translate[inrow[frac >> 16]];
frac += fracstep;
}
}
GL_Upload8_EXT((byte *)pixels, scaled_width, scaled_height, false,
false);
return;
}
for (i = 0; i < 256; i++)
translate32[i] = d_8to24table[translate[i]];
out = pixels;
fracstep = inwidth * 0x10000 / scaled_width;
for (i = 0; i < scaled_height; i++, out += scaled_width) {
inrow = original + inwidth * (i * inheight / scaled_height);
frac = fracstep >> 1;
for (j = 0; j < scaled_width; j += 4) {
out[j] = translate32[inrow[frac >> 16]];
frac += fracstep;
out[j + 1] = translate32[inrow[frac >> 16]];
frac += fracstep;
out[j + 2] = translate32[inrow[frac >> 16]];
frac += fracstep;
out[j + 3] = translate32[inrow[frac >> 16]];
frac += fracstep;
}
}
glTexImage2D(GL_TEXTURE_2D, 0, gl_solid_format, scaled_width,
scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#endif
}
/*
===============
R_NewMap
===============
*/
void
R_NewMap(void)
{
int i;
for (i = 0; i < 256; i++)
d_lightstylevalue[i] = 264; // normal light value
memset(&r_worldentity, 0, sizeof(r_worldentity));
r_worldentity.model = cl.worldmodel;
// clear out efrags in case the level hasn't been reloaded
// FIXME: is this one short?
for (i = 0; i < cl.worldmodel->numleafs; i++)
cl.worldmodel->leafs[i].efrags = NULL;
r_viewleaf = NULL;
R_ClearParticles();
GL_BuildLightmaps();
// identify sky texture
skytexturenum = -1;
mirrortexturenum = -1;
for (i = 0; i < cl.worldmodel->numtextures; i++) {
if (!cl.worldmodel->textures[i])
continue;
if (!strncmp(cl.worldmodel->textures[i]->name, "sky", 3))
skytexturenum = i;
if (!strncmp(cl.worldmodel->textures[i]->name, "window02_1", 10))
mirrortexturenum = i;
cl.worldmodel->textures[i]->texturechain = NULL;
}
}
/*
====================
R_TimeRefresh_f
For program optimization
====================
*/
void
R_TimeRefresh_f(void)
{
int i;
float start, stop, time;
glDrawBuffer(GL_FRONT);
glFinish();
start = Sys_DoubleTime();
for (i = 0; i < 128; i++) {
r_refdef.viewangles[1] = i / 128.0 * 360.0;
R_RenderView();
}
glFinish();
stop = Sys_DoubleTime();
time = stop - start;
Con_Printf("%f seconds (%f fps)\n", time, 128 / time);
glDrawBuffer(GL_BACK);
GL_EndRendering();
}
void
D_FlushCaches(void)
{
}

910
NQ/gl_screen.c Normal file
View File

@ -0,0 +1,910 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// screen.c -- master for refresh, status bar, console, chat, notify, etc
#include "cmd.h"
#include "console.h"
#include "draw.h"
#include "glquake.h"
#include "host.h"
#include "keys.h"
#include "menu.h"
#include "quakedef.h"
#include "sbar.h"
#include "screen.h"
#include "sound.h"
#include "sys.h"
#include "view.h"
#include "wad.h"
/*
background clear
rendering
turtle/net/ram icons
sbar
centerprint / slow centerprint
notify lines
intermission / finale overlay
loading plaque
console
menu
required background clears
required update regions
syncronous draw mode or async
One off screen buffer, with updates either copied or xblited
Need to double buffer?
async draw will require the refresh area to be cleared, because it will be
xblited, but sync draw can just ignore it.
sync
draw
CenterPrint()
SlowPrint()
Screen_Update();
Con_Printf();
net
turn off messages option
the refresh is always rendered, unless the console is full screen
console is:
notify lines
half
full
*/
int glx, gly, glwidth, glheight;
// only the refresh window will be updated unless these variables are flagged
int scr_copytop;
int scr_copyeverything;
float scr_con_current;
float scr_conlines; // lines of console to display
float oldscreensize, oldfov;
cvar_t scr_viewsize = { "viewsize", "100", true };
cvar_t scr_fov = { "fov", "90" }; // 10 - 170
cvar_t scr_conspeed = { "scr_conspeed", "300" };
cvar_t scr_centertime = { "scr_centertime", "2" };
cvar_t scr_showram = { "showram", "1" };
cvar_t scr_showturtle = { "showturtle", "0" };
cvar_t scr_showpause = { "showpause", "1" };
cvar_t scr_printspeed = { "scr_printspeed", "8" };
cvar_t gl_triplebuffer = { "gl_triplebuffer", "1", true };
qboolean scr_initialized; // ready to draw
qpic_t *scr_ram;
qpic_t *scr_net;
qpic_t *scr_turtle;
int scr_fullupdate;
int clearconsole;
int clearnotify;
int sb_lines;
viddef_t vid; // global video state
vrect_t scr_vrect;
qboolean scr_disabled_for_loading;
qboolean scr_drawloading;
float scr_disabled_time;
qboolean block_drawing;
void SCR_ScreenShot_f(void);
/*
===============================================================================
CENTER PRINTING
===============================================================================
*/
char scr_centerstring[1024];
float scr_centertime_start; // for slow victory printing
float scr_centertime_off;
int scr_center_lines;
int scr_erase_lines;
int scr_erase_center;
/*
==============
SCR_CenterPrint
Called for important messages that should stay in the center of the screen
for a few moments
==============
*/
void
SCR_CenterPrint(char *str)
{
strncpy(scr_centerstring, str, sizeof(scr_centerstring) - 1);
scr_centertime_off = scr_centertime.value;
scr_centertime_start = cl.time;
// count the number of lines for centering
scr_center_lines = 1;
while (*str) {
if (*str == '\n')
scr_center_lines++;
str++;
}
}
void
SCR_DrawCenterString(void)
{
char *start;
int l;
int j;
int x, y;
int remaining;
// the finale prints the characters one at a time
if (cl.intermission)
remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
else
remaining = 9999;
scr_erase_center = 0;
start = scr_centerstring;
if (scr_center_lines <= 4)
y = vid.height * 0.35;
else
y = 48;
do {
// scan the width of the line
for (l = 0; l < 40; l++)
if (start[l] == '\n' || !start[l])
break;
x = (vid.width - l * 8) / 2;
for (j = 0; j < l; j++, x += 8) {
Draw_Character(x, y, start[j]);
if (!remaining--)
return;
}
y += 8;
while (*start && *start != '\n')
start++;
if (!*start)
break;
start++; // skip the \n
} while (1);
}
void
SCR_CheckDrawCenterString(void)
{
scr_copytop = 1;
if (scr_center_lines > scr_erase_lines)
scr_erase_lines = scr_center_lines;
scr_centertime_off -= host_frametime;
if (scr_centertime_off <= 0 && !cl.intermission)
return;
if (key_dest != key_game)
return;
SCR_DrawCenterString();
}
//=============================================================================
/*
====================
CalcFov
====================
*/
float
CalcFov(float fov_x, float width, float height)
{
float a;
float x;
if (fov_x < 1 || fov_x > 179)
Sys_Error("Bad fov: %f", fov_x);
x = width / tan(fov_x / 360 * M_PI);
a = atan(height / x);
a = a * 360 / M_PI;
return a;
}
/*
=================
SCR_CalcRefdef
Must be called whenever vid changes
Internal use only
=================
*/
static void
SCR_CalcRefdef(void)
{
float size;
int h;
qboolean full = false;
scr_fullupdate = 0; // force a background redraw
vid.recalc_refdef = 0;
// force the status bar to redraw
Sbar_Changed();
//========================================
// bound viewsize
if (scr_viewsize.value < 30)
Cvar_Set("viewsize", "30");
if (scr_viewsize.value > 120)
Cvar_Set("viewsize", "120");
// bound field of view
if (scr_fov.value < 10)
Cvar_Set("fov", "10");
if (scr_fov.value > 170)
Cvar_Set("fov", "170");
// intermission is always full screen
if (cl.intermission)
size = 120;
else
size = scr_viewsize.value;
if (size >= 120)
sb_lines = 0; // no status bar at all
else if (size >= 110)
sb_lines = 24; // no inventory
else
sb_lines = 24 + 16 + 8;
if (scr_viewsize.value >= 100.0) {
full = true;
size = 100.0;
} else
size = scr_viewsize.value;
if (cl.intermission) {
full = true;
size = 100;
sb_lines = 0;
}
size /= 100.0;
h = vid.height - sb_lines;
r_refdef.vrect.width = vid.width * size;
if (r_refdef.vrect.width < 96) {
size = 96.0 / r_refdef.vrect.width;
r_refdef.vrect.width = 96; // min for icons
}
r_refdef.vrect.height = vid.height * size;
if (r_refdef.vrect.height > vid.height - sb_lines)
r_refdef.vrect.height = vid.height - sb_lines;
if (r_refdef.vrect.height > vid.height)
r_refdef.vrect.height = vid.height;
r_refdef.vrect.x = (vid.width - r_refdef.vrect.width) / 2;
if (full)
r_refdef.vrect.y = 0;
else
r_refdef.vrect.y = (h - r_refdef.vrect.height) / 2;
r_refdef.fov_x = scr_fov.value;
r_refdef.fov_y =
CalcFov(r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
scr_vrect = r_refdef.vrect;
}
/*
=================
SCR_SizeUp_f
Keybinding command
=================
*/
void
SCR_SizeUp_f(void)
{
Cvar_SetValue("viewsize", scr_viewsize.value + 10);
vid.recalc_refdef = 1;
}
/*
=================
SCR_SizeDown_f
Keybinding command
=================
*/
void
SCR_SizeDown_f(void)
{
Cvar_SetValue("viewsize", scr_viewsize.value - 10);
vid.recalc_refdef = 1;
}
//============================================================================
/*
==================
SCR_Init
==================
*/
void
SCR_Init(void)
{
Cvar_RegisterVariable(&scr_fov);
Cvar_RegisterVariable(&scr_viewsize);
Cvar_RegisterVariable(&scr_conspeed);
Cvar_RegisterVariable(&scr_showram);
Cvar_RegisterVariable(&scr_showturtle);
Cvar_RegisterVariable(&scr_showpause);
Cvar_RegisterVariable(&scr_centertime);
Cvar_RegisterVariable(&scr_printspeed);
Cvar_RegisterVariable(&gl_triplebuffer);
//
// register our commands
//
Cmd_AddCommand("screenshot", SCR_ScreenShot_f);
Cmd_AddCommand("sizeup", SCR_SizeUp_f);
Cmd_AddCommand("sizedown", SCR_SizeDown_f);
scr_ram = Draw_PicFromWad("ram");
scr_net = Draw_PicFromWad("net");
scr_turtle = Draw_PicFromWad("turtle");
scr_initialized = true;
}
/*
==============
SCR_DrawRam
==============
*/
void
SCR_DrawRam(void)
{
if (!scr_showram.value)
return;
if (!r_cache_thrash)
return;
Draw_Pic(scr_vrect.x + 32, scr_vrect.y, scr_ram);
}
/*
==============
SCR_DrawTurtle
==============
*/
void
SCR_DrawTurtle(void)
{
static int count;
if (!scr_showturtle.value)
return;
if (host_frametime < 0.1) {
count = 0;
return;
}
count++;
if (count < 3)
return;
Draw_Pic(scr_vrect.x, scr_vrect.y, scr_turtle);
}
/*
==============
SCR_DrawNet
==============
*/
void
SCR_DrawNet(void)
{
if (realtime - cl.last_received_message < 0.3)
return;
if (cls.demoplayback)
return;
Draw_Pic(scr_vrect.x + 64, scr_vrect.y, scr_net);
}
/*
==============
DrawPause
==============
*/
void
SCR_DrawPause(void)
{
qpic_t *pic;
if (!scr_showpause.value) // turn off for screenshots
return;
if (!cl.paused)
return;
pic = Draw_CachePic("gfx/pause.lmp");
Draw_Pic((vid.width - pic->width) / 2,
(vid.height - 48 - pic->height) / 2, pic);
}
/*
==============
SCR_DrawLoading
==============
*/
void
SCR_DrawLoading(void)
{
qpic_t *pic;
if (!scr_drawloading)
return;
pic = Draw_CachePic("gfx/loading.lmp");
Draw_Pic((vid.width - pic->width) / 2,
(vid.height - 48 - pic->height) / 2, pic);
}
//=============================================================================
/*
==================
SCR_SetUpToDrawConsole
==================
*/
void
SCR_SetUpToDrawConsole(void)
{
Con_CheckResize();
if (scr_drawloading)
return; // never a console with loading plaque
// decide on the height of the console
con_forcedup = !cl.worldmodel || cls.state != ca_active;
if (con_forcedup) {
scr_conlines = vid.height; // full screen
scr_con_current = scr_conlines;
} else if (key_dest == key_console)
scr_conlines = vid.height / 2; // half screen
else
scr_conlines = 0; // none visible
if (scr_conlines < scr_con_current) {
scr_con_current -= scr_conspeed.value * host_frametime;
if (scr_conlines > scr_con_current)
scr_con_current = scr_conlines;
} else if (scr_conlines > scr_con_current) {
scr_con_current += scr_conspeed.value * host_frametime;
if (scr_conlines < scr_con_current)
scr_con_current = scr_conlines;
}
if (clearconsole++ < vid.numpages) {
Sbar_Changed();
} else if (clearnotify++ < vid.numpages) {
} else
con_notifylines = 0;
}
/*
==================
SCR_DrawConsole
==================
*/
void
SCR_DrawConsole(void)
{
if (scr_con_current) {
scr_copyeverything = 1;
Con_DrawConsole(scr_con_current);
clearconsole = 0;
} else {
if (key_dest == key_game || key_dest == key_message)
Con_DrawNotify(); // only draw notify in game
}
}
/*
==============================================================================
SCREEN SHOTS
==============================================================================
*/
typedef struct _TargaHeader {
unsigned char id_length, colormap_type, image_type;
unsigned short colormap_index, colormap_length;
unsigned char colormap_size;
unsigned short x_origin, y_origin, width, height;
unsigned char pixel_size, attributes;
} TargaHeader;
/*
==================
SCR_ScreenShot_f
==================
*/
void
SCR_ScreenShot_f(void)
{
byte *buffer;
char pcxname[80];
char checkname[MAX_OSPATH];
int i, c, temp;
//
// find a file name to save it to
//
strcpy(pcxname, "quake00.tga");
for (i = 0; i <= 99; i++) {
pcxname[5] = i / 10 + '0';
pcxname[6] = i % 10 + '0';
sprintf(checkname, "%s/%s", com_gamedir, pcxname);
if (Sys_FileTime(checkname) == -1)
break; // file doesn't exist
}
if (i == 100) {
Con_Printf("SCR_ScreenShot_f: Couldn't create a PCX file\n");
return;
}
// FIXME - this is a PCX header?
buffer = malloc(glwidth * glheight * 3 + 18);
memset(buffer, 0, 18);
buffer[2] = 2; // uncompressed type
buffer[12] = glwidth & 255;
buffer[13] = glwidth >> 8;
buffer[14] = glheight & 255;
buffer[15] = glheight >> 8;
buffer[16] = 24; // pixel size
glReadPixels(glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE,
buffer + 18);
// swap rgb to bgr
c = 18 + glwidth * glheight * 3;
for (i = 18; i < c; i += 3) {
temp = buffer[i];
buffer[i] = buffer[i + 2];
buffer[i + 2] = temp;
}
COM_WriteFile(pcxname, buffer, glwidth * glheight * 3 + 18);
free(buffer);
Con_Printf("Wrote %s\n", pcxname);
}
//=============================================================================
/*
===============
SCR_BeginLoadingPlaque
================
*/
void
SCR_BeginLoadingPlaque(void)
{
S_StopAllSounds(true);
if (cls.state != ca_active)
return;
// redraw with no console and the loading plaque
Con_ClearNotify();
scr_centertime_off = 0;
scr_con_current = 0;
scr_drawloading = true;
scr_fullupdate = 0;
Sbar_Changed();
SCR_UpdateScreen();
scr_drawloading = false;
scr_disabled_for_loading = true;
scr_disabled_time = realtime;
scr_fullupdate = 0;
}
/*
===============
SCR_EndLoadingPlaque
================
*/
void
SCR_EndLoadingPlaque(void)
{
scr_disabled_for_loading = false;
scr_fullupdate = 0;
Con_ClearNotify();
}
//=============================================================================
char *scr_notifystring;
qboolean scr_drawdialog;
void
SCR_DrawNotifyString(void)
{
char *start;
int l;
int j;
int x, y;
start = scr_notifystring;
y = vid.height * 0.35;
do {
// scan the width of the line
for (l = 0; l < 40; l++)
if (start[l] == '\n' || !start[l])
break;
x = (vid.width - l * 8) / 2;
for (j = 0; j < l; j++, x += 8)
Draw_Character(x, y, start[j]);
y += 8;
while (*start && *start != '\n')
start++;
if (!*start)
break;
start++; // skip the \n
} while (1);
}
/*
==================
SCR_ModalMessage
Displays a text string in the center of the screen and waits for a Y or N
keypress.
==================
*/
int
SCR_ModalMessage(char *text)
{
if (cls.state == ca_dedicated)
return true;
scr_notifystring = text;
// draw a fresh screen
scr_fullupdate = 0;
scr_drawdialog = true;
SCR_UpdateScreen();
scr_drawdialog = false;
S_ClearBuffer(); // so dma doesn't loop current sound
do {
key_count = -1; // wait for a key down and up
Sys_SendKeyEvents();
} while (key_lastpress != 'y' && key_lastpress != 'n'
&& key_lastpress != K_ESCAPE);
scr_fullupdate = 0;
SCR_UpdateScreen();
return key_lastpress == 'y';
}
//=============================================================================
/*
===============
SCR_BringDownConsole
Brings the console down and fades the palettes back to normal
================
*/
void
SCR_BringDownConsole(void)
{
int i;
scr_centertime_off = 0;
for (i = 0; i < 20 && scr_conlines != scr_con_current; i++)
SCR_UpdateScreen();
cl.cshifts[0].percent = 0; // no area contents palette on next frame
VID_SetPalette(host_basepal);
}
void
SCR_TileClear(void)
{
if (r_refdef.vrect.x > 0) {
// left
Draw_TileClear(0, 0, r_refdef.vrect.x, vid.height - sb_lines);
// right
Draw_TileClear(r_refdef.vrect.x + r_refdef.vrect.width, 0,
vid.width - r_refdef.vrect.x + r_refdef.vrect.width,
vid.height - sb_lines);
}
if (r_refdef.vrect.y > 0) {
// top
Draw_TileClear(r_refdef.vrect.x, 0,
r_refdef.vrect.x + r_refdef.vrect.width,
r_refdef.vrect.y);
// bottom
Draw_TileClear(r_refdef.vrect.x,
r_refdef.vrect.y + r_refdef.vrect.height,
r_refdef.vrect.width,
vid.height - sb_lines -
(r_refdef.vrect.height + r_refdef.vrect.y));
}
}
/*
==================
SCR_UpdateScreen
This is called every frame, and can also be called explicitly to flush
text to the screen.
WARNING: be very careful calling this from elsewhere, because the refresh
needs almost the entire 256k of stack space!
==================
*/
void
SCR_UpdateScreen(void)
{
if (block_drawing)
return;
vid.numpages = 2 + gl_triplebuffer.value;
scr_copytop = 0;
scr_copyeverything = 0;
if (scr_disabled_for_loading) {
if (realtime - scr_disabled_time > 60) {
scr_disabled_for_loading = false;
Con_Printf("load failed.\n");
} else
return;
}
if (!scr_initialized || !con_initialized)
return; // not initialized yet
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
//
// determine size of refresh window
//
if (oldfov != scr_fov.value) {
oldfov = scr_fov.value;
vid.recalc_refdef = true;
}
if (oldscreensize != scr_viewsize.value) {
oldscreensize = scr_viewsize.value;
vid.recalc_refdef = true;
}
if (vid.recalc_refdef)
SCR_CalcRefdef();
//
// do 3D refresh drawing, and then update the screen
//
SCR_SetUpToDrawConsole();
V_RenderView();
GL_Set2D();
//
// draw any areas not covered by the refresh
//
SCR_TileClear();
if (scr_drawdialog) {
Sbar_Draw();
Draw_FadeScreen();
SCR_DrawNotifyString();
scr_copyeverything = true;
} else if (scr_drawloading) {
SCR_DrawLoading();
Sbar_Draw();
} else if (cl.intermission == 1 && key_dest == key_game) {
Sbar_IntermissionOverlay();
} else if (cl.intermission == 2 && key_dest == key_game) {
Sbar_FinaleOverlay();
SCR_CheckDrawCenterString();
} else {
if (crosshair.value) {
//Draw_Crosshair();
Draw_Character(scr_vrect.x + scr_vrect.width / 2,
scr_vrect.y + scr_vrect.height / 2, '+');
}
SCR_DrawRam();
SCR_DrawNet();
SCR_DrawTurtle();
SCR_DrawPause();
SCR_CheckDrawCenterString();
Sbar_Draw();
SCR_DrawConsole();
M_Draw();
}
V_UpdatePalette();
GL_EndRendering();
}

171
NQ/glqnotes.txt Normal file
View File

@ -0,0 +1,171 @@
Glquake v0.99, Quake v1.09 release notes
3dfx owners -- read the 3dfx.txt file.
On a standard OpenGL system, all you should need to do to run glquake is put
glquake.exe in your quake directory, and run it from there. DO NOT install
the opengl32.dll unless you have a 3dfx! Glquake should change the screen
resolution to 640*480*32k colors and run full screen by default.
If you are running win-95, your desktop must be set to 32k or 64k colors
before running glquake. NT can switch automatically.
Theoretically, glquake will run on any compliant OpenGL that supports the
texture objects extensions, but unless it is very powerfull hardware that
accelerates everything needed, the game play will not be acceptable. If it
has to go through any software emulation paths, the performance will likely
by well under one frame per second.
3dfx has provided an opengl32.dll that implements everything glquake needs,
but it is not a full opengl implementation. Other opengl applications are
very unlikely to work with it, so consider it basically a "glquake driver".
See the encluded 3dfx.txt for specific instalation notes. 3dfx can only run
full screen, but you must still have your desktop set to a 16 bit color mode
for glquake to start.
resolution options
------------------
We had dynamic resolution changing in glquake for a while, but every single
opengl driver I tried it on messed up in one way or another, so it is now
limited to startup time only.
glquake -window
This will start glquake in a window on your desktop instead of switching the
screen to lower resolution and covering everything.
glquake -width 800 -height 600
Tries to run glquake at the specified resolution. Combined with -window, it
creates a desktop window that size, otherwise it tries to set a full screen
resolution.
You can also specify the resolution of the console independant of the screen
resolution.
glquake -conwidth 320
This will specify a console resolution of 320 by 240 (the height is
automatically determined by the default 4:3 aspect ratio, you can also
specify the height directly with -conheight).
In higher resolution modes such as 800x600 and 1024x768, glquake will default
to a 640x480 console, since the font becomes small enough at higher
resolutions to become unreadable. If do you wish to have a higher resolution
console and status bar, specify it as well, such as:
glquake -width 800 -height 600 -conwidth 800
texture options
---------------
The amount of textures used in the game can have a large impact on performance.
There are several options that let you trade off visual quality for better
performance.
There is no way to flush already loaded textures, so it is best to change
these options on the command line, or they will only take effect on some of
the textures when you change levels.
OpenGL only allows textures to repeat on power of two boundaries (32, 64,
128, etc), but software quake had a number of textures that repeated at 24
or 96 pixel boundaries. These need to be either stretched out to the next
higher size, or shrunk down to the next lower. By default, they are filtered
down to the smaller size, but you can cause it to use the larger size if you
really want by using:
glquake +gl_round_down 0
This will generally run well on a normal 4 MB 3dfx card, but for other cards
that have either worse texture management or slower texture swapping speeds,
there are some additional settings that can drastically lower the amount of
textures to be managed.
glquake +gl_picmip 1
This causes all textures to have one half the dimensions they otherwise would.
This makes them blurry, but very small. You can set this to 2 to make the
textures one quarter the resolution on each axis for REALLY blurry textures.
glquake +gl_playermip 1
This is similar to picmip, but is only used for other players in deathmatch.
Each player in a deathmatch requires an individual skin texture, so this can
be a serious problem for texture management. It wouldn't be unreasonable to
set this to 2 or even 3 if you are playing competatively (and don't care if
the other guys have smudged skins). If you change this during the game, it
will take effect as soon as a player changes their skin colors.
GLQuake also supports the following extensions for faster texture operation:
GL_SGIS_multitexture
Multitextures support allows certain hardware to render the world in one
pass instead of two. GLQuake uses two passes, one for the world textures
and the second for the lightmaps that are blended on the textures. On some
hardware, with a GL_SIGS_multitexture supported OpenGL implementation, this
can be done in one pass. On hardware that supports this, you will get a
60% to 100% increase in frame rate. Currently, only 3DFX dual TMU cards
(such as the Obsidian 2220) support this extension, but other hardware will
soon follow.
This extension will be autodetected and used. If for some reason it is not
working correctly, specify the command line option "-nomtex" to disable it.
GL_EXT_shared_texture_palette
GLQuake uses 16bit textures by default but on OpenGL implementations
that support the GL_EXT_shared_texture_palette extension, GLQuake will use
8bit textures instead. This results in using half the needed texture memory
of 16bit texture and can improve performance. This is very little difference
in visual quality due to the fact that the textures are 8bit sources to
begin with.
run time options
----------------
At the console, you can set these values to effect drawing.
gl_texturemode GL_NEAREST
Sets texture mapping to point sampled, which may be faster on some GL systems
(not on 3dfx).
gl_texturemode GL_LINEAR_MIPMAP
This is the default texture mode.
gl_texturemode GL_LINEAR_MIPMAP_LINEAR
This is the highest quality texture mapping (trilinear), but only very high
end hardware (intergraph intense 3D / realizm) supports it. Not that big of
a deal, actually.
gl_finish 0
This causes the game to not issue a glFinish() call each frame, which may make
some hardware run faster. If this is cleared, the 3dfx will back up a number
of frames and not be very playable.
gl_flashblend 0
By default, glquake just draws a shaded ball around objects that are emiting
light. Clearing this variable will cause it to properly relight the world
like normal quake, but it can be a significant speed hit on some systems.
gl_ztrick 0
Glquake uses a buffering method that avoids clearing the Z buffer, but some
hardware platforms don't like it. If the status bar and console are flashing
every other frame, clear this variable.
gl_keeptjunctions 0
If you clear this, glquake will remove colinear vertexes when it reloads the
level. This can give a few percent speedup, but it can leave a couple stray
blinking pixels on the screen.
novelty features
----------------
These are some rendering tricks that were easy to do in glquake. They aren't
very robust, but they are pretty cool to look at.
r_shadows 1
This causes every object to cast a shadow.
r_wateralpha 0.7
This sets the opacity of water textures, so you can see through it in properly
processed maps. 0.3 is very faint, almost like fog. 1 is completely solid
(the default). Unfortunately, the standard quake maps don't contain any
visibility information for seeing past water surfaces, so you can't just play
quake with this turned on. If you just want to see what it looks like, you
can set "r_novis 1", but that will make things go very slow. When I get a
chance, I will probably release some maps that have been processed properly
for this.
r_mirroralpha 0.3
This changes one particular texture (the stained glass texture in the EASY
start hall) into a mirror. The value is the opacity of the mirror surface.

986
NQ/host.c Normal file
View File

@ -0,0 +1,986 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// host.c -- coordinates spawning and killing of local servers
#include "cdaudio.h"
#include "cmd.h"
#include "console.h"
#include "draw.h"
#include "host.h"
#include "input.h"
#include "keys.h"
#include "menu.h"
#include "net.h"
#include "net_vcr.h"
#include "protocol.h"
#include "quakedef.h"
#include "sbar.h"
#include "screen.h"
#include "server.h"
#include "sound.h"
#include "sys.h"
#include "view.h"
#include "wad.h"
#ifdef GLQUAKE
#include "gl_model.h"
#else
#include "r_local.h"
#endif
/*
* A server can allways be started, even if the system started out as a client
* to a remote system.
*
* A client can NOT be started if the system started as a dedicated server.
*
* Memory is cleared/released when a server or client begins, not when they
* end.
*/
quakeparms_t host_parms;
qboolean host_initialized; // true if into command execution
double host_frametime;
double host_time;
double realtime; // without any filtering or bounding
double oldrealtime; // last frame run
int host_framecount;
int host_hunklevel;
int minimum_memory;
client_t *host_client; // current client
jmp_buf host_abortserver;
byte *host_basepal;
byte *host_colormap;
cvar_t host_framerate = { "host_framerate", "0" }; // set for slow motion
cvar_t host_speeds = { "host_speeds", "0" }; // set for running times
cvar_t sys_ticrate = { "sys_ticrate", "0.05" };
cvar_t serverprofile = { "serverprofile", "0" };
cvar_t fraglimit = { "fraglimit", "0", false, true };
cvar_t timelimit = { "timelimit", "0", false, true };
cvar_t teamplay = { "teamplay", "0", false, true };
cvar_t samelevel = { "samelevel", "0" };
cvar_t noexit = { "noexit", "0", false, true };
cvar_t developer = { "developer", "0" };
cvar_t skill = { "skill", "1" }; // 0 - 3
cvar_t deathmatch = { "deathmatch", "0" }; // 0, 1, or 2
cvar_t coop = { "coop", "0" }; // 0 or 1
cvar_t pausable = { "pausable", "1" };
cvar_t temp1 = { "temp1", "0" };
/*
================
Host_EndGame
================
*/
void
Host_EndGame(char *message, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, message);
vsprintf(string, message, argptr);
va_end(argptr);
Con_DPrintf("%s: %s\n", __func__, string);
if (sv.active)
Host_ShutdownServer(false);
if (cls.state == ca_dedicated)
Sys_Error("%s: %s", __func__, string); // dedicated servers exit
if (cls.demonum != -1)
CL_NextDemo();
else
CL_Disconnect();
longjmp(host_abortserver, 1);
}
/*
================
Host_Error
This shuts down both the client and server
================
*/
void
Host_Error(char *error, ...)
{
va_list argptr;
char string[1024];
static qboolean inerror = false;
if (inerror)
Sys_Error("%s: recursively entered", __func__);
inerror = true;
SCR_EndLoadingPlaque(); // reenable screen updates
va_start(argptr, error);
vsprintf(string, error, argptr);
va_end(argptr);
Con_Printf("%s: %s\n", __func__, string);
if (sv.active)
Host_ShutdownServer(false);
if (cls.state == ca_dedicated)
Sys_Error("%s: %s", __func__, string); // dedicated servers exit
CL_Disconnect();
cls.demonum = -1;
inerror = false;
longjmp(host_abortserver, 1);
}
/*
================
Host_FindMaxClients
================
*/
void
Host_FindMaxClients(void)
{
int i;
svs.maxclients = 1;
i = COM_CheckParm("-dedicated");
if (i) {
cls.state = ca_dedicated;
if (i != (com_argc - 1)) {
svs.maxclients = Q_atoi(com_argv[i + 1]);
} else
svs.maxclients = 8;
} else
cls.state = ca_disconnected;
i = COM_CheckParm("-listen");
if (i) {
if (cls.state == ca_dedicated)
Sys_Error("Only one of -dedicated or -listen can be specified");
if (i != (com_argc - 1))
svs.maxclients = Q_atoi(com_argv[i + 1]);
else
svs.maxclients = 8;
}
if (svs.maxclients < 1)
svs.maxclients = 8;
else if (svs.maxclients > MAX_SCOREBOARD)
svs.maxclients = MAX_SCOREBOARD;
svs.maxclientslimit = svs.maxclients;
if (svs.maxclientslimit < 4)
svs.maxclientslimit = 4;
svs.clients =
Hunk_AllocName(svs.maxclientslimit * sizeof(client_t), "clients");
if (svs.maxclients > 1)
Cvar_SetValue("deathmatch", 1.0);
else
Cvar_SetValue("deathmatch", 0.0);
}
/*
=======================
Host_InitLocal
======================
*/
void
Host_InitLocal(void)
{
Host_InitCommands();
Cvar_RegisterVariable(&host_framerate);
Cvar_RegisterVariable(&host_speeds);
Cvar_RegisterVariable(&sys_ticrate);
Cvar_RegisterVariable(&serverprofile);
Cvar_RegisterVariable(&fraglimit);
Cvar_RegisterVariable(&timelimit);
Cvar_RegisterVariable(&teamplay);
Cvar_RegisterVariable(&samelevel);
Cvar_RegisterVariable(&noexit);
Cvar_RegisterVariable(&skill);
Cvar_RegisterVariable(&developer);
Cvar_RegisterVariable(&deathmatch);
Cvar_RegisterVariable(&coop);
Cvar_RegisterVariable(&pausable);
Cvar_RegisterVariable(&temp1);
Host_FindMaxClients();
host_time = 1.0; // so a think at time 0 won't get called
}
/*
===============
Host_WriteConfiguration
Writes key bindings and archived cvars to config.cfg
===============
*/
void
Host_WriteConfiguration(void)
{
FILE *f;
// dedicated servers initialize the host but don't parse and set the
// config.cfg cvars
if (host_initialized & !isDedicated) {
f = fopen(va("%s/config.cfg", com_gamedir), "w");
if (!f) {
Con_Printf("Couldn't write config.cfg.\n");
return;
}
Key_WriteBindings(f);
Cvar_WriteVariables(f);
fclose(f);
}
}
/*
=================
SV_ClientPrintf
Sends text across to be displayed
FIXME: make this just a stuffed echo?
=================
*/
void
SV_ClientPrintf(char *fmt, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, fmt);
vsprintf(string, fmt, argptr);
va_end(argptr);
MSG_WriteByte(&host_client->message, svc_print);
MSG_WriteString(&host_client->message, string);
}
/*
=================
SV_BroadcastPrintf
Sends text to all active clients
=================
*/
void
SV_BroadcastPrintf(char *fmt, ...)
{
va_list argptr;
char string[1024];
int i;
va_start(argptr, fmt);
vsprintf(string, fmt, argptr);
va_end(argptr);
for (i = 0; i < svs.maxclients; i++)
if (svs.clients[i].active && svs.clients[i].spawned) {
MSG_WriteByte(&svs.clients[i].message, svc_print);
MSG_WriteString(&svs.clients[i].message, string);
}
}
/*
=================
Host_ClientCommands
Send text over to the client to be executed
=================
*/
void
Host_ClientCommands(char *fmt, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, fmt);
vsprintf(string, fmt, argptr);
va_end(argptr);
MSG_WriteByte(&host_client->message, svc_stufftext);
MSG_WriteString(&host_client->message, string);
}
/*
=====================
SV_DropClient
Called when the player is getting totally kicked off the host
if (crash = true), don't bother sending signofs
=====================
*/
void
SV_DropClient(qboolean crash)
{
int saveSelf;
int i;
client_t *client;
if (!crash) {
// send any final messages (don't check for errors)
if (NET_CanSendMessage(host_client->netconnection)) {
MSG_WriteByte(&host_client->message, svc_disconnect);
NET_SendMessage(host_client->netconnection,
&host_client->message);
}
if (host_client->edict && host_client->spawned) {
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
saveSelf = pr_global_struct->self;
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
PR_ExecuteProgram(pr_global_struct->ClientDisconnect);
pr_global_struct->self = saveSelf;
}
Sys_Printf("Client %s removed\n", host_client->name);
}
// break the net connection
NET_Close(host_client->netconnection);
host_client->netconnection = NULL;
// free the client (the body stays around)
host_client->active = false;
host_client->name[0] = 0;
host_client->old_frags = -999999;
net_activeconnections--;
// send notification to all clients
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) {
if (!client->active)
continue;
MSG_WriteByte(&client->message, svc_updatename);
MSG_WriteByte(&client->message, host_client - svs.clients);
MSG_WriteString(&client->message, "");
MSG_WriteByte(&client->message, svc_updatefrags);
MSG_WriteByte(&client->message, host_client - svs.clients);
MSG_WriteShort(&client->message, 0);
MSG_WriteByte(&client->message, svc_updatecolors);
MSG_WriteByte(&client->message, host_client - svs.clients);
MSG_WriteByte(&client->message, 0);
}
}
/*
==================
Host_ShutdownServer
This only happens at the end of a game, not between levels
==================
*/
void
Host_ShutdownServer(qboolean crash)
{
int i;
int count;
sizebuf_t buf;
char message[4];
double start;
if (!sv.active)
return;
sv.active = false;
// stop all client sounds immediately
if (cls.state >= ca_connected)
CL_Disconnect();
// flush any pending messages - like the score!!!
start = Sys_DoubleTime();
do {
count = 0;
for (i = 0, host_client = svs.clients; i < svs.maxclients;
i++, host_client++) {
if (host_client->active && host_client->message.cursize) {
if (NET_CanSendMessage(host_client->netconnection)) {
NET_SendMessage(host_client->netconnection,
&host_client->message);
SZ_Clear(&host_client->message);
} else {
NET_GetMessage(host_client->netconnection);
count++;
}
}
}
if ((Sys_DoubleTime() - start) > 3.0)
break;
}
while (count);
// make sure all the clients know we're disconnecting
buf.data = message;
buf.maxsize = 4;
buf.cursize = 0;
MSG_WriteByte(&buf, svc_disconnect);
count = NET_SendToAll(&buf, 5);
if (count)
Con_Printf
("Host_ShutdownServer: NET_SendToAll failed for %u clients\n",
count);
for (i = 0, host_client = svs.clients; i < svs.maxclients;
i++, host_client++)
if (host_client->active)
SV_DropClient(crash);
//
// clear structures
//
memset(&sv, 0, sizeof(sv));
memset(svs.clients, 0, svs.maxclientslimit * sizeof(client_t));
}
/*
================
Host_ClearMemory
This clears all the memory used by both the client and server, but does
not reinitialize anything.
================
*/
void
Host_ClearMemory(void)
{
Con_DPrintf("Clearing memory\n");
D_FlushCaches();
Mod_ClearAll();
if (host_hunklevel)
Hunk_FreeToLowMark(host_hunklevel);
cls.signon = 0;
memset(&sv, 0, sizeof(sv));
memset(&cl, 0, sizeof(cl));
}
//============================================================================
/*
===================
Host_FilterTime
Returns false if the time is too short to run a frame
===================
*/
qboolean
Host_FilterTime(float time)
{
realtime += time;
if (!cls.timedemo && realtime - oldrealtime < 1.0 / 72.0)
return false; // framerate is too high
host_frametime = realtime - oldrealtime;
oldrealtime = realtime;
if (host_framerate.value > 0)
host_frametime = host_framerate.value;
else { // don't allow really long or short frames
if (host_frametime > 0.1)
host_frametime = 0.1;
if (host_frametime < 0.001)
host_frametime = 0.001;
}
return true;
}
/*
===================
Host_GetConsoleCommands
Add them exactly as if they had been typed at the console
===================
*/
void
Host_GetConsoleCommands(void)
{
char *cmd;
while (1) {
cmd = Sys_ConsoleInput();
if (!cmd)
break;
Cbuf_AddText(cmd);
}
}
/*
==================
Host_ServerFrame
==================
*/
#ifdef FPS_20
void
_Host_ServerFrame(void)
{
// run the world state
pr_global_struct->frametime = host_frametime;
// read client messages
SV_RunClients();
// move things around and think
// always pause in single player if in console or menus
if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game))
SV_Physics();
}
void
Host_ServerFrame(void)
{
float save_host_frametime;
float temp_host_frametime;
// run the world state
pr_global_struct->frametime = host_frametime;
// set the time and clear the general datagram
SV_ClearDatagram();
// check for new clients
SV_CheckForNewClients();
temp_host_frametime = save_host_frametime = host_frametime;
while (temp_host_frametime > (1.0 / 72.0)) {
if (temp_host_frametime > 0.05)
host_frametime = 0.05;
else
host_frametime = temp_host_frametime;
temp_host_frametime -= host_frametime;
_Host_ServerFrame();
}
host_frametime = save_host_frametime;
// send all messages to the clients
SV_SendClientMessages();
}
#else
void
Host_ServerFrame(void)
{
/* run the world state */
pr_global_struct->frametime = host_frametime;
/* set the time and clear the general datagram */
SV_ClearDatagram();
/* check for new clients */
SV_CheckForNewClients();
/* read client messages */
SV_RunClients();
/*
* Move things around and think. Always pause in single player if in
* console or menus
*/
if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game))
SV_Physics();
/* send all messages to the clients */
SV_SendClientMessages();
}
#endif
/*
==================
Host_Frame
Runs all active servers
==================
*/
void
_Host_Frame(float time)
{
static double time1 = 0;
static double time2 = 0;
static double time3 = 0;
int pass1, pass2, pass3;
/* something bad happened, or the server disconnected */
if (setjmp(host_abortserver))
return;
/* keep the random time dependent */
rand();
/*
* Decide the simulation time. Don't run too fast, or packets will flood
* out.
*/
if (!Host_FilterTime(time))
return;
/* get new key events */
Sys_SendKeyEvents();
/* allow mice or other external controllers to add commands */
IN_Commands();
/* process console commands */
Cbuf_Execute();
NET_Poll();
/* if running the server locally, make intentions now */
if (sv.active)
CL_SendCmd();
//-------------------
//
// server operations
//
//-------------------
/* check for commands typed to the host */
Host_GetConsoleCommands();
if (sv.active)
Host_ServerFrame();
//-------------------
//
// client operations
//
//-------------------
/*
* if running the server remotely, send intentions now after the incoming
* messages have been read
*/
if (!sv.active)
CL_SendCmd();
host_time += host_frametime;
/* fetch results from server */
if (cls.state >= ca_connected)
CL_ReadFromServer();
/* update video */
if (host_speeds.value)
time1 = Sys_DoubleTime();
SCR_UpdateScreen();
if (host_speeds.value)
time2 = Sys_DoubleTime();
/* update audio */
if (cls.state == ca_active) {
S_Update(r_origin, vpn, vright, vup);
CL_DecayLights();
} else
S_Update(vec3_origin, vec3_origin, vec3_origin, vec3_origin);
CDAudio_Update();
if (host_speeds.value) {
pass1 = (time1 - time3) * 1000;
time3 = Sys_DoubleTime();
pass2 = (time2 - time1) * 1000;
pass3 = (time3 - time2) * 1000;
Con_Printf("%3i tot %3i server %3i gfx %3i snd\n",
pass1 + pass2 + pass3, pass1, pass2, pass3);
}
host_framecount++;
}
void
Host_Frame(float time)
{
double time1, time2;
static double timetotal;
static int timecount;
int i, c, m;
if (!serverprofile.value) {
_Host_Frame(time);
return;
}
time1 = Sys_DoubleTime();
_Host_Frame(time);
time2 = Sys_DoubleTime();
timetotal += time2 - time1;
timecount++;
if (timecount < 1000)
return;
m = timetotal * 1000 / timecount;
timecount = 0;
timetotal = 0;
c = 0;
for (i = 0; i < svs.maxclients; i++) {
if (svs.clients[i].active)
c++;
}
Con_Printf("serverprofile: %2i clients %2i msec\n", c, m);
}
//============================================================================
#define VCR_SIGNATURE 0x56435231
// "VCR1"
void
Host_InitVCR(quakeparms_t *parms)
{
int i, len, n;
char *p;
if (COM_CheckParm("-playback")) {
if (com_argc != 2)
Sys_Error("No other parameters allowed with -playback");
Sys_FileOpenRead("quake.vcr", &vcrFile);
if (vcrFile == -1)
Sys_Error("playback file not found");
Sys_FileRead(vcrFile, &i, sizeof(int));
if (i != VCR_SIGNATURE)
Sys_Error("Invalid signature in vcr file");
Sys_FileRead(vcrFile, &com_argc, sizeof(int));
com_argv = malloc(com_argc * sizeof(char *));
com_argv[0] = parms->argv[0];
for (i = 0; i < com_argc; i++) {
Sys_FileRead(vcrFile, &len, sizeof(int));
p = malloc(len);
Sys_FileRead(vcrFile, p, len);
com_argv[i + 1] = p;
}
com_argc++; /* add one for arg[0] */
parms->argc = com_argc;
parms->argv = com_argv;
}
if ((n = COM_CheckParm("-record")) != 0) {
vcrFile = Sys_FileOpenWrite("quake.vcr");
i = VCR_SIGNATURE;
Sys_FileWrite(vcrFile, &i, sizeof(int));
i = com_argc - 1;
Sys_FileWrite(vcrFile, &i, sizeof(int));
for (i = 1; i < com_argc; i++) {
if (i == n) {
len = 10;
Sys_FileWrite(vcrFile, &len, sizeof(int));
Sys_FileWrite(vcrFile, "-playback", len);
continue;
}
len = strlen(com_argv[i]) + 1;
Sys_FileWrite(vcrFile, &len, sizeof(int));
Sys_FileWrite(vcrFile, com_argv[i], len);
}
}
}
/*
====================
Host_Init
====================
*/
void
Host_Init(quakeparms_t *parms)
{
if (standard_quake)
minimum_memory = MINIMUM_MEMORY;
else
minimum_memory = MINIMUM_MEMORY_LEVELPAK;
if (COM_CheckParm("-minmemory"))
parms->memsize = minimum_memory;
host_parms = *parms;
if (parms->memsize < minimum_memory)
Sys_Error("Only %4.1f megs of memory available, can't execute game",
parms->memsize / (float)0x100000);
com_argc = parms->argc;
com_argv = parms->argv;
Memory_Init(parms->membase, parms->memsize);
Cbuf_Init();
Cmd_Init();
V_Init();
Chase_Init();
Host_InitVCR(parms);
COM_Init(parms->basedir);
Host_InitLocal();
W_LoadWadFile("gfx.wad");
Key_Init();
Con_Init();
M_Init();
PR_Init();
Mod_Init();
NET_Init();
SV_Init();
Con_Printf("Exe: " __TIME__ " " __DATE__ "\n");
Con_Printf("%4.1f megabyte heap\n", parms->memsize / (1024 * 1024.0));
R_InitTextures(); // needed even for dedicated servers
if (cls.state != ca_dedicated) {
host_basepal = (byte *)COM_LoadHunkFile("gfx/palette.lmp");
if (!host_basepal)
Sys_Error("Couldn't load gfx/palette.lmp");
host_colormap = (byte *)COM_LoadHunkFile("gfx/colormap.lmp");
if (!host_colormap)
Sys_Error("Couldn't load gfx/colormap.lmp");
#if 0
#ifndef _WIN32
/*
* On non win32, mouse comes before video for security reasons
* FIXME - huh?
*/
IN_Init ();
#endif
#endif
VID_Init(host_basepal);
Draw_Init();
SCR_Init();
R_Init();
#ifndef _WIN32
/*
* On Win32, sound initialization has to come before video
* initialization, so we can put up a popup if the sound hardware is
* in use
*/
S_Init();
#else
#ifdef GLQUAKE
// FIXME: doesn't use the new one-window approach yet
S_Init();
#endif
#endif // _WIN32
CDAudio_Init();
Sbar_Init();
CL_Init();
//#ifdef _WIN32
// on non win32, mouse comes before video for security reasons
IN_Init();
//#endif
}
Cbuf_InsertText("exec quake.rc\n");
Hunk_AllocName(0, "-HOST_HUNKLEVEL-");
host_hunklevel = Hunk_LowMark();
host_initialized = true;
Sys_Printf("========Quake Initialized=========\n");
}
/*
===============
Host_Shutdown
FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better
to run quit through here before the final handoff to the sys code.
===============
*/
void
Host_Shutdown(void)
{
static qboolean isdown = false;
if (isdown) {
printf("recursive shutdown\n");
return;
}
isdown = true;
// keep Con_Printf from trying to update the screen
scr_disabled_for_loading = true;
Host_WriteConfiguration();
CDAudio_Shutdown();
NET_Shutdown();
S_Shutdown();
IN_Shutdown();
if (cls.state != ca_dedicated) {
VID_Shutdown();
}
}

68
NQ/host.h Normal file
View File

@ -0,0 +1,68 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef HOST_H
#define HOST_H
#include "qtypes.h" /* qboolean */
#include "quakedef.h" /* quakeparms_t */
// FIXME - some of this is out of place or badly named...
extern quakeparms_t host_parms;
extern cvar_t sys_ticrate;
extern cvar_t sys_nostdout;
extern cvar_t developer;
extern qboolean host_initialized; // true if into command execution
extern double host_frametime;
extern byte *host_basepal;
extern byte *host_colormap;
extern int host_framecount; // incremented every frame, never reset
extern double realtime; // not bounded in any way, changed at
// start of every frame, never reset
void Host_ClearMemory(void);
void Host_ServerFrame(void);
void Host_InitCommands(void);
void Host_Init(quakeparms_t *parms);
void Host_Shutdown(void);
void Host_Error(char *error, ...);
void Host_EndGame(char *message, ...);
void Host_Frame(float time);
void Host_Quit_f(void);
void Host_ClientCommands(char *fmt, ...);
void Host_ShutdownServer(qboolean crash);
extern qboolean msg_suppress_1; // suppresses resolution and cache size console
// output an fullscreen DIB focus gain/loss
extern int current_skill; // skill level for currently loaded level (in
// case the user changes the cvar while the
// level is running, this reflects the level
// actually in use)
extern qboolean isDedicated;
extern int minimum_memory;
#endif /* HOST_H */

1631
NQ/host_cmd.c Normal file

File diff suppressed because it is too large Load Diff

279
NQ/kit/3DFX.TXT Normal file
View File

@ -0,0 +1,279 @@
GLQuake Drivers for Voodoo Graphics Based 3D Accelerators
Preliminary Release 2
Copyright ( 1997 3Dfx Interactive, Inc. )
All Rights Reserved
3Dfx Interactive, Inc.
www: www.3dfx.com
news: news.3dfx.com
-----------------------------------------------------------------------
NOTE: GLQuake requires DirectX 2.0 or DirectX 3.0
(Needed for DirectSound support)
DirectX 2.0 or DirectX 3.0 can be installed from the media provided
with your Voodoo Based 3D Accelerator.
-----------------------------------------------------------------------
Release Notes for GLQuake Preliminary Release 2 (Quake Version 1.07)
-----------------------------------------------------------------------
What's in the distribution?
-----------------------------------------------------------------------
This distribution contains GLQuake Drivers for Voodoo Graphics Based 3D
Accelerators. These drivers were tested on the following boards:
- Diamond Monster 3D
- Orchid Righteous 3D
- 3Dfx Interactive reference boards
NOTE: The enclosed drivers are not meant to replace any Direct3D or
Glide drivers provided by your Voodoo Graphics card manufacturer.
Please obtain supported drivers from:
- Diamond supported drivers can be obtained from www.diamondmm.com
- Orchid supported drivers can be obtained from www.orchid.com
Included Files
--------------
OPENGL32.DLL - QuakeGL Interface to Voodoo Graphics
FXMEMMAP.VXD - Voodoo Graphics VXD
GLQUAKE.EXE - ID Software provided GLQUAKE.EXE
README.TXT - ID Software provided README.TXT
READ3DFX.TXT - This File
All enclosed files MUST reside in your Quake directory and not in the
Windows\SYSTEM directory.
OEMSR2 users: Do NOT replace OPENGL32.DLL located in your
Windows\SYSTEM directory.
-----------------------------------------------------------------------
Installation
-----------------------------------------------------------------------
Requirements
------------
- Voodoo Graphics Based 3D Accelerator
- Windows 95
- A PC with a Pentium 90 or higher CPU
- 16MB of RAM
- 2D Video card set at 16 bit or higher color
Installation
------------
Adding GLQuake Driver Support
-----------------------------
1) Install the FULL Version (Not the Shareware version!) of Quake.
2) Copy the GLQUAKE.EXE and other associated files from the GLQuake
ZIP file to your Quake Directory. (Use Windows Explorer)
3) Copy the enclosed OPENGL32.DLL file to your Quake Directory.
(Use Windows Explorer) NOTE: DO NOT COPY OPENGL32.DLL to your
Windows\SYSTEM directory
4) Create a Desktop Shortcut for GLQuake: Using the right mouse button
drag the GLQUAKE.EXE file from Windows Explorer to the Desktop.
When prompted choose "Create Shortcut"
5) Create an autoexec.cfg file in the ID1\ directory of your quake
installation if you do not already have one. Add the line
gl_playermip "2"
to the file.
6) Start GLQuake by running the shortcut.
Troubleshooting and Frequently Asked Questions
----------------------------------------------
1. Will GLQuake work with shareware Quake?
No, the full registered version of Quake is required.
2. Do I need any other drivers to run GLQuake on Voodoo Graphics?
Just make sure that the FXMEMMAP.VXD file is in your Windows\SYSTEM
directory and that DirectX 2.0, DirectX 3.0 or DirectX 3.0a are
installed as GLQuake uses DirectSound. The latest version of DirectX
can be obtained from:
http://www.microsoft.com/mediadev/download/isdk.htm
3. I installed GLQuake and try to run the GLQUAKE.EXE file but I get a
"no RGB fullscreen modes available" How do I get GLQuake to run?
Make sure that your 2D video card is set to 16bit color (65K colors,
not 16 colors). In addition, do not start GLQuake from a full screen
MS-DOS prompt.
4. GLQuake comes up for a little while then drops me back to the
Windows 95 desktop, what's wrong?
Your Virtual Memory settings on your system should be increased. Open
Control Panel, System - click on the Performance tab and then click on
Virtual Memory. Adjust the settings so that the minimum swap file size
is 80MB. You may also want to delete all the mesh files - do this by
deleting the quake\ID1\glquake directory.
5. Why does GLQuake try to connect to my Internet connection whenever
it starts?
GLQuake uses Windows networking. Auto-Dial is likely enabled in your
Internet Control Panel or in Internet Explorer Options. Single
Player users: To disable Network use in GLQuake and prevent the
network connection screen from coming up, add "-nolan" to the
GLQUAKE.EXE command line, example:
GLQUAKE.EXE -nolan
6. I have a three button mouse, but I can't use or set the middle
button in GLQuake, what's wrong?
To use a three button mouse with GLQuake, your Windows mouse driver
must support three buttons. Use the Logitech PS/2, Serial or Bus
driver instead of the Microsoft or Standard PS/2, Serial or Bus driver.
Also, make certain that your Mouse control panel sets the middle button
to "middle" and not "double click".
7. Mouse input seems jumpy, how do I fix that?
From the console (hit the ~ tilde key), enter m_filter 1 <enter>
This option can be added to the AUTOEXEC.CFG file (in the \ID1
directory). You may also add this option to the GLQUAKE.EXE command
line, example:
GLQUAKE.EXE +m_filter 1
8. While playing GLQuake the sound stutters, how do I fix it?
If your sound card does not support DirectSound, you may encounter
stuttering sound during game play. Try adding the following value to
the CONFIG.CFG file (in the quake\ID1 directory):
_snd_mixahead ".14"
9. When I hit ALT-TAB or the Windows start button to switch to another
application why do I return to a 640x480 display?
GLQuake by default needs to keep the 2D display at 640x480 while it is
running. To return the display to your normal setting you must exit
GLQuake. To prevent this, add the following to the GLQUAKE.EXE command
line options "+_windowed_mouse 1" and "-window" example:
GLQUAKE.EXE +_windowed_mouse 1 -window
10. GLQuake multiplayer can't find other games or won't connect.
GLQuake uses Windows 95 Networking. Verify that the correct networking
components are installed and that you can connect to the other machine
using File and print sharing or TCP/IP ping. If you are using IPX also
make certain that the frame type is the same on all the systems.
11. GLQuake multiplayer slows down alot, how do I fix it?
Add gl_playermip 2 to the AUTOEXEC.CFG file (in the \ID1 directory)
You may however add "+gl_playermip 2" to the GLQUAKE.EXE command line,
example:
GLQUAKE.EXE +gl_playermip 2
12. Does the Activision(r) Scourge of Armagon add-on (Mission Pack 1)
work with GLQuake?
Yes, start GLQUAKE.EXE with a "-hipnotic" switch. Example:
GLQUAKE.EXE -hipnotic
13. Do other 3rd party quake levels work with GLQuake?
Not all 3rd party levels have been tested, some may not work properly
or optimally.
14. Will GLQuake use a Voodoo Graphics accelerator under Windows NT?
The 3Dfx GLQuake drivers currently only work under Windows 95.
15. After installing GLQuake the OpenGL screen savers in Windows 95
(OEMSR2) don't work. What's wrong?
The OpenGL Windows 95 screen savers in OEMSR2 will fail if you copied
the OPENGL32.DLL file that comes with GLQuake to your Windows\SYSTEM
directory. The 3Dfx OPENGL32.DLL file only works with Quake. It will
not run with other OpenGL applications. If you copied the 3Dfx
OPENGL32.DLL to your Windows\SYSTEM directory and need to restore the
Microsoft provided OPENGL32.DLL, follow these steps:
OEMSR2 Users
------------
1) Insert your Windows 95 CD into your CD-ROM drive
2) Open a MS-DOS prompt, (Click Start, Programs, MS-DOS Prompt)
3) Switch to the Windows\SYSTEM directory, ie:
C: <enter>
CD\Windows\system <enter>
4) At the command prompt, enter:
EXTRACT /A E:\WIN95 opengl32.dll <enter>
(Substitute E:\ for your CD-ROM drive letter)
Standard Windows 95 Users
-------------------------
1) Download and reinstall OpenGL for Windows 95 from the source
you previously used.
16. How do I get support for GLQuake
GLQuake is currently unsupported. You may however find answers to
questions on various Quake dedicated websites. 3Dfx provides a GLQuake
newsgroup on news.3dfx.com (Newsgroup name is 3dfx.games.glquake ) to
discuss GLQuake with other users. 3Dfx also provides a regularly
updated GLQuake FAQ at: http://www.3dfx.com/game_dev/quake_faq.html
16. How do I send a bug report?
If your problem is not resolved in this document or our updated FAQ
(please see #15) and your bug is related to visual quality, performance
or stability send an email to quake_bugs@3dfx.com - Describe your
system configuration (CPU Type, CPU Speed, 2D Video Card type, Amount
of Memory, Virtual Memory Size..etc.) and how to recreate the bug.
Voodoo Graphics is a trademark of 3Dfx Interactive, Inc. All other
trademarks are the property of their respective owners.

167
NQ/kit/JOYSTICK.TXT Normal file
View File

@ -0,0 +1,167 @@
NEW NOTE FOR 1.08:
Joysticks are disabled by defualt now, due to problems on some systems without joysticks installed. Type "joystick 1" at the console, and everything will behave as documented here. This will be saved in your config file, so you will only have to do it once.
Description of Windows 95 Quake DirectInput support
By: FPgaming, Inc. (www.fpgaming.com) -- Creators of the Assassin 3D
File: JOYSTICK.TXT
Created: 02/21/97
(This may be more information than you ever wanted to know.)
The joystick support with Windows 95 Quake has been significantly enhanced. Standard joysticks, digital joysticks and new advanced controllers like the FPgaming Assassin 3D, the Logitech WingMan Warrior and the SpaceTec IMC SpaceOrb are all supported.
To make it work, just verify that your joystick or game controller is selected in the Joystick control panel applet and has been calibrated and tested, then launch Windows 95 Quake (WinQuake.exe or glquake.exe). For standard and new digital joysticks, WinQuake will detect the joystick and automatically configure it. For advanced controllers, you will additionally need to run a config file (or run it from your autoexec.cfg) prior to the device operating. This will set the advanced features for your particular device. This config file should be obtained from your game controller company. The config files for the comman game controllers are included below. If you don't want your joystick or game controller enabled, add '-nojoy' to your command-line.
Standard Joystick Support
The standard joystick support has been enhanced to provide the following:
1. proportional movement (the farther you move the stick, the faster you move)
2. support for up to 32 buttons (JOY1-JOY4 and AUX5-AUX32)
3. sensitivity setting for each control (allows tuning and inverting the control direction)
4. dead-zone setting for each control
The default joystick setting is for joystick left/right movement to control turning and for joystick forward/backward movement to control moving forward/backward. For optional strafing, add the 'sidestep' feature to one of your buttons (via the Customize menu). For optional looking, add the 'mouse look' feature to one of your buttons (also via the Customize menu).
Additionally, there are several features that you can set from the Options menu. 'Always Run' allows you change your maximum speed from walking to running. 'Invert Mouse' allows you to change the direction the joystick has to move to when looking up and down. 'Lookspring' enables automatic look-forward-when-moving. And, 'Lookstrafe' automatically enables strafing when the 'mouse look' button is pressed.
The following variables control your sensititivity settings:
joyforwardsensitivity - controls the ramp-up speed for moving forward and backward
joysidesensitivity - controls the ramp-up speed for moving side to side
joypitchsensitivity - controls the speed that you look up and down
joyyawsensitivity - controls the speed that you look left to right
You can set the sensitivity settings to negative numbers. This inverts the direction of movement for the control. The default sensitivity settings are 1 (or -1). There is no limit on the range; whatever feels good.
The following variables control your threshold settings:
joyforwardthreshold - controls the dead-zone for moving forward and backward
joysidethreshold - controls the dead-zone for moving side to side
joypitchthreshold - controls the dead-zone for looking up and down
joyyawthreshold - controls the dead-zone for looking left and right
The threshold settings allow you to control your dead-zone (or no-movement zone). The default threshold settings are .15 (meaning 15% of the full-range). The range of the threshold settings is from 0 to 1. Troublesome analog joysticks may need a larger number (like .2). Premium joysticks can use a smaller number (like .1).
The joystick sensitivity settings and the threshold settings are not saved after you quit your game as inadvertant settings can really hose your control. If you want to keep any changes, add them into your autoexec.cfg file.
If your joystick has a POV hat, the buttons are mapped to AUX29-AUX32. So, you get 8 buttons with the Logitech WingMan Extreme and 12 buttons with the Microsoft SideWinder 3D Pro, etc.
Advanced Controller Support
The following features have been added:
1. support for all 6 axes (X, Y, Z, R, U, V)
2. mapping of any axis to any control (Forward, Look, Side, Turn)
3. proportional movement for all controls
4. sensitivity setting for any control (allows tuning and inverting the control direction)
5. threshold setting for any control (allows dead-zone setting)
6. support for absolute controls (like joysticks) and relative controls (like trackballs and spinners)
7. support for up to 32 buttons (JOY1-JOY4 and AUX5-AUX32)
To make an advanced controller operate, you will need to get a config file from your game controller company. This file is typically placed in your quake\id1 directory and then it is called within your autoexec.cfg file. For example, if your config file is named gamectrl.cfg, place that file within your quake\id1 directory and add 'exec gamectrl.cfg' in your autoexec.cfg file. If you don't have an autoexec.cfg file, you can create one and just place this one line in it.
******************************************************************************
NOTE: The information below is for game controller companies to integrate their device and for anyone wanting to create a custom setup.
In addition to the above new variables, there are six more for axis mapping. These are:
joyadvaxisx - controls mapping of DirectInput axis X (typically joystick left and right)
joyadvaxisy - controls mapping of DirectInput axis Y (typically joystick forward and backward)
joyadvaxisz - controls mapping of DirectInput axis Z (typically joystick throttle)
joyadvaxisr - controls mapping of DirectInput axis R (typically joystick rudder)
joyadvaxisu - controls mapping of DirectInput axis U (custom axis - Assassin 3D trackball left and right, WingMan Warrior SpinControl and SpaceOrb roll)
joyadvaxisv - controls mapping of DirectInput axis V (custom axis - Assassin 3D trackball forward and backward and SpaceOrb yaw)
Each joyadvaxis variable can be set to the following controls:
0 = Axis not used
1 = Axis is for forward and backward movement
2 = Axis is for looking up and down (pitch)
3 = Axis is for side to side movement
4 = Axis is for turning left and right (yaw)
Additionally, each axis can be designated as an absolute axis (like a joystick) or a relative axis (like the FPgaming trackball or the WingMan Warrior SpinControl). Absolute axes are defined as having a stopping position whereas relative axes don't have a stopping position and just go around and around. To designate an axis as a relative axis, add 16 to the above control number. For example, to set the Assassin 3D's axis U to be looking left and right, type 'joyadvaxisu 20'. As another example, to make your rudder pedals contol turning left and right, type 'joyadvaxisr 4'. It's a bit complicated, but only needs to be done once.
The advanced axes variables will not have any effect until joyadvanced is set to 1.0. Additionally, any changes to the to the axes will not take effect until the joyadvancedupdate command is executed. So, the procedure for creating an advanced mapping is:
1. set 'joyadvanced 1'
2. make any desired mapping changes
3. make any desired sensitivity changes
4. make any desired threshold changes
3. call 'joyadvancedupdate'
Here is the config file for the FPgaming Assassin 3D:
// ADVA3D.CFG
// Revision 1.0 -- refer to www.fpgaming.com for updates
joyname "FPgaming Assassin 3D"
joyadvanced 1
joyadvaxisx 3
joyadvaxisy 1
joyadvaxisz 0
joyadvaxisr 0
joyadvaxisu 20
joyadvaxisv 18
joyforwardsensitivity -1.0
joysidesensitivity 1.0
joypitchsensitivity -0.25
joyyawsensitivity -0.5
joyforwardthreshold 0.15
joysidethreshold 0.15
joyyawthreshold 0.0
joypitchthreshold 0.0
+mlook
joyadvancedupdate
Here is a config file for the Logitech WingMan Warrior:
// ADVWW.CFG
// Revision 0.1 -- refer to www.logitech.com for updates
joyname "Logitech WingMan Warrior"
joyadvanced 1.0
joywwhack1 1.0
joywwhack2 1.0
joyadvaxisx 3
joyadvaxisy 1
joyadvaxisz 0
joyadvaxisr 0
joyadvaxisu 20
joyadvaxisv 0
joyforwardsensitivity -1.0
joysidesensitivity 1.0
joypitchsensitivity 0.0
joyyawsensitivity -0.6
joyforwardthreshold 0.15
joysidethreshold 0.15
joypitchthreshold 0.0
joyyawthreshold 0.0
joyadvancedupdate
Here is a config file for the SpaceTec IMC SpaceOrb:
// ADVSPORB.CFG
// Revision 0.1 -- refer to www.spacetec.com for updates
joyname "SpaceTec IMC SpaceOrb"
joyadvanced 1.0
joyadvaxisx 3
joyadvaxisy 1
joyadvaxisz 0
joyadvaxisr 2
joyadvaxisu 0
joyadvaxisv 4
joyforwardsensitivity -1.0
joysidesensitivity 1.0
joypitchsensitivity -0.5
joyyawsensitivity 1
joyforwardthreshold 0.1
joysidethreshold 0.1
joypitchthreshold 0.1
joyyawthreshold 0.1
+mlook
joyadvancedupdate
Here is a config file for making your joystick operate looking around and strafing, your rudder pedals control turning left and right and throttle control moving forward and backward:
joyname "Joystick, Rudder & Throttle"
joyadvanced 1.0
joyadvaxisx 3
joyadvaxisy 2
joyadvaxisz 1
joyadvaxisr 4
joyadvaxisu 0
joyadvaxisv 0
joyforwardsensitivity -1.0
joysidesensitivity -1.0
joypitchsensitivity 1.0
joyyawsensitivity -1.0
joyforwardthreshold 0.15
joysidethreshold 0.15
joyyawthreshold 0.15
joypitchthreshold 0.15
joyadvancedupdate

171
NQ/kit/README.TXT Normal file
View File

@ -0,0 +1,171 @@
Glquake v0.97, Quake v1.09 release notes
3dfx owners -- read the 3dfx.txt file.
On a standard OpenGL system, all you should need to do to run glquake is put
glquake.exe in your quake directory, and run it from there. DO NOT install
the opengl32.dll unless you have a 3dfx! Glquake should change the screen
resolution to 640*480*32k colors and run full screen by default.
If you are running win-95, your desktop must be set to 32k or 64k colors
before running glquake. NT can switch automatically.
Theoretically, glquake will run on any compliant OpenGL that supports the
texture objects extensions, but unless it is very powerfull hardware that
accelerates everything needed, the game play will not be acceptable. If it
has to go through any software emulation paths, the performance will likely
by well under one frame per second.
3dfx has provided an opengl32.dll that implements everything glquake needs,
but it is not a full opengl implementation. Other opengl applications are
very unlikely to work with it, so consider it basically a "glquake driver".
See the encluded 3dfx.txt for specific instalation notes. 3dfx can only run
full screen, but you must still have your desktop set to a 16 bit color mode
for glquake to start.
resolution options
------------------
We had dynamic resolution changing in glquake for a while, but every single
opengl driver I tried it on messed up in one way or another, so it is now
limited to startup time only.
glquake -window
This will start glquake in a window on your desktop instead of switching the
screen to lower resolution and covering everything.
glquake -width 800 -height 600
Tries to run glquake at the specified resolution. Combined with -window, it
creates a desktop window that size, otherwise it tries to set a full screen
resolution.
You can also specify the resolution of the console independant of the screen
resolution.
glquake -conwidth 320
This will specify a console resolution of 320 by 240 (the height is
automatically determined by the default 4:3 aspect ratio, you can also
specify the height directly with -conheight).
In higher resolution modes such as 800x600 and 1024x768, glquake will default
to a 640x480 console, since the font becomes small enough at higher
resolutions to become unreadable. If do you wish to have a higher resolution
console and status bar, specify it as well, such as:
glquake -width 800 -height 600 -conwidth 800
texture options
---------------
The amount of textures used in the game can have a large impact on performance.
There are several options that let you trade off visual quality for better
performance.
There is no way to flush already loaded textures, so it is best to change
these options on the command line, or they will only take effect on some of
the textures when you change levels.
OpenGL only allows textures to repeat on power of two boundaries (32, 64,
128, etc), but software quake had a number of textures that repeated at 24
or 96 pixel boundaries. These need to be either stretched out to the next
higher size, or shrunk down to the next lower. By default, they are filtered
down to the smaller size, but you can cause it to use the larger size if you
really want by using:
glquake +gl_round_down 0
This will generally run well on a normal 4 MB 3dfx card, but for other cards
that have either worse texture management or slower texture swapping speeds,
there are some additional settings that can drastically lower the amount of
textures to be managed.
glquake +gl_picmip 1
This causes all textures to have one half the dimensions they otherwise would.
This makes them blurry, but very small. You can set this to 2 to make the
textures one quarter the resolution on each axis for REALLY blurry textures.
glquake +gl_playermip 1
This is similar to picmip, but is only used for other players in deathmatch.
Each player in a deathmatch requires an individual skin texture, so this can
be a serious problem for texture management. It wouldn't be unreasonable to
set this to 2 or even 3 if you are playing competatively (and don't care if
the other guys have smudged skins). If you change this during the game, it
will take effect as soon as a player changes their skin colors.
GLQuake also supports the following extensions for faster texture operation:
GL_SGIS_multitexture
Multitextures support allows certain hardware to render the world in one
pass instead of two. GLQuake uses two passes, one for the world textures
and the second for the lightmaps that are blended on the textures. On some
hardware, with a GL_SIGS_multitexture supported OpenGL implementation, this
can be done in one pass. On hardware that supports this, you will get a
60% to 100% increase in frame rate. Currently, only 3DFX dual TMU cards
(such as the Obsidian 2220) support this extension, but other hardware will
soon follow.
This extension will be autodetected and used. If for some reason it is not
working correctly, specify the command line option "-nomtex" to disable it.
GL_EXT_shared_texture_palette
GLQuake uses 16bit textures by default but on OpenGL implementations
that support the GL_EXT_shared_texture_palette extension, GLQuake will use
8bit textures instead. This results in using half the needed texture memory
of 16bit texture and can improve performance. This is very little difference
in visual quality due to the fact that the textures are 8bit sources to
begin with.
run time options
----------------
At the console, you can set these values to effect drawing.
gl_texturemode GL_NEAREST
Sets texture mapping to point sampled, which may be faster on some GL systems
(not on 3dfx).
gl_texturemode GL_LINEAR_MIPMAP
This is the default texture mode.
gl_texturemode GL_LINEAR_MIPMAP_LINEAR
This is the highest quality texture mapping (trilinear), but only very high
end hardware (intergraph intense 3D / realizm) supports it. Not that big of
a deal, actually.
gl_finish 0
This causes the game to not issue a glFinish() call each frame, which may make
some hardware run faster. If this is cleared, the 3dfx will back up a number
of frames and not be very playable.
gl_flashblend 0
By default, glquake just draws a shaded ball around objects that are emiting
light. Clearing this variable will cause it to properly relight the world
like normal quake, but it can be a significant speed hit on some systems.
gl_ztrick 0
Glquake uses a buffering method that avoids clearing the Z buffer, but some
hardware platforms don't like it. If the status bar and console are flashing
every other frame, clear this variable.
gl_keeptjunctions 0
If you clear this, glquake will remove colinear vertexes when it reloads the
level. This can give a few percent speedup, but it can leave a couple stray
blinking pixels on the screen.
novelty features
----------------
These are some rendering tricks that were easy to do in glquake. They aren't
very robust, but they are pretty cool to look at.
r_shadows 1
This causes every object to cast a shadow.
r_wateralpha 0.7
This sets the opacity of water textures, so you can see through it in properly
processed maps. 0.3 is very faint, almost like fog. 1 is completely solid
(the default). Unfortunately, the standard quake maps don't contain any
visibility information for seeing past water surfaces, so you can't just play
quake with this turned on. If you just want to see what it looks like, you
can set "r_novis 1", but that will make things go very slow. When I get a
chance, I will probably release some maps that have been processed properly
for this.
r_mirroralpha 0.3
This changes one particular texture (the stained glass texture in the EASY
start hall) into a mirror. The value is the opacity of the mirror surface.

3231
NQ/menu.c Normal file

File diff suppressed because it is too large Load Diff

64
NQ/menu.h Normal file
View File

@ -0,0 +1,64 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef MENU_H
#define MENU_H
#include "wad.h"
//
// the net drivers should just set the apropriate bits in m_activenet,
// instead of having the menu code look through their internal tables
//
#define MNET_IPX 1
#define MNET_TCP 2
extern int m_activenet;
//
// menus
//
void M_Init(void);
void M_Keydown(int key);
void M_Draw(void);
void M_ToggleMenu_f(void);
void M_Menu_Options_f(void);
void M_Menu_Quit_f(void);
void M_DrawPic(int x, int y, const qpic_t *pic);
void M_DrawTransPic(int x, int y, const qpic_t *pic);
void M_DrawCharacter(int cx, int line, int num);
void M_DrawTextBox(int x, int y, int width, int lines);
void M_Print(int cx, int cy, const char *str);
void M_PrintWhite(int cx, int cy, const char *str);
/* FIXME - These are only here for NQ/net_dgrm.c */
extern qboolean m_return_onerror;
extern char m_return_reason[32];
extern int m_return_state;
enum {
m_none, m_main, m_singleplayer, m_load, m_save, m_multiplayer, m_setup,
m_net, m_options, m_video, m_keys, m_help, m_quit, m_serialconfig,
m_modemconfig, m_lanconfig, m_gameoptions, m_search,
m_slist
} m_state;
#endif /* MENU_H */

1827
NQ/model.c Normal file

File diff suppressed because it is too large Load Diff

370
NQ/model.h Normal file
View File

@ -0,0 +1,370 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef MODEL_H
#define MODEL_H
#include "bspfile.h"
#include "modelgen.h"
#include "render.h"
#include "spritegn.h"
#include "zone.h"
/*
d*_t structures are on-disk representations
m*_t structures are in-memory
*/
/*
==============================================================================
BRUSH MODELS
==============================================================================
*/
//
// in memory representation
//
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct {
vec3_t position;
} mvertex_t;
#define SIDE_FRONT 0
#define SIDE_BACK 1
#define SIDE_ON 2
// plane_t structure
// !!! if this is changed, it must be changed in asm_i386.h too !!!
typedef struct mplane_s {
vec3_t normal;
float dist;
byte type; // for texture axis selection and fast side tests
byte signbits; // signx + signy<<1 + signz<<1
byte pad[2];
} mplane_t;
typedef struct texture_s {
char name[16];
unsigned width, height;
int anim_total; // total tenths in sequence ( 0 = no)
int anim_min, anim_max; // time for this frame min <=time< max
struct texture_s *anim_next; // in the animation sequence
struct texture_s *alternate_anims; // bmodels in frmae 1 use these
unsigned offsets[MIPLEVELS]; // four mip maps stored
} texture_t;
#define SURF_PLANEBACK 2
#define SURF_DRAWSKY 4
#define SURF_DRAWSPRITE 8
#define SURF_DRAWTURB 0x10
#define SURF_DRAWTILED 0x20
#define SURF_DRAWBACKGROUND 0x40
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct {
unsigned short v[2];
unsigned int cachededgeoffset;
} medge_t;
typedef struct {
float vecs[2][4];
float mipadjust;
texture_t *texture;
int flags;
} mtexinfo_t;
typedef struct msurface_s {
int visframe; // should be drawn when node is crossed
int dlightframe;
unsigned dlightbits;
mplane_t *plane;
int flags;
int firstedge; // look up in model->surfedges[], negative numbers
int numedges; // are backwards edges
// surface generation data
struct surfcache_s *cachespots[MIPLEVELS];
short texturemins[2];
short extents[2];
mtexinfo_t *texinfo;
// lighting info
byte styles[MAXLIGHTMAPS];
byte *samples; // [numstyles*surfsize]
} msurface_t;
typedef struct mnode_s {
// common with leaf
int contents; // 0, to differentiate from leafs
int visframe; // node needs to be traversed if current
short minmaxs[6]; // for bounding box culling
struct mnode_s *parent;
// node specific
mplane_t *plane;
struct mnode_s *children[2];
unsigned short firstsurface;
unsigned short numsurfaces;
} mnode_t;
typedef struct mleaf_s {
// common with node
int contents; // wil be a negative contents number
int visframe; // node needs to be traversed if current
short minmaxs[6]; // for bounding box culling
struct mnode_s *parent;
// leaf specific
byte *compressed_vis;
efrag_t *efrags;
msurface_t **firstmarksurface;
int nummarksurfaces;
int key; // BSP sequence number for leaf's contents
byte ambient_sound_level[NUM_AMBIENTS];
} mleaf_t;
// !!! if this is changed, it must be changed in asm_i386.h too !!!
typedef struct {
dclipnode_t *clipnodes;
mplane_t *planes;
int firstclipnode;
int lastclipnode;
vec3_t clip_mins;
vec3_t clip_maxs;
} hull_t;
/*
==============================================================================
SPRITE MODELS
==============================================================================
*/
// FIXME: shorten these?
typedef struct mspriteframe_s {
int width;
int height;
void *pcachespot; // remove?
float up, down, left, right;
byte pixels[4];
} mspriteframe_t;
typedef struct {
int numframes;
float *intervals;
mspriteframe_t *frames[1];
} mspritegroup_t;
typedef struct {
spriteframetype_t type;
mspriteframe_t *frameptr;
} mspriteframedesc_t;
typedef struct {
int type;
int maxwidth;
int maxheight;
int numframes;
float beamlength; // remove?
void *cachespot; // remove?
mspriteframedesc_t frames[1];
} msprite_t;
/*
==============================================================================
ALIAS MODELS
Alias models are position independent, so the cache manager can move them.
==============================================================================
*/
typedef struct {
aliasframetype_t type;
trivertx_t bboxmin;
trivertx_t bboxmax;
int frame;
char name[16];
} maliasframedesc_t;
typedef struct {
aliasskintype_t type;
void *pcachespot;
int skin;
} maliasskindesc_t;
typedef struct {
trivertx_t bboxmin;
trivertx_t bboxmax;
int frame;
} maliasgroupframedesc_t;
typedef struct {
int numframes;
int intervals;
maliasgroupframedesc_t frames[1];
} maliasgroup_t;
typedef struct {
int numskins;
int intervals;
maliasskindesc_t skindescs[1];
} maliasskingroup_t;
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct mtriangle_s {
int facesfront;
int vertindex[3];
} mtriangle_t;
typedef struct {
int model;
int stverts;
int skindesc;
int triangles;
maliasframedesc_t frames[1];
} aliashdr_t;
//===================================================================
//
// Whole model
//
typedef enum { mod_brush, mod_sprite, mod_alias } modtype_t;
#define EF_ROCKET 1 // leave a trail
#define EF_GRENADE 2 // leave a trail
#define EF_GIB 4 // leave a trail
#define EF_ROTATE 8 // rotate (bonus items)
#define EF_TRACER 16 // green split trail
#define EF_ZOMGIB 32 // small blood trail
#define EF_TRACER2 64 // orange split trail + rotate
#define EF_TRACER3 128 // purple trail
typedef struct model_s {
char name[MAX_QPATH];
qboolean needload; // bmodels and sprites don't cache normally
modtype_t type;
int numframes;
synctype_t synctype;
int flags;
//
// volume occupied by the model graphics
//
vec3_t mins, maxs;
float radius;
//
// brush model
//
int firstmodelsurface, nummodelsurfaces;
int numsubmodels;
dmodel_t *submodels;
int numplanes;
mplane_t *planes;
int numleafs; // number of visible leafs, not counting 0
mleaf_t *leafs;
int numvertexes;
mvertex_t *vertexes;
int numedges;
medge_t *edges;
int numnodes;
mnode_t *nodes;
int numtexinfo;
mtexinfo_t *texinfo;
int numsurfaces;
msurface_t *surfaces;
int numsurfedges;
int *surfedges;
int numclipnodes;
dclipnode_t *clipnodes;
int nummarksurfaces;
msurface_t **marksurfaces;
hull_t hulls[MAX_MAP_HULLS];
int numtextures;
texture_t **textures;
byte *visdata;
byte *lightdata;
char *entities;
//
// additional model data
//
cache_user_t cache; // only access through Mod_Extradata
} model_t;
//============================================================================
void Mod_Init(void);
void Mod_ClearAll(void);
model_t *Mod_ForName(char *name, qboolean crash);
void *Mod_Extradata(model_t *mod); // handles caching
void Mod_TouchModel(char *name);
void Mod_Print(void);
mleaf_t *Mod_PointInLeaf(float *p, model_t *model);
byte *Mod_LeafPVS(mleaf_t *leaf, model_t *model);
// FIXME - surely this doesn't belong here?
texture_t *R_TextureAnimation(texture_t *base);
#endif /* MODEL_H */

138
NQ/modelgen.h Normal file
View File

@ -0,0 +1,138 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef MODELGEN_H
#define MODELGEN_H
//
// modelgen.h: header file for model generation program
//
// *********************************************************
// * This file must be identical in the modelgen directory *
// * and in the Quake directory, because it's used to *
// * pass data from one to the other via model files. *
// *********************************************************
// FIXME - check modelgen for INCLUDELIBS (it's not in the engine...)
//#ifdef INCLUDELIBS
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "host.h"
#include "mathlib.h"
#include "qtypes.h"
//#endif
#define ALIAS_VERSION 6
#define ALIAS_ONSEAM 0x0020
// must match definition in spritegn.h
#ifndef SYNCTYPE_T
#define SYNCTYPE_T
typedef enum { ST_SYNC = 0, ST_RAND } synctype_t;
#endif
typedef enum { ALIAS_SINGLE = 0, ALIAS_GROUP } aliasframetype_t;
typedef enum { ALIAS_SKIN_SINGLE = 0, ALIAS_SKIN_GROUP } aliasskintype_t;
typedef struct {
int ident;
int version;
vec3_t scale;
vec3_t scale_origin;
float boundingradius;
vec3_t eyeposition;
int numskins;
int skinwidth;
int skinheight;
int numverts;
int numtris;
int numframes;
synctype_t synctype;
int flags;
float size;
} mdl_t;
// TODO: could be shorts
typedef struct {
int onseam;
int s;
int t;
} stvert_t;
typedef struct dtriangle_s {
int facesfront;
int vertindex[3];
} dtriangle_t;
#define DT_FACES_FRONT 0x0010
// This mirrors trivert_t in trilib.h, is present so Quake knows how to
// load this data
typedef struct {
byte v[3];
byte lightnormalindex;
} trivertx_t;
typedef struct {
trivertx_t bboxmin; // lightnormal isn't used
trivertx_t bboxmax; // lightnormal isn't used
char name[16]; // frame name from grabbing
} daliasframe_t;
typedef struct {
int numframes;
trivertx_t bboxmin; // lightnormal isn't used
trivertx_t bboxmax; // lightnormal isn't used
} daliasgroup_t;
typedef struct {
int numskins;
} daliasskingroup_t;
typedef struct {
float interval;
} daliasinterval_t;
typedef struct {
float interval;
} daliasskininterval_t;
typedef struct {
aliasframetype_t type;
} daliasframetype_t;
typedef struct {
aliasskintype_t type;
} daliasskintype_t;
/* little-endian "IDPO" */
#define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I')
#endif /* MODELGEN_H */

347
NQ/net.h Normal file
View File

@ -0,0 +1,347 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef NET_H
#define NET_H
#include "common.h"
/* net.h -- quake's interface to the networking layer */
struct qsockaddr {
short sa_family;
unsigned char sa_data[14];
};
#define NET_NAMELEN 64
#define NET_MAXMESSAGE 8192
#define NET_HEADERSIZE (2 * sizeof(unsigned int))
#define NET_DATAGRAMSIZE (MAX_DATAGRAM + NET_HEADERSIZE)
// NetHeader flags
#define NETFLAG_LENGTH_MASK 0x0000ffff
#define NETFLAG_DATA 0x00010000
#define NETFLAG_ACK 0x00020000
#define NETFLAG_NAK 0x00040000
#define NETFLAG_EOM 0x00080000
#define NETFLAG_UNRELIABLE 0x00100000
#define NETFLAG_CTL 0x80000000
#define NET_PROTOCOL_VERSION 3
/*
* This is the network info/connection protocol. It is used to find Quake
* servers, get info about them, and connect to them. Once connected, the
* Quake game protocol (documented elsewhere) is used.
*
* General notes:
* game_name is currently always "QUAKE", but is there so this same protocol
* can be used for future games as well; can you say Quake2?
*
* CCREQ_CONNECT
* string game_name "QUAKE"
* byte net_protocol_version NET_PROTOCOL_VERSION
*
* CCREQ_SERVER_INFO
* string game_name "QUAKE"
* byte net_protocol_version NET_PROTOCOL_VERSION
*
* CCREQ_PLAYER_INFO
* byte player_number
*
* CCREQ_RULE_INFO
* string rule
*
*
*
* CCREP_ACCEPT
* long port
*
* CCREP_REJECT
* string reason
*
* CCREP_SERVER_INFO
* string server_address
* string host_name
* string level_name
* byte current_players
* byte max_players
* byte protocol_version NET_PROTOCOL_VERSION
*
* CCREP_PLAYER_INFO
* byte player_number
* string name
* long colors
* long frags
* long connect_time
* string address
*
* CCREP_RULE_INFO
* string rule
* string value
*
* note:
* There are two address forms used above. The short form is just a port
* number. The address that goes along with the port is defined * as
* "whatever address you receive this reponse from". This lets us * use
* the host OS to solve the problem of multiple host addresses * (possibly
* with no routing between them); the host will use the right * address
* when we reply to the inbound connection request. The long * from is a
* full address and port in a string. It is used for * returning the
* address of a server that is not running locally.
*/
#define CCREQ_CONNECT 0x01
#define CCREQ_SERVER_INFO 0x02
#define CCREQ_PLAYER_INFO 0x03
#define CCREQ_RULE_INFO 0x04
#define CCREP_ACCEPT 0x81
#define CCREP_REJECT 0x82
#define CCREP_SERVER_INFO 0x83
#define CCREP_PLAYER_INFO 0x84
#define CCREP_RULE_INFO 0x85
struct net_landriver_s;
struct net_driver_s;
typedef struct qsocket_s {
struct qsocket_s *next;
double connecttime;
double lastMessageTime;
double lastSendTime;
qboolean disconnected;
qboolean canSend;
qboolean sendNext;
struct net_driver_s *driver;
struct net_landriver_s *landriver;
int socket;
void *driverdata;
unsigned int ackSequence;
unsigned int sendSequence;
unsigned int unreliableSendSequence;
int sendMessageLength;
byte sendMessage[NET_MAXMESSAGE];
unsigned int receiveSequence;
unsigned int unreliableReceiveSequence;
int receiveMessageLength;
byte receiveMessage[NET_MAXMESSAGE];
struct qsockaddr addr;
char address[NET_NAMELEN];
} qsocket_t;
extern qsocket_t *net_activeSockets;
extern qsocket_t *net_freeSockets;
typedef struct net_landriver_s {
char *name;
qboolean initialized;
int controlSock;
int (*Init)(void);
void (*Shutdown)(void);
void (*Listen)(qboolean state);
int (*OpenSocket)(int port);
int (*CloseSocket)(int socket);
int (*Connect)(int socket, struct qsockaddr *addr);
int (*CheckNewConnections)(void);
int (*Read)(int socket, byte *buf, int len, struct qsockaddr *addr);
int (*Write)(int socket, byte *buf, int len, struct qsockaddr *addr);
int (*Broadcast)(int socket, byte *buf, int len);
char *(*AddrToString)(struct qsockaddr *addr);
int (*StringToAddr)(char *string, struct qsockaddr *addr);
int (*GetSocketAddr)(int socket, struct qsockaddr *addr);
int (*GetNameFromAddr)(struct qsockaddr *addr, char *name);
int (*GetAddrFromName)(char *name, struct qsockaddr *addr);
int (*AddrCompare)(struct qsockaddr *addr1, struct qsockaddr *addr2);
int (*GetSocketPort)(struct qsockaddr *addr);
int (*SetSocketPort)(struct qsockaddr *addr, int port);
} net_landriver_t;
#define MAX_NET_DRIVERS 8
extern int net_numlandrivers;
extern net_landriver_t net_landrivers[MAX_NET_DRIVERS];
typedef struct net_driver_s {
char *name;
qboolean initialized;
int (*Init)(void);
void (*Listen)(qboolean state);
void (*SearchForHosts)(qboolean xmit);
qsocket_t *(*Connect)(char *host);
qsocket_t *(*CheckNewConnections)(void);
int (*QGetMessage)(qsocket_t *sock);
int (*QSendMessage)(qsocket_t *sock, sizebuf_t *data);
int (*SendUnreliableMessage)(qsocket_t *sock, sizebuf_t *data);
qboolean (*CanSendMessage)(qsocket_t *sock);
qboolean (*CanSendUnreliableMessage)(qsocket_t *sock);
void (*Close)(qsocket_t *sock);
void (*Shutdown)(void);
int controlSock;
} net_driver_t;
extern int net_numdrivers;
extern net_driver_t net_drivers[MAX_NET_DRIVERS];
extern int DEFAULTnet_hostport;
extern int net_hostport;
extern net_driver_t *net_driver;
extern cvar_t hostname;
extern char playername[];
extern int playercolor;
extern int messagesSent;
extern int messagesReceived;
extern int unreliableMessagesSent;
extern int unreliableMessagesReceived;
qsocket_t *NET_NewQSocket(void);
void NET_FreeQSocket(qsocket_t *);
double SetNetTime(void);
#define HOSTCACHESIZE 8
typedef struct {
char name[16];
char map[16];
char cname[32];
int users;
int maxusers;
net_driver_t *driver;
net_landriver_t *ldriver;
struct qsockaddr addr;
} hostcache_t;
extern int hostCacheCount;
extern hostcache_t hostcache[HOSTCACHESIZE];
#ifdef IDGODS
qboolean IsID(struct qsockaddr *addr);
#endif
/*
* ===========================================================================
*
* public network functions
*
* ===========================================================================
*/
extern double net_time;
extern sizebuf_t net_message;
extern int net_activeconnections;
void NET_Init(void);
void NET_Shutdown(void);
/*
* returns a new connection number if there is one pending, else -1
*/
struct qsocket_s *NET_CheckNewConnections(void);
/*
* called by client to connect to a host. Returns -1 if not able to
*/
struct qsocket_s *NET_Connect(char *host);
/*
* Returns true or false if the given qsocket can currently accept a message
* to be transmitted.
*/
qboolean NET_CanSendMessage(qsocket_t *sock);
/*
* returns data in net_message sizebuf
* returns 0 if no data is waiting
* returns 1 if a message was received
* returns 2 if an unreliable message was received
* returns -1 if the connection died
*/
int NET_GetMessage(struct qsocket_s *sock);
/*
* returns 0 if the message connot be delivered reliably, but the connection
* is still considered valid
* returns 1 if the message was sent properly
* returns -1 if the connection died
*/
int NET_SendMessage(struct qsocket_s *sock, sizebuf_t *data);
int NET_SendUnreliableMessage(struct qsocket_s *sock, sizebuf_t *data);
/*
* This is a reliable *blocking* send to all attached clients.
*/
int NET_SendToAll(sizebuf_t *data, int blocktime);
/*
* if a dead connection is returned by a get or send function, this function
* should be called when it is convenient
*
* Server calls when a client is kicked off for a game related misbehavior
* like an illegal protocal conversation. Client calls when disconnecting
* from a server. A netcon_t number will not be reused until this function is
* called for it
*/
void NET_Close(struct qsocket_s *sock);
void NET_Poll(void);
typedef struct _PollProcedure {
struct _PollProcedure *next;
double nextTime;
void (*procedure) ();
void *arg;
} PollProcedure;
void SchedulePollProcedure(PollProcedure *pp, double timeOffset);
extern qboolean serialAvailable;
extern qboolean ipxAvailable;
extern qboolean tcpipAvailable;
extern char my_ipx_address[NET_NAMELEN];
extern char my_tcpip_address[NET_NAMELEN];
extern void (*GetComPortConfig)(int portNumber, int *port, int *irq,
int *baud, qboolean *useModem);
extern void (*SetComPortConfig)(int portNumber, int port, int irq, int baud,
qboolean useModem);
extern void (*GetModemConfig)(int portNumber, char *dialType, char *clear,
char *init, char *hangup);
extern void (*SetModemConfig)(int portNumber, char *dialType, char *clear,
char *init, char *hangup);
extern qboolean slistInProgress;
extern qboolean slistSilent;
extern qboolean slistLocal;
void NET_Slist_f(void);
extern qboolean recording;
#endif /* NET_H */

90
NQ/net_bsd.c Normal file
View File

@ -0,0 +1,90 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "net.h"
#include "net_dgrm.h"
#include "net_loop.h"
#include "net_udp.h"
#include "quakedef.h"
net_driver_t net_drivers[MAX_NET_DRIVERS] = {
{
.name = "Loopback",
.initialized = false,
.Init = Loop_Init,
.Listen = Loop_Listen,
.SearchForHosts = Loop_SearchForHosts,
.Connect = Loop_Connect,
.CheckNewConnections = Loop_CheckNewConnections,
.QGetMessage = Loop_GetMessage,
.QSendMessage = Loop_SendMessage,
.SendUnreliableMessage = Loop_SendUnreliableMessage,
.CanSendMessage = Loop_CanSendMessage,
.CanSendUnreliableMessage = Loop_CanSendUnreliableMessage,
.Close = Loop_Close,
.Shutdown = Loop_Shutdown
}, {
.name = "Datagram",
.initialized = false,
.Init = Datagram_Init,
.Listen = Datagram_Listen,
.SearchForHosts = Datagram_SearchForHosts,
.Connect = Datagram_Connect,
.CheckNewConnections = Datagram_CheckNewConnections,
.QGetMessage = Datagram_GetMessage,
.QSendMessage = Datagram_SendMessage,
.SendUnreliableMessage = Datagram_SendUnreliableMessage,
.CanSendMessage = Datagram_CanSendMessage,
.CanSendUnreliableMessage = Datagram_CanSendUnreliableMessage,
.Close = Datagram_Close,
.Shutdown = Datagram_Shutdown
}
};
int net_numdrivers = 2;
net_landriver_t net_landrivers[MAX_NET_DRIVERS] = {
{
.name = "UDP",
.initialized = false,
.controlSock = 0,
.Init = UDP_Init,
.Shutdown = UDP_Shutdown,
.Listen = UDP_Listen,
.OpenSocket = UDP_OpenSocket,
.CloseSocket = UDP_CloseSocket,
.Connect = UDP_Connect,
.CheckNewConnections = UDP_CheckNewConnections,
.Read = UDP_Read,
.Write = UDP_Write,
.Broadcast = UDP_Broadcast,
.AddrToString = UDP_AddrToString,
.StringToAddr = UDP_StringToAddr,
.GetSocketAddr = UDP_GetSocketAddr,
.GetNameFromAddr = UDP_GetNameFromAddr,
.GetAddrFromName = UDP_GetAddrFromName,
.AddrCompare = UDP_AddrCompare,
.GetSocketPort = UDP_GetSocketPort,
.SetSocketPort = UDP_SetSocketPort
}
};
int net_numlandrivers = 1;

1416
NQ/net_dgrm.c Normal file

File diff suppressed because it is too large Load Diff

42
NQ/net_dgrm.h Normal file
View File

@ -0,0 +1,42 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef NET_DGRM_H
#define NET_DGRM_H
#include "net.h"
// net_dgrm.h
int Datagram_Init(void);
void Datagram_Listen(qboolean state);
void Datagram_SearchForHosts(qboolean xmit);
qsocket_t *Datagram_Connect(char *host);
qsocket_t *Datagram_CheckNewConnections(void);
int Datagram_GetMessage(qsocket_t *sock);
int Datagram_SendMessage(qsocket_t *sock, sizebuf_t *data);
int Datagram_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data);
qboolean Datagram_CanSendMessage(qsocket_t *sock);
qboolean Datagram_CanSendUnreliableMessage(qsocket_t *sock);
void Datagram_Close(qsocket_t *sock);
void Datagram_Shutdown(void);
#endif /* NET_DGRM_H */

260
NQ/net_loop.c Normal file
View File

@ -0,0 +1,260 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// net_loop.c
#include "quakedef.h"
#include "net_loop.h"
#include "client.h"
#include "server.h"
#include "console.h"
#include "sys.h"
qboolean localconnectpending = false;
qsocket_t *loop_client = NULL;
qsocket_t *loop_server = NULL;
int
Loop_Init(void)
{
if (cls.state == ca_dedicated)
return -1;
return 0;
}
void
Loop_Shutdown(void)
{
}
void
Loop_Listen(qboolean state)
{
}
void
Loop_SearchForHosts(qboolean xmit)
{
if (!sv.active)
return;
hostCacheCount = 1;
if (strcmp(hostname.string, "UNNAMED") == 0)
strcpy(hostcache[0].name, "local");
else
strcpy(hostcache[0].name, hostname.string);
strcpy(hostcache[0].map, sv.name);
hostcache[0].users = net_activeconnections;
hostcache[0].maxusers = svs.maxclients;
hostcache[0].driver = net_driver;
strcpy(hostcache[0].cname, "local");
}
qsocket_t *
Loop_Connect(char *host)
{
if (strcmp(host, "local") != 0)
return NULL;
localconnectpending = true;
if (!loop_client) {
if ((loop_client = NET_NewQSocket()) == NULL) {
Con_Printf("Loop_Connect: no qsocket available\n");
return NULL;
}
strcpy(loop_client->address, "localhost");
}
loop_client->receiveMessageLength = 0;
loop_client->sendMessageLength = 0;
loop_client->canSend = true;
if (!loop_server) {
if ((loop_server = NET_NewQSocket()) == NULL) {
Con_Printf("Loop_Connect: no qsocket available\n");
return NULL;
}
strcpy(loop_server->address, "LOCAL");
}
loop_server->receiveMessageLength = 0;
loop_server->sendMessageLength = 0;
loop_server->canSend = true;
loop_client->driverdata = (void *)loop_server;
loop_server->driverdata = (void *)loop_client;
return loop_client;
}
qsocket_t *
Loop_CheckNewConnections(void)
{
if (!localconnectpending)
return NULL;
localconnectpending = false;
loop_server->sendMessageLength = 0;
loop_server->receiveMessageLength = 0;
loop_server->canSend = true;
loop_client->sendMessageLength = 0;
loop_client->receiveMessageLength = 0;
loop_client->canSend = true;
return loop_server;
}
static int
IntAlign(int value)
{
return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1));
}
int
Loop_GetMessage(qsocket_t *sock)
{
int ret;
int length;
if (sock->receiveMessageLength == 0)
return 0;
ret = sock->receiveMessage[0];
length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8);
// alignment byte skipped here
SZ_Clear(&net_message);
SZ_Write(&net_message, &sock->receiveMessage[4], length);
length = IntAlign(length + 4);
sock->receiveMessageLength -= length;
if (sock->receiveMessageLength)
memcpy(sock->receiveMessage, &sock->receiveMessage[length],
sock->receiveMessageLength);
if (sock->driverdata && ret == 1)
((qsocket_t *)sock->driverdata)->canSend = true;
return ret;
}
int
Loop_SendMessage(qsocket_t *sock, sizebuf_t *data)
{
byte *buffer;
int *bufferLength;
if (!sock->driverdata)
return -1;
bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
if ((*bufferLength + data->cursize + 4) > NET_MAXMESSAGE)
Sys_Error("%s: overflow", __func__);
buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
// message type
*buffer++ = 1;
// length
*buffer++ = data->cursize & 0xff;
*buffer++ = data->cursize >> 8;
// align
buffer++;
// message
memcpy(buffer, data->data, data->cursize);
*bufferLength = IntAlign(*bufferLength + data->cursize + 4);
sock->canSend = false;
return 1;
}
int
Loop_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data)
{
byte *buffer;
int *bufferLength;
if (!sock->driverdata)
return -1;
bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short)) >
NET_MAXMESSAGE)
return 0;
buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
// message type
*buffer++ = 2;
// length
*buffer++ = data->cursize & 0xff;
*buffer++ = data->cursize >> 8;
// align
buffer++;
// message
memcpy(buffer, data->data, data->cursize);
*bufferLength = IntAlign(*bufferLength + data->cursize + 4);
return 1;
}
qboolean
Loop_CanSendMessage(qsocket_t *sock)
{
if (!sock->driverdata)
return false;
return sock->canSend;
}
qboolean
Loop_CanSendUnreliableMessage(qsocket_t *sock)
{
return true;
}
void
Loop_Close(qsocket_t *sock)
{
if (sock->driverdata)
((qsocket_t *)sock->driverdata)->driverdata = NULL;
sock->receiveMessageLength = 0;
sock->sendMessageLength = 0;
sock->canSend = true;
if (sock == loop_client)
loop_client = NULL;
else
loop_server = NULL;
}

41
NQ/net_loop.h Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef NET_LOOP_H
#define NET_LOOP_H
#include "net.h"
// net_loop.h
int Loop_Init(void);
void Loop_Listen(qboolean state);
void Loop_SearchForHosts(qboolean xmit);
qsocket_t *Loop_Connect(char *host);
qsocket_t *Loop_CheckNewConnections(void);
int Loop_GetMessage(qsocket_t *sock);
int Loop_SendMessage(qsocket_t *sock, sizebuf_t *data);
int Loop_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data);
qboolean Loop_CanSendMessage(qsocket_t *sock);
qboolean Loop_CanSendUnreliableMessage(qsocket_t *sock);
void Loop_Close(qsocket_t *sock);
void Loop_Shutdown(void);
#endif /* NET_LOOP_H */

985
NQ/net_main.c Normal file
View File

@ -0,0 +1,985 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "cmd.h"
#include "console.h"
#include "net_vcr.h"
#include "quakedef.h"
#include "server.h"
#include "sys.h"
#include "zone.h"
qsocket_t *net_activeSockets = NULL;
qsocket_t *net_freeSockets = NULL;
static int net_numsockets = 0;
qboolean serialAvailable = false;
qboolean ipxAvailable = false;
qboolean tcpipAvailable = false;
int net_hostport;
int DEFAULTnet_hostport = 26000;
char my_ipx_address[NET_NAMELEN];
char my_tcpip_address[NET_NAMELEN];
void (*GetComPortConfig)(int portNumber, int *port, int *irq,
int *baud, qboolean *useModem);
void (*SetComPortConfig)(int portNumber, int port, int irq, int baud,
qboolean useModem);
void (*GetModemConfig)(int portNumber, char *dialType, char *clear,
char *init, char *hangup);
void (*SetModemConfig)(int portNumber, char *dialType, char *clear,
char *init, char *hangup);
static qboolean listening = false;
qboolean slistInProgress = false;
qboolean slistSilent = false;
qboolean slistLocal = true;
#define IS_LOOP_DRIVER(p) ((p) == &net_drivers[0])
static double slistStartTime;
static int slistLastShown;
static void Slist_Send(void);
static void Slist_Poll(void);
static PollProcedure slistSendProcedure = { NULL, 0.0, Slist_Send };
static PollProcedure slistPollProcedure = { NULL, 0.0, Slist_Poll };
sizebuf_t net_message;
int net_activeconnections = 0;
int messagesSent = 0;
int messagesReceived = 0;
int unreliableMessagesSent = 0;
int unreliableMessagesReceived = 0;
cvar_t net_messagetimeout = { "net_messagetimeout", "300" };
cvar_t hostname = { "hostname", "UNNAMED" };
qboolean configRestored = false;
cvar_t config_com_port = { "_config_com_port", "0x3f8", true };
cvar_t config_com_irq = { "_config_com_irq", "4", true };
cvar_t config_com_baud = { "_config_com_baud", "57600", true };
cvar_t config_com_modem = { "_config_com_modem", "1", true };
cvar_t config_modem_dialtype = { "_config_modem_dialtype", "T", true };
cvar_t config_modem_clear = { "_config_modem_clear", "ATZ", true };
cvar_t config_modem_init = { "_config_modem_init", "", true };
cvar_t config_modem_hangup = { "_config_modem_hangup", "AT H", true };
#ifdef IDGODS
cvar_t idgods = { "idgods", "0" };
#endif
qboolean recording = false;
net_driver_t *net_driver;
double net_time;
double
SetNetTime(void)
{
net_time = Sys_DoubleTime();
return net_time;
}
/*
* ===================
* NET_NewQSocket
*
* Called by drivers when a new communications endpoint is required
* The sequence and buffer fields will be filled in properly
* ===================
*/
qsocket_t *
NET_NewQSocket(void)
{
qsocket_t *sock;
if (!net_freeSockets)
return NULL;
if (net_activeconnections >= svs.maxclients)
return NULL;
/* get one from free list */
sock = net_freeSockets;
net_freeSockets = sock->next;
/* add it to active list */
sock->next = net_activeSockets;
net_activeSockets = sock;
sock->disconnected = false;
sock->connecttime = net_time;
strcpy(sock->address, "UNSET ADDRESS");
sock->driver = net_driver;
sock->socket = 0;
sock->driverdata = NULL;
sock->canSend = true;
sock->sendNext = false;
sock->lastMessageTime = net_time;
sock->ackSequence = 0;
sock->sendSequence = 0;
sock->unreliableSendSequence = 0;
sock->sendMessageLength = 0;
sock->receiveSequence = 0;
sock->unreliableReceiveSequence = 0;
sock->receiveMessageLength = 0;
return sock;
}
void
NET_FreeQSocket(qsocket_t *sock)
{
qsocket_t *s;
/* remove it from active list */
if (sock == net_activeSockets)
net_activeSockets = net_activeSockets->next;
else {
for (s = net_activeSockets; s; s = s->next)
if (s->next == sock) {
s->next = sock->next;
break;
}
if (!s)
Sys_Error("%s: not active", __func__);
}
/* add it to free list */
sock->next = net_freeSockets;
net_freeSockets = sock;
sock->disconnected = true;
}
static void
NET_Listen_f(void)
{
int i;
if (Cmd_Argc() != 2) {
Con_Printf("\"listen\" is \"%u\"\n", listening ? 1 : 0);
return;
}
listening = Q_atoi(Cmd_Argv(1)) ? true : false;
for (i = 0; i < net_numdrivers;i++) {
net_driver = &net_drivers[i];
if (net_driver->initialized == false)
continue;
net_driver->Listen(listening);
}
}
static void
MaxPlayers_f(void)
{
int n;
if (Cmd_Argc() != 2) {
Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
return;
}
if (sv.active) {
Con_Printf
("maxplayers can not be changed while a server is running.\n");
return;
}
n = Q_atoi(Cmd_Argv(1));
if (n < 1)
n = 1;
if (n > svs.maxclientslimit) {
n = svs.maxclientslimit;
Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
}
if ((n == 1) && listening)
Cbuf_AddText("listen 0\n");
if ((n > 1) && (!listening))
Cbuf_AddText("listen 1\n");
svs.maxclients = n;
if (n == 1) {
Cvar_Set("deathmatch", "0");
Cvar_Set("coop", "0");
} else {
if (coop.value)
Cvar_Set("deathmatch", "0");
else
Cvar_Set("deathmatch", "1");
}
}
static void
NET_Port_f(void)
{
int n;
if (Cmd_Argc() != 2) {
Con_Printf("\"port\" is \"%u\"\n", net_hostport);
return;
}
n = Q_atoi(Cmd_Argv(1));
if (n < 1 || n > 65534) {
Con_Printf("Bad value, must be between 1 and 65534\n");
return;
}
DEFAULTnet_hostport = n;
net_hostport = n;
if (listening) {
/* force a change to the new port */
Cbuf_AddText("listen 0\n");
Cbuf_AddText("listen 1\n");
}
}
static void
PrintSlistHeader(void)
{
Con_Printf("Server Map Users\n");
Con_Printf("--------------- --------------- -----\n");
slistLastShown = 0;
}
static void
PrintSlist(void)
{
int n;
for (n = slistLastShown; n < hostCacheCount; n++) {
if (hostcache[n].maxusers)
Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name,
hostcache[n].map, hostcache[n].users,
hostcache[n].maxusers);
else
Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name,
hostcache[n].map);
}
slistLastShown = n;
}
static void
PrintSlistTrailer(void)
{
if (hostCacheCount)
Con_Printf("== end list ==\n\n");
else
Con_Printf("No Quake servers found.\n\n");
}
void
NET_Slist_f(void)
{
if (slistInProgress)
return;
if (!slistSilent) {
Con_Printf("Looking for Quake servers...\n");
PrintSlistHeader();
}
slistInProgress = true;
slistStartTime = Sys_DoubleTime();
SchedulePollProcedure(&slistSendProcedure, 0.0);
SchedulePollProcedure(&slistPollProcedure, 0.1);
hostCacheCount = 0;
}
static void
Slist_Send(void)
{
int i;
for (i = 0; i < net_numdrivers; i++) {
net_driver = &net_drivers[i];
/* Only list the loop driver if slistLocal is true */
if (!slistLocal && IS_LOOP_DRIVER(net_driver))
continue;
if (net_driver->initialized == false)
continue;
net_driver->SearchForHosts(true);
}
if ((Sys_DoubleTime() - slistStartTime) < 0.5)
SchedulePollProcedure(&slistSendProcedure, 0.75);
}
static void
Slist_Poll(void)
{
int i;
for (i = 0; i < net_numdrivers; i++) {
net_driver = &net_drivers[i];
/* Only list the loop driver if slistLocal is true */
if (!slistLocal && IS_LOOP_DRIVER(net_driver))
continue;
if (net_driver->initialized == false)
continue;
net_driver->SearchForHosts(false);
}
if (!slistSilent)
PrintSlist();
if ((Sys_DoubleTime() - slistStartTime) < 1.5) {
SchedulePollProcedure(&slistPollProcedure, 0.1);
return;
}
if (!slistSilent)
PrintSlistTrailer();
slistInProgress = false;
slistSilent = false;
slistLocal = true;
}
/*
* ===================
* NET_Connect
* ===================
*/
int hostCacheCount = 0;
hostcache_t hostcache[HOSTCACHESIZE];
qsocket_t *
NET_Connect(char *host)
{
qsocket_t *ret;
int i, n;
int numdrivers = net_numdrivers;
SetNetTime();
if (host && *host == 0)
host = NULL;
if (host) {
if (strcasecmp(host, "local") == 0) {
numdrivers = 1;
goto JustDoIt;
}
if (hostCacheCount) {
for (n = 0; n < hostCacheCount; n++)
if (strcasecmp(host, hostcache[n].name) == 0) {
host = hostcache[n].cname;
break;
}
if (n < hostCacheCount)
goto JustDoIt;
}
}
slistSilent = host ? true : false;
NET_Slist_f();
while (slistInProgress)
NET_Poll();
if (host == NULL) {
if (hostCacheCount != 1)
return NULL;
host = hostcache[0].cname;
Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host);
}
if (hostCacheCount)
for (n = 0; n < hostCacheCount; n++)
if (strcasecmp(host, hostcache[n].name) == 0) {
host = hostcache[n].cname;
break;
}
JustDoIt:
for (i = 0; i < numdrivers; i++) {
net_driver = &net_drivers[i];
if (net_driver->initialized == false)
continue;
ret = net_driver->Connect(host);
if (ret)
return ret;
}
if (host) {
Con_Printf("\n");
PrintSlistHeader();
PrintSlist();
PrintSlistTrailer();
}
return NULL;
}
/*
* ===================
* NET_CheckNewConnections
* ===================
*/
static struct {
double time;
int op;
long session;
} vcrConnect;
qsocket_t *
NET_CheckNewConnections(void)
{
int i;
qsocket_t *ret;
SetNetTime();
for (i = 0; i < net_numdrivers; i++) {
net_driver = &net_drivers[i];
if (net_driver->initialized == false)
continue;
if (!IS_LOOP_DRIVER(net_driver) && listening == false)
continue;
ret = net_driver->CheckNewConnections();
if (ret) {
if (recording) {
vcrConnect.time = host_time;
vcrConnect.op = VCR_OP_CONNECT;
vcrConnect.session = (long)ret;
Sys_FileWrite(vcrFile, &vcrConnect, sizeof(vcrConnect));
Sys_FileWrite(vcrFile, ret->address, NET_NAMELEN);
}
return ret;
}
}
if (recording) {
vcrConnect.time = host_time;
vcrConnect.op = VCR_OP_CONNECT;
vcrConnect.session = 0;
Sys_FileWrite(vcrFile, &vcrConnect, sizeof(vcrConnect));
}
return NULL;
}
/*
* ===================
* NET_Close
* ===================
*/
void
NET_Close(qsocket_t *sock)
{
if (!sock)
return;
if (sock->disconnected)
return;
SetNetTime();
/* call the driver_Close function */
sock->driver->Close(sock);
NET_FreeQSocket(sock);
}
/*
* =================
* NET_GetMessage
*
* If there is a complete message, return it in net_message
*
* returns 0 if no data is waiting
* returns 1 if a message was received
* returns -1 if connection is invalid
* =================
*/
static struct {
double time;
int op;
long session;
int ret;
int len;
} vcrGetMessage;
int
NET_GetMessage(qsocket_t *sock)
{
int ret;
if (!sock)
return -1;
if (sock->disconnected) {
Con_Printf("%s: disconnected socket\n", __func__);
return -1;
}
SetNetTime();
ret = sock->driver->QGetMessage(sock);
/* see if this connection has timed out (not for loop) */
if (ret == 0 && (!IS_LOOP_DRIVER(sock->driver))) {
if (net_time - sock->lastMessageTime > net_messagetimeout.value) {
NET_Close(sock);
return -1;
}
}
if (ret > 0) {
if (!IS_LOOP_DRIVER(sock->driver)) {
sock->lastMessageTime = net_time;
if (ret == 1)
messagesReceived++;
else if (ret == 2)
unreliableMessagesReceived++;
}
if (recording) {
vcrGetMessage.time = host_time;
vcrGetMessage.op = VCR_OP_GETMESSAGE;
vcrGetMessage.session = (long)sock;
vcrGetMessage.ret = ret;
vcrGetMessage.len = net_message.cursize;
Sys_FileWrite(vcrFile, &vcrGetMessage, 24);
Sys_FileWrite(vcrFile, net_message.data, net_message.cursize);
}
} else {
if (recording) {
vcrGetMessage.time = host_time;
vcrGetMessage.op = VCR_OP_GETMESSAGE;
vcrGetMessage.session = (long)sock;
vcrGetMessage.ret = ret;
Sys_FileWrite(vcrFile, &vcrGetMessage, 20);
}
}
return ret;
}
/*
* ==================
* NET_SendMessage
*
* Try to send a complete length+message unit over the reliable stream.
* returns 0 : if the message cannot be delivered reliably, but the connection
* is still considered valid
* returns 1 : if the message was sent properly
* returns -1 : if the connection died
* ==================
*/
static struct {
double time;
int op;
long session;
int r;
} vcrSendMessage;
int
NET_SendMessage(qsocket_t *sock, sizebuf_t *data)
{
int r;
if (!sock)
return -1;
if (sock->disconnected) {
Con_Printf("%s: disconnected socket\n", __func__);
return -1;
}
SetNetTime();
r = sock->driver->QSendMessage(sock, data);
if (r == 1 && !IS_LOOP_DRIVER(sock->driver))
messagesSent++;
if (recording) {
vcrSendMessage.time = host_time;
vcrSendMessage.op = VCR_OP_SENDMESSAGE;
vcrSendMessage.session = (long)sock;
vcrSendMessage.r = r;
Sys_FileWrite(vcrFile, &vcrSendMessage, 20);
}
return r;
}
int
NET_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data)
{
int r;
if (!sock)
return -1;
if (sock->disconnected) {
Con_Printf("NET_SendMessage: disconnected socket\n");
return -1;
}
SetNetTime();
r = sock->driver->SendUnreliableMessage(sock, data);
if (r == 1 && !IS_LOOP_DRIVER(sock->driver))
unreliableMessagesSent++;
if (recording) {
vcrSendMessage.time = host_time;
vcrSendMessage.op = VCR_OP_SENDMESSAGE;
vcrSendMessage.session = (long)sock;
vcrSendMessage.r = r;
Sys_FileWrite(vcrFile, &vcrSendMessage, 20);
}
return r;
}
/*
* ==================
* NET_CanSendMessage
*
* Returns true or false if the given qsocket can currently accept a
* message to be transmitted.
* ==================
*/
qboolean
NET_CanSendMessage(qsocket_t *sock)
{
int r;
if (!sock)
return false;
if (sock->disconnected)
return false;
SetNetTime();
r = sock->driver->CanSendMessage(sock);
if (recording) {
vcrSendMessage.time = host_time;
vcrSendMessage.op = VCR_OP_CANSENDMESSAGE;
vcrSendMessage.session = (long)sock;
vcrSendMessage.r = r;
Sys_FileWrite(vcrFile, &vcrSendMessage, 20);
}
return r;
}
int
NET_SendToAll(sizebuf_t *data, int blocktime)
{
double start;
int i;
int count = 0;
qboolean state1[MAX_SCOREBOARD];
qboolean state2[MAX_SCOREBOARD];
for (i = 0, host_client = svs.clients; i < svs.maxclients;
i++, host_client++) {
if (!host_client->netconnection)
continue;
if (host_client->active) {
if (host_client->netconnection->driver == net_drivers) {
NET_SendMessage(host_client->netconnection, data);
state1[i] = true;
state2[i] = true;
continue;
}
count++;
state1[i] = false;
state2[i] = false;
} else {
state1[i] = true;
state2[i] = true;
}
}
start = Sys_DoubleTime();
while (count) {
count = 0;
for (i = 0, host_client = svs.clients; i < svs.maxclients;
i++, host_client++) {
if (!state1[i]) {
if (NET_CanSendMessage(host_client->netconnection)) {
state1[i] = true;
NET_SendMessage(host_client->netconnection, data);
} else {
NET_GetMessage(host_client->netconnection);
}
count++;
continue;
}
if (!state2[i]) {
if (NET_CanSendMessage(host_client->netconnection)) {
state2[i] = true;
} else {
NET_GetMessage(host_client->netconnection);
}
count++;
continue;
}
}
if ((Sys_DoubleTime() - start) > blocktime)
break;
}
return count;
}
/*
* ====================
* NET_Init
* ====================
*/
void
NET_Init(void)
{
int i;
int controlSocket;
qsocket_t *s;
if (COM_CheckParm("-playback")) {
net_numdrivers = 1;
net_drivers[0].Init = VCR_Init;
}
if (COM_CheckParm("-record"))
recording = true;
i = COM_CheckParm("-port");
if (!i)
i = COM_CheckParm("-udpport");
if (!i)
i = COM_CheckParm("-ipxport");
if (i) {
if (i < com_argc - 1)
DEFAULTnet_hostport = Q_atoi(com_argv[i + 1]);
else
Sys_Error("%s: you must specify a number after -port", __func__);
}
net_hostport = DEFAULTnet_hostport;
if (COM_CheckParm("-listen") || cls.state == ca_dedicated)
listening = true;
net_numsockets = svs.maxclientslimit;
if (cls.state != ca_dedicated)
net_numsockets++;
SetNetTime();
for (i = 0; i < net_numsockets; i++) {
s = (qsocket_t *)Hunk_AllocName(sizeof(qsocket_t), "qsocket");
s->next = net_freeSockets;
net_freeSockets = s;
s->disconnected = true;
}
/* allocate space for network message buffer */
SZ_Alloc(&net_message, NET_MAXMESSAGE);
Cvar_RegisterVariable(&net_messagetimeout);
Cvar_RegisterVariable(&hostname);
Cvar_RegisterVariable(&config_com_port);
Cvar_RegisterVariable(&config_com_irq);
Cvar_RegisterVariable(&config_com_baud);
Cvar_RegisterVariable(&config_com_modem);
Cvar_RegisterVariable(&config_modem_dialtype);
Cvar_RegisterVariable(&config_modem_clear);
Cvar_RegisterVariable(&config_modem_init);
Cvar_RegisterVariable(&config_modem_hangup);
#ifdef IDGODS
Cvar_RegisterVariable(&idgods);
#endif
Cmd_AddCommand("slist", NET_Slist_f);
Cmd_AddCommand("listen", NET_Listen_f);
Cmd_AddCommand("maxplayers", MaxPlayers_f);
Cmd_AddCommand("port", NET_Port_f);
/* initialize all the drivers */
for (i = 0; i < net_numdrivers; i++) {
net_driver = &net_drivers[i];
controlSocket = net_driver->Init();
if (controlSocket == -1)
continue;
net_driver->initialized = true;
net_driver->controlSock = controlSocket;
if (listening)
net_driver->Listen(true);
}
if (*my_ipx_address)
Con_DPrintf("IPX address %s\n", my_ipx_address);
if (*my_tcpip_address)
Con_DPrintf("TCP/IP address %s\n", my_tcpip_address);
}
/*
* ====================
* NET_Shutdown
* ====================
*/
void
NET_Shutdown(void)
{
int i;
qsocket_t *sock;
SetNetTime();
for (sock = net_activeSockets; sock; sock = sock->next)
NET_Close(sock);
/*
* shutdown the drivers
*/
for (i = 0; i < net_numdrivers; i++) {
net_driver = &net_drivers[i];
if (net_driver->initialized == true) {
net_driver->Shutdown();
net_driver->initialized = false;
}
}
if (vcrFile != -1) {
Con_Printf("Closing vcrfile.\n");
Sys_FileClose(vcrFile);
}
}
static PollProcedure *pollProcedureList = NULL;
void
NET_Poll(void)
{
PollProcedure *pp;
qboolean useModem;
if (!configRestored) {
if (serialAvailable) {
if (config_com_modem.value == 1.0)
useModem = true;
else
useModem = false;
SetComPortConfig(0, (int)config_com_port.value,
(int)config_com_irq.value,
(int)config_com_baud.value, useModem);
SetModemConfig(0, config_modem_dialtype.string,
config_modem_clear.string,
config_modem_init.string,
config_modem_hangup.string);
}
configRestored = true;
}
SetNetTime();
/*
* FIXME - A procedure could schedule itself to the head of the list, but
* wouldn't be executed until next frame/tic; problem?
*/
for (pp = pollProcedureList; pp; pp = pp->next) {
if (pp->nextTime > net_time)
break;
pollProcedureList = pp->next;
pp->procedure(pp->arg);
}
}
void
SchedulePollProcedure(PollProcedure *proc, double timeOffset)
{
PollProcedure *pp, *prev;
proc->nextTime = Sys_DoubleTime() + timeOffset;
for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next) {
if (pp->nextTime >= proc->nextTime)
break;
prev = pp;
}
if (prev == NULL) {
proc->next = pollProcedureList;
pollProcedureList = proc;
return;
}
proc->next = pp;
prev->next = proc;
}
#ifdef IDGODS
#define IDNET 0xc0f62800
qboolean
IsID(struct qsockaddr *addr)
{
if (idgods.value == 0.0)
return false;
if (addr->sa_family != 2)
return false;
if ((BigLong(*(int *)&addr->sa_data[2]) & 0xffffff00) == IDNET)
return true;
return false;
}
#endif

44
NQ/net_none.c Normal file
View File

@ -0,0 +1,44 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include "net_loop.h"
net_driver_t net_drivers[MAX_NET_DRIVERS] = {
{
"Loopback",
false,
Loop_Init,
Loop_Listen,
Loop_SearchForHosts,
Loop_Connect,
Loop_CheckNewConnections,
Loop_GetMessage,
Loop_SendMessage,
Loop_SendUnreliableMessage,
Loop_CanSendMessage,
Loop_CanSendUnreliableMessage,
Loop_Close,
Loop_Shutdown}
};
int net_numdrivers = 1;
net_landriver_t net_landrivers[MAX_NET_DRIVERS];
int net_numlandrivers = 0;

41
NQ/net_ser.h Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef NET_SER_H
#define NET_SER_H
#include "common.h"
#include "net.h"
#include "qtypes.h"
int Serial_Init(void);
void Serial_Listen(qboolean state);
void Serial_SearchForHosts(qboolean xmit);
qsocket_t *Serial_Connect(char *host);
qsocket_t *Serial_CheckNewConnections(void);
int Serial_GetMessage(qsocket_t *sock);
int Serial_SendMessage(qsocket_t *sock, sizebuf_t *data);
int Serial_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data);
qboolean Serial_CanSendMessage(qsocket_t *sock);
qboolean Serial_CanSendUnreliableMessage(qsocket_t *sock);
void Serial_Close(qsocket_t *sock);
void Serial_Shutdown(void);
#endif /* NET_SER_H */

409
NQ/net_udp.c Normal file
View File

@ -0,0 +1,409 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include "common.h"
#include "console.h"
#include "net.h"
#include "net_udp.h"
#include "quakedef.h"
#include "sys.h"
/* socket for fielding new connections */
static int net_acceptsocket = -1;
static int net_controlsocket;
static int net_broadcastsocket = 0;
static struct qsockaddr broadcastaddr;
static unsigned long myAddr;
int
UDP_Init(void)
{
struct hostent *local;
char buff[MAXHOSTNAMELEN];
struct qsockaddr addr;
char *colon;
if (COM_CheckParm("-noudp"))
return -1;
/* determine my name & address */
gethostname(buff, MAXHOSTNAMELEN);
local = gethostbyname(buff);
myAddr = *(int *)local->h_addr_list[0];
/* if the quake hostname isn't set, set it to the machine name */
if (strcmp(hostname.string, "UNNAMED") == 0) {
buff[15] = 0; /* FIXME - 15? */
Cvar_Set("hostname", buff);
}
if ((net_controlsocket = UDP_OpenSocket(0)) == -1)
Sys_Error("%s: Unable to open control socket", __func__);
((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET;
((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr =
INADDR_BROADCAST;
((struct sockaddr_in *)&broadcastaddr)->sin_port = htons(net_hostport);
UDP_GetSocketAddr(net_controlsocket, &addr);
strcpy(my_tcpip_address, UDP_AddrToString(&addr));
colon = strrchr(my_tcpip_address, ':');
if (colon)
*colon = 0;
Con_Printf("UDP Initialized\n");
tcpipAvailable = true;
return net_controlsocket;
}
void
UDP_Shutdown(void)
{
UDP_Listen(false);
UDP_CloseSocket(net_controlsocket);
}
void
UDP_Listen(qboolean state)
{
/* enable listening */
if (state) {
if (net_acceptsocket != -1)
return;
if ((net_acceptsocket = UDP_OpenSocket(net_hostport)) == -1)
Sys_Error("%s: Unable to open accept socket", __func__);
return;
}
/* disable listening */
if (net_acceptsocket == -1)
return;
UDP_CloseSocket(net_acceptsocket);
net_acceptsocket = -1;
}
int
UDP_OpenSocket(int port)
{
int newsocket;
struct sockaddr_in address;
qboolean _true = true;
if ((newsocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
return -1;
if (ioctl(newsocket, FIONBIO, (char *)&_true) == -1)
goto ErrorReturn;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(newsocket, (void *)&address, sizeof(address)) == -1)
goto ErrorReturn;
return newsocket;
ErrorReturn:
close(newsocket);
return -1;
}
int
UDP_CloseSocket(int socket)
{
if (socket == net_broadcastsocket)
net_broadcastsocket = 0;
return close(socket);
}
/*
* ============
* PartialIPAddress
*
* this lets you type only as much of the net address as required,
* using the local network components to fill in the rest
* ============
*/
static int
PartialIPAddress(char *in, struct qsockaddr *hostaddr)
{
char buff[256];
char *b;
int addr;
int num;
int mask;
int run;
int port;
buff[0] = '.';
b = buff;
strcpy(buff + 1, in);
if (buff[1] == '.')
b++;
addr = 0;
mask = -1;
while (*b == '.') {
b++;
num = 0;
run = 0;
while (!(*b < '0' || *b > '9')) {
num = num * 10 + *b++ - '0';
if (++run > 3)
return -1;
}
if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0)
return -1;
if (num < 0 || num > 255)
return -1;
mask <<= 8;
addr = (addr << 8) + num;
}
if (*b++ == ':')
port = Q_atoi(b);
else
port = net_hostport;
hostaddr->sa_family = AF_INET;
((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port);
((struct sockaddr_in *)hostaddr)->sin_addr.s_addr =
(myAddr & htonl(mask)) | htonl(addr);
return 0;
}
int
UDP_Connect(int socket, struct qsockaddr *addr)
{
return 0;
}
int
UDP_CheckNewConnections(void)
{
unsigned long available;
if (net_acceptsocket == -1)
return -1;
if (ioctl(net_acceptsocket, FIONREAD, &available) == -1)
Sys_Error("%s: ioctlsocket (FIONREAD) failed", __func__);
if (available)
return net_acceptsocket;
return -1;
}
int
UDP_Read(int socket, byte *buf, int len, struct qsockaddr *addr)
{
int addrlen = sizeof(struct qsockaddr);
int ret;
ret = recvfrom(socket, buf, len, 0, (struct sockaddr *)addr, &addrlen);
if (ret == -1 && (errno == EWOULDBLOCK || errno == ECONNREFUSED))
return 0;
return ret;
}
int
UDP_MakeSocketBroadcastCapable(int socket)
{
int i = 1;
/* make this socket broadcast capable */
if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i))
< 0)
return -1;
net_broadcastsocket = socket;
return 0;
}
int
UDP_Broadcast(int socket, byte *buf, int len)
{
int ret;
if (socket != net_broadcastsocket) {
if (net_broadcastsocket != 0)
Sys_Error("Attempted to use multiple broadcasts sockets");
ret = UDP_MakeSocketBroadcastCapable(socket);
if (ret == -1) {
Con_Printf("Unable to make socket broadcast capable\n");
return ret;
}
}
return UDP_Write(socket, buf, len, &broadcastaddr);
}
int
UDP_Write(int socket, byte *buf, int len, struct qsockaddr *addr)
{
int ret;
ret =
sendto(socket, buf, len, 0, (struct sockaddr *)addr,
sizeof(struct qsockaddr));
if (ret == -1 && errno == EWOULDBLOCK)
return 0;
return ret;
}
char *
UDP_AddrToString(struct qsockaddr *addr)
{
static char buffer[22];
int haddr;
haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff,
(haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff,
ntohs(((struct sockaddr_in *)addr)->sin_port));
return buffer;
}
int
UDP_StringToAddr(char *string, struct qsockaddr *addr)
{
int ha1, ha2, ha3, ha4, hp;
int ipaddr;
sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp);
ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4;
addr->sa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr);
((struct sockaddr_in *)addr)->sin_port = htons(hp);
return 0;
}
int
UDP_GetSocketAddr(int socket, struct qsockaddr *addr)
{
int addrlen = sizeof(struct qsockaddr);
unsigned int a;
memset(addr, 0, sizeof(struct qsockaddr));
getsockname(socket, (struct sockaddr *)addr, &addrlen);
a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
if (a == 0 || a == inet_addr("127.0.0.1"))
((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr;
return 0;
}
int
UDP_GetNameFromAddr(struct qsockaddr *addr, char *name)
{
struct hostent *hostentry;
hostentry =
gethostbyaddr((char *)&((struct sockaddr_in *)addr)->sin_addr,
sizeof(struct in_addr), AF_INET);
if (hostentry) {
strncpy(name, (char *)hostentry->h_name, NET_NAMELEN - 1);
return 0;
}
strcpy(name, UDP_AddrToString(addr));
return 0;
}
int
UDP_GetAddrFromName(char *name, struct qsockaddr *addr)
{
struct hostent *hostentry;
if (name[0] >= '0' && name[0] <= '9')
return PartialIPAddress(name, addr);
hostentry = gethostbyname(name);
if (!hostentry)
return -1;
addr->sa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_port = htons(net_hostport);
((struct sockaddr_in *)addr)->sin_addr.s_addr =
*(int *)hostentry->h_addr_list[0];
return 0;
}
int
UDP_AddrCompare(struct qsockaddr *addr1, struct qsockaddr *addr2)
{
if (addr1->sa_family != addr2->sa_family)
return -1;
if (((struct sockaddr_in *)addr1)->sin_addr.s_addr !=
((struct sockaddr_in *)addr2)->sin_addr.s_addr)
return -1;
if (((struct sockaddr_in *)addr1)->sin_port !=
((struct sockaddr_in *)addr2)->sin_port)
return 1;
return 0;
}
int
UDP_GetSocketPort(struct qsockaddr *addr)
{
return ntohs(((struct sockaddr_in *)addr)->sin_port);
}
int
UDP_SetSocketPort(struct qsockaddr *addr, int port)
{
((struct sockaddr_in *)addr)->sin_port = htons(port);
return 0;
}

47
NQ/net_udp.h Normal file
View File

@ -0,0 +1,47 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef NET_UDP_H
#define NET_UDP_H
#include "net.h"
// net_udp.h
int UDP_Init(void);
void UDP_Shutdown(void);
void UDP_Listen(qboolean state);
int UDP_OpenSocket(int port);
int UDP_CloseSocket(int socket);
int UDP_Connect(int socket, struct qsockaddr *addr);
int UDP_CheckNewConnections(void);
int UDP_Read(int socket, byte *buf, int len, struct qsockaddr *addr);
int UDP_Write(int socket, byte *buf, int len, struct qsockaddr *addr);
int UDP_Broadcast(int socket, byte *buf, int len);
char *UDP_AddrToString(struct qsockaddr *addr);
int UDP_StringToAddr(char *string, struct qsockaddr *addr);
int UDP_GetSocketAddr(int socket, struct qsockaddr *addr);
int UDP_GetNameFromAddr(struct qsockaddr *addr, char *name);
int UDP_GetAddrFromName(char *name, struct qsockaddr *addr);
int UDP_AddrCompare(struct qsockaddr *addr1, struct qsockaddr *addr2);
int UDP_GetSocketPort(struct qsockaddr *addr);
int UDP_SetSocketPort(struct qsockaddr *addr, int port);
#endif /* NET_UDP_H */

181
NQ/net_vcr.c Normal file
View File

@ -0,0 +1,181 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// net_vcr.c
#include "quakedef.h"
#include "net_vcr.h"
#include "sys.h"
#include "server.h"
int vcrFile = -1;
/*
* This is the playback portion of the VCR. It reads the file produced by the
* recorder and plays it back to the host. The recording contains everything
* necessary (events, timestamps, and data) to duplicate the game from the
* viewpoint of everything above the network layer.
*/
static struct {
double time;
int op;
long session;
} next;
int
VCR_Init(void)
{
net_drivers[0].Init = VCR_Init;
net_drivers[0].SearchForHosts = VCR_SearchForHosts;
net_drivers[0].Connect = VCR_Connect;
net_drivers[0].CheckNewConnections = VCR_CheckNewConnections;
net_drivers[0].QGetMessage = VCR_GetMessage;
net_drivers[0].QSendMessage = VCR_SendMessage;
net_drivers[0].CanSendMessage = VCR_CanSendMessage;
net_drivers[0].Close = VCR_Close;
net_drivers[0].Shutdown = VCR_Shutdown;
Sys_FileRead(vcrFile, &next, sizeof(next));
return 0;
}
void
VCR_ReadNext(void)
{
if (Sys_FileRead(vcrFile, &next, sizeof(next)) == 0) {
next.op = 255;
Sys_Error("=== END OF PLAYBACK===");
}
if (next.op < 1 || next.op > VCR_MAX_MESSAGE)
Sys_Error("%s: bad op", __func__);
}
void
VCR_Listen(qboolean state)
{
}
void
VCR_Shutdown(void)
{
}
int
VCR_GetMessage(qsocket_t *sock)
{
int ret;
if (host_time != next.time || next.op != VCR_OP_GETMESSAGE
|| next.session != *(long *)(&sock->driverdata))
Sys_Error("VCR missmatch");
Sys_FileRead(vcrFile, &ret, sizeof(int));
if (ret != 1) {
VCR_ReadNext();
return ret;
}
Sys_FileRead(vcrFile, &net_message.cursize, sizeof(int));
Sys_FileRead(vcrFile, net_message.data, net_message.cursize);
VCR_ReadNext();
return 1;
}
int
VCR_SendMessage(qsocket_t *sock, sizebuf_t *data)
{
int ret;
if (host_time != next.time || next.op != VCR_OP_SENDMESSAGE
|| next.session != *(long *)(&sock->driverdata))
Sys_Error("VCR missmatch");
Sys_FileRead(vcrFile, &ret, sizeof(int));
VCR_ReadNext();
return ret;
}
qboolean
VCR_CanSendMessage(qsocket_t *sock)
{
qboolean ret;
if (host_time != next.time || next.op != VCR_OP_CANSENDMESSAGE
|| next.session != *(long *)(&sock->driverdata))
Sys_Error("VCR missmatch");
Sys_FileRead(vcrFile, &ret, sizeof(int));
VCR_ReadNext();
return ret;
}
void
VCR_Close(qsocket_t *sock)
{
}
void
VCR_SearchForHosts(qboolean xmit)
{
}
qsocket_t *
VCR_Connect(char *host)
{
return NULL;
}
qsocket_t *
VCR_CheckNewConnections(void)
{
qsocket_t *sock;
if (host_time != next.time || next.op != VCR_OP_CONNECT)
Sys_Error("VCR missmatch");
if (!next.session) {
VCR_ReadNext();
return NULL;
}
sock = NET_NewQSocket();
*(long *)(&sock->driverdata) = next.session;
Sys_FileRead(vcrFile, sock->address, NET_NAMELEN);
VCR_ReadNext();
return sock;
}

47
NQ/net_vcr.h Normal file
View File

@ -0,0 +1,47 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef NET_VCR_H
#define NET_VCR_H
#include "net.h"
// net_vcr.h
#define VCR_OP_CONNECT 1
#define VCR_OP_GETMESSAGE 2
#define VCR_OP_SENDMESSAGE 3
#define VCR_OP_CANSENDMESSAGE 4
#define VCR_MAX_MESSAGE 4
extern int vcrFile;
int VCR_Init(void);
void VCR_Listen(qboolean state);
void VCR_SearchForHosts(qboolean xmit);
qsocket_t *VCR_Connect(char *host);
qsocket_t *VCR_CheckNewConnections(void);
int VCR_GetMessage(qsocket_t *sock);
int VCR_SendMessage(qsocket_t *sock, sizebuf_t *data);
qboolean VCR_CanSendMessage(qsocket_t *sock);
void VCR_Close(qsocket_t *sock);
void VCR_Shutdown(void);
#endif /* NET_VCR_H */

115
NQ/net_win.c Normal file
View File

@ -0,0 +1,115 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "net.h"
#include "net_dgrm.h"
#include "net_loop.h"
#include "net_ser.h"
#include "net_wins.h"
#include "net_wipx.h"
#include "quakedef.h"
net_driver_t net_drivers[MAX_NET_DRIVERS] = {
{
.name = "Loopback",
.initialized = false,
.Init = Loop_Init,
.Listen = Loop_Listen,
.SearchForHosts = Loop_SearchForHosts,
.Connect = Loop_Connect,
.CheckNewConnections = Loop_CheckNewConnections,
.QGetMessage = Loop_GetMessage,
.QSendMessage = Loop_SendMessage,
.SendUnreliableMessage = Loop_SendUnreliableMessage,
.CanSendMessage = Loop_CanSendMessage,
.CanSendUnreliableMessage = Loop_CanSendUnreliableMessage,
.Close = Loop_Close,
.Shutdown = Loop_Shutdown
}, {
.name = "Datagram",
.initialized = false,
.Init = Datagram_Init,
.Listen = Datagram_Listen,
.SearchForHosts = Datagram_SearchForHosts,
.Connect = Datagram_Connect,
.CheckNewConnections = Datagram_CheckNewConnections,
.QGetMessage = Datagram_GetMessage,
.QSendMessage = Datagram_SendMessage,
.SendUnreliableMessage = Datagram_SendUnreliableMessage,
.CanSendMessage = Datagram_CanSendMessage,
.CanSendUnreliableMessage = Datagram_CanSendUnreliableMessage,
.Close = Datagram_Close,
.Shutdown = Datagram_Shutdown
}
};
int net_numdrivers = 2;
net_landriver_t net_landrivers[MAX_NET_DRIVERS] = {
{
.name = "Winsock TCPIP",
.initialized = false,
.controlSock = 0,
.Init = WINS_Init,
.Shutdown = WINS_Shutdown,
.Listen = WINS_Listen,
.OpenSocket = WINS_OpenSocket,
.CloseSocket = WINS_CloseSocket,
.Connect = WINS_Connect,
.CheckNewConnections = WINS_CheckNewConnections,
.Read = WINS_Read,
.Write = WINS_Write,
.Broadcast = WINS_Broadcast,
.AddrToString = WINS_AddrToString,
.StringToAddr = WINS_StringToAddr,
.GetSocketAddr = WINS_GetSocketAddr,
.GetNameFromAddr = WINS_GetNameFromAddr,
.GetAddrFromName = WINS_GetAddrFromName,
.AddrCompare = WINS_AddrCompare,
.GetSocketPort = WINS_GetSocketPort,
.SetSocketPort = WINS_SetSocketPort
}, {
.name = "Winsock IPX",
.initialized = false,
.controlSock = 0,
.Init = WIPX_Init,
.Shutdown = WIPX_Shutdown,
.Listen = WIPX_Listen,
.OpenSocket = WIPX_OpenSocket,
.CloseSocket = WIPX_CloseSocket,
.Connect = WIPX_Connect,
.CheckNewConnections = WIPX_CheckNewConnections,
.Read = WIPX_Read,
.Write = WIPX_Write,
.Broadcast = WIPX_Broadcast,
.AddrToString = WIPX_AddrToString,
.StringToAddr = WIPX_StringToAddr,
.GetSocketAddr = WIPX_GetSocketAddr,
.GetNameFromAddr = WIPX_GetNameFromAddr,
.GetAddrFromName = WIPX_GetAddrFromName,
.AddrCompare = WIPX_AddrCompare,
.GetSocketPort = WIPX_GetSocketPort,
.SetSocketPort = WIPX_SetSocketPort
}
};
int net_numlandrivers = 2;

576
NQ/net_wins.c Normal file
View File

@ -0,0 +1,576 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "console.h"
#include "net.h"
#include "net_wins.h"
#include "quakedef.h"
#include "sys.h"
#include "winquake.h"
/* socket for fielding new connections */
static int net_acceptsocket = -1;
static int net_controlsocket;
static int net_broadcastsocket = 0;
static struct qsockaddr broadcastaddr;
static unsigned long myAddr;
qboolean winsock_lib_initialized;
int (PASCAL FAR *pWSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData);
int (PASCAL FAR *pWSACleanup) (void);
int (PASCAL FAR *pWSAGetLastError) (void);
SOCKET (PASCAL FAR *psocket) (int af, int type, int protocol);
int (PASCAL FAR *pioctlsocket) (SOCKET s, long cmd, u_long FAR *argp);
int (PASCAL FAR *psetsockopt) (SOCKET s, int level, int optname,
const char FAR *optval, int optlen);
int (PASCAL FAR *precvfrom) (SOCKET s, char FAR *buf, int len,
int flags, struct sockaddr FAR *from,
int FAR *fromlen);
int (PASCAL FAR *psendto) (SOCKET s, const char FAR *buf, int len,
int flags, const struct sockaddr FAR *to,
int tolen);
int (PASCAL FAR *pclosesocket) (SOCKET s);
int (PASCAL FAR *pgethostname) (char FAR *name, int namelen);
struct hostent FAR *(PASCAL FAR *pgethostbyname)(const char FAR *name);
struct hostent FAR *(PASCAL FAR *pgethostbyaddr)(const char FAR *addr,
int len, int type);
int (PASCAL FAR *pgetsockname) (SOCKET s, struct sockaddr FAR *name,
int FAR *namelen);
int winsock_initialized = 0;
WSADATA winsockdata;
static double blocktime;
BOOL PASCAL FAR
BlockingHook(void)
{
MSG msg;
BOOL ret;
if ((Sys_DoubleTime() - blocktime) > 2.0) {
WSACancelBlockingCall();
return FALSE;
}
/* get the next message, if any */
ret = (BOOL)PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
/* if we got one, process it */
if (ret) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* TRUE if we got a message */
return ret;
}
void
WINS_GetLocalAddress()
{
struct hostent *local = NULL;
char buff[MAXHOSTNAMELEN];
unsigned long addr;
if (myAddr != INADDR_ANY)
return;
if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR)
return;
blocktime = Sys_DoubleTime();
WSASetBlockingHook(BlockingHook);
local = pgethostbyname(buff);
WSAUnhookBlockingHook();
if (local == NULL)
return;
myAddr = *(int *)local->h_addr_list[0];
addr = ntohl(myAddr);
sprintf(my_tcpip_address, "%ld.%ld.%ld.%ld", (addr >> 24) & 0xff,
(addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
}
int
WINS_Init(void)
{
int i;
char buff[MAXHOSTNAMELEN];
char *p;
int r;
WORD wVersionRequested;
HINSTANCE hInst;
/*
* initialize the Winsock function vectors (we do this instead of
* statically linking so we can run on Win 3.1, where there isn't
* necessarily Winsock)
*/
hInst = LoadLibrary("wsock32.dll");
if (hInst == NULL) {
Con_SafePrintf("Failed to load winsock.dll\n");
winsock_lib_initialized = false;
return -1;
}
winsock_lib_initialized = true;
pWSAStartup = (void *)GetProcAddress(hInst, "WSAStartup");
pWSACleanup = (void *)GetProcAddress(hInst, "WSACleanup");
pWSAGetLastError = (void *)GetProcAddress(hInst, "WSAGetLastError");
psocket = (void *)GetProcAddress(hInst, "socket");
pioctlsocket = (void *)GetProcAddress(hInst, "ioctlsocket");
psetsockopt = (void *)GetProcAddress(hInst, "setsockopt");
precvfrom = (void *)GetProcAddress(hInst, "recvfrom");
psendto = (void *)GetProcAddress(hInst, "sendto");
pclosesocket = (void *)GetProcAddress(hInst, "closesocket");
pgethostname = (void *)GetProcAddress(hInst, "gethostname");
pgethostbyname = (void *)GetProcAddress(hInst, "gethostbyname");
pgethostbyaddr = (void *)GetProcAddress(hInst, "gethostbyaddr");
pgetsockname = (void *)GetProcAddress(hInst, "getsockname");
if (!pWSAStartup || !pWSACleanup || !pWSAGetLastError ||
!psocket || !pioctlsocket || !psetsockopt ||
!precvfrom || !psendto || !pclosesocket ||
!pgethostname || !pgethostbyname || !pgethostbyaddr ||
!pgetsockname) {
Con_SafePrintf("Couldn't GetProcAddress from winsock.dll\n");
return -1;
}
if (COM_CheckParm("-noudp"))
return -1;
if (winsock_initialized == 0) {
wVersionRequested = MAKEWORD(1, 1);
r = pWSAStartup(MAKEWORD(1, 1), &winsockdata);
if (r) {
Con_SafePrintf("Winsock initialization failed.\n");
return -1;
}
}
winsock_initialized++;
/* determine my name */
if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR) {
Con_DPrintf("Winsock TCP/IP Initialization failed.\n");
if (--winsock_initialized == 0)
pWSACleanup();
return -1;
}
/* if the quake hostname isn't set, set it to the machine name */
if (strcmp(hostname.string, "UNNAMED") == 0) {
/* see if it's a text IP address (well, close enough) */
for (p = buff; *p; p++)
if ((*p < '0' || *p > '9') && *p != '.')
break;
/* if it is a real name, strip off the domain; we only want the host */
if (*p) {
for (i = 0; i < 15; i++)
if (buff[i] == '.')
break;
buff[i] = 0;
}
Cvar_Set("hostname", buff);
}
i = COM_CheckParm("-ip");
if (i) {
if (i < com_argc - 1) {
myAddr = inet_addr(com_argv[i + 1]);
if (myAddr == INADDR_NONE)
Sys_Error("%s is not a valid IP address", com_argv[i + 1]);
strcpy(my_tcpip_address, com_argv[i + 1]);
} else {
Sys_Error("%s: you must specify an IP address after -ip",
__func__);
}
} else {
myAddr = INADDR_ANY;
strcpy(my_tcpip_address, "INADDR_ANY");
}
if ((net_controlsocket = WINS_OpenSocket(0)) == -1) {
Con_Printf("WINS_Init: Unable to open control socket\n");
if (--winsock_initialized == 0)
pWSACleanup();
return -1;
}
((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET;
((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr =
INADDR_BROADCAST;
((struct sockaddr_in *)&broadcastaddr)->sin_port =
htons((unsigned short)net_hostport);
Con_Printf("Winsock TCP/IP Initialized\n");
tcpipAvailable = true;
return net_controlsocket;
}
void
WINS_Shutdown(void)
{
WINS_Listen(false);
WINS_CloseSocket(net_controlsocket);
if (--winsock_initialized == 0)
pWSACleanup();
}
void
WINS_Listen(qboolean state)
{
/* enable listening */
if (state) {
if (net_acceptsocket != -1)
return;
WINS_GetLocalAddress();
if ((net_acceptsocket = WINS_OpenSocket(net_hostport)) == -1)
Sys_Error("%s: Unable to open accept socket", __func__);
return;
}
/* disable listening */
if (net_acceptsocket == -1)
return;
WINS_CloseSocket(net_acceptsocket);
net_acceptsocket = -1;
}
int
WINS_OpenSocket(int port)
{
int newsocket;
struct sockaddr_in address;
u_long _true = 1;
if ((newsocket = psocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
return -1;
if (pioctlsocket(newsocket, FIONBIO, &_true) == -1)
goto ErrorReturn;
address.sin_family = AF_INET;
address.sin_addr.s_addr = myAddr;
address.sin_port = htons((unsigned short)port);
if (bind(newsocket, (void *)&address, sizeof(address)) == 0)
return newsocket;
Sys_Error("Unable to bind to %s",
WINS_AddrToString((struct qsockaddr *)&address));
ErrorReturn:
pclosesocket(newsocket);
return -1;
}
int
WINS_CloseSocket(int socket)
{
if (socket == net_broadcastsocket)
net_broadcastsocket = 0;
return pclosesocket(socket);
}
/*
* ============
* PartialIPAddress
*
* this lets you type only as much of the net address as required, using the
* local network components to fill in the rest
* ============
*/
static int
PartialIPAddress(char *in, struct qsockaddr *hostaddr)
{
char buff[256];
char *b;
int addr;
int num;
int mask;
int run;
int port;
buff[0] = '.';
b = buff;
strcpy(buff + 1, in);
if (buff[1] == '.')
b++;
addr = 0;
mask = -1;
while (*b == '.') {
b++;
num = 0;
run = 0;
while (!(*b < '0' || *b > '9')) {
num = num * 10 + *b++ - '0';
if (++run > 3)
return -1;
}
if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0)
return -1;
if (num < 0 || num > 255)
return -1;
mask <<= 8;
addr = (addr << 8) + num;
}
if (*b++ == ':')
port = Q_atoi(b);
else
port = net_hostport;
hostaddr->sa_family = AF_INET;
((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port);
((struct sockaddr_in *)hostaddr)->sin_addr.s_addr =
(myAddr & htonl(mask)) | htonl(addr);
return 0;
}
int
WINS_Connect(int socket, struct qsockaddr *addr)
{
return 0;
}
int
WINS_CheckNewConnections(void)
{
char buf[4096];
if (net_acceptsocket == -1)
return -1;
if (precvfrom(net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL)
> 0) {
return net_acceptsocket;
}
return -1;
}
int
WINS_Read(int socket, byte *buf, int len, struct qsockaddr *addr)
{
int addrlen = sizeof(struct qsockaddr);
int ret;
ret = precvfrom(socket, buf, len, 0, (struct sockaddr *)addr, &addrlen);
if (ret == -1) {
int err = pWSAGetLastError();
if (err == WSAEWOULDBLOCK || err == WSAECONNREFUSED)
return 0;
}
return ret;
}
int
WINS_MakeSocketBroadcastCapable(int socket)
{
int i = 1;
/* make this socket broadcast capable */
if (psetsockopt
(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0)
return -1;
net_broadcastsocket = socket;
return 0;
}
int
WINS_Broadcast(int socket, byte *buf, int len)
{
int ret;
if (socket != net_broadcastsocket) {
if (net_broadcastsocket != 0)
Sys_Error("Attempted to use multiple broadcasts sockets");
WINS_GetLocalAddress();
ret = WINS_MakeSocketBroadcastCapable(socket);
if (ret == -1) {
Con_Printf("Unable to make socket broadcast capable\n");
return ret;
}
}
return WINS_Write(socket, buf, len, &broadcastaddr);
}
int
WINS_Write(int socket, byte *buf, int len, struct qsockaddr *addr)
{
int ret;
ret =
psendto(socket, buf, len, 0, (struct sockaddr *)addr,
sizeof(struct qsockaddr));
if (ret == -1)
if (pWSAGetLastError() == WSAEWOULDBLOCK)
return 0;
return ret;
}
char *
WINS_AddrToString(struct qsockaddr *addr)
{
static char buffer[22];
int haddr;
haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff,
(haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff,
ntohs(((struct sockaddr_in *)addr)->sin_port));
return buffer;
}
int
WINS_StringToAddr(char *string, struct qsockaddr *addr)
{
int ha1, ha2, ha3, ha4, hp;
int ipaddr;
sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp);
ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4;
addr->sa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr);
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp);
return 0;
}
int
WINS_GetSocketAddr(int socket, struct qsockaddr *addr)
{
int addrlen = sizeof(struct qsockaddr);
unsigned int a;
memset(addr, 0, sizeof(struct qsockaddr));
pgetsockname(socket, (struct sockaddr *)addr, &addrlen);
a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
if (a == 0 || a == inet_addr("127.0.0.1"))
((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr;
return 0;
}
int
WINS_GetNameFromAddr(struct qsockaddr *addr, char *name)
{
struct hostent *hostentry;
hostentry =
pgethostbyaddr((char *)&((struct sockaddr_in *)addr)->sin_addr,
sizeof(struct in_addr), AF_INET);
if (hostentry) {
strncpy(name, (char *)hostentry->h_name, NET_NAMELEN - 1);
return 0;
}
strcpy(name, WINS_AddrToString(addr));
return 0;
}
int
WINS_GetAddrFromName(char *name, struct qsockaddr *addr)
{
struct hostent *hostentry;
if (name[0] >= '0' && name[0] <= '9')
return PartialIPAddress(name, addr);
hostentry = pgethostbyname(name);
if (!hostentry)
return -1;
addr->sa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_port =
htons((unsigned short)net_hostport);
((struct sockaddr_in *)addr)->sin_addr.s_addr =
*(int *)hostentry->h_addr_list[0];
return 0;
}
int
WINS_AddrCompare(struct qsockaddr *addr1, struct qsockaddr *addr2)
{
if (addr1->sa_family != addr2->sa_family)
return -1;
if (((struct sockaddr_in *)addr1)->sin_addr.s_addr !=
((struct sockaddr_in *)addr2)->sin_addr.s_addr)
return -1;
if (((struct sockaddr_in *)addr1)->sin_port !=
((struct sockaddr_in *)addr2)->sin_port)
return 1;
return 0;
}
int
WINS_GetSocketPort(struct qsockaddr *addr)
{
return ntohs(((struct sockaddr_in *)addr)->sin_port);
}
int
WINS_SetSocketPort(struct qsockaddr *addr, int port)
{
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port);
return 0;
}

43
NQ/net_wins.h Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef NET_WINS_H
#define NET_WINS_H
int WINS_Init(void);
void WINS_Shutdown(void);
void WINS_Listen(qboolean state);
int WINS_OpenSocket(int port);
int WINS_CloseSocket(int socket);
int WINS_Connect(int socket, struct qsockaddr *addr);
int WINS_CheckNewConnections(void);
int WINS_Read(int socket, byte *buf, int len, struct qsockaddr *addr);
int WINS_Write(int socket, byte *buf, int len, struct qsockaddr *addr);
int WINS_Broadcast(int socket, byte *buf, int len);
char *WINS_AddrToString(struct qsockaddr *addr);
int WINS_StringToAddr(char *string, struct qsockaddr *addr);
int WINS_GetSocketAddr(int socket, struct qsockaddr *addr);
int WINS_GetNameFromAddr(struct qsockaddr *addr, char *name);
int WINS_GetAddrFromName(char *name, struct qsockaddr *addr);
int WINS_AddrCompare(struct qsockaddr *addr1, struct qsockaddr *addr2);
int WINS_GetSocketPort(struct qsockaddr *addr);
int WINS_SetSocketPort(struct qsockaddr *addr, int port);
#endif /* NET_WINS_H */

467
NQ/net_wipx.c Normal file
View File

@ -0,0 +1,467 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// net_wipx.c
#include "console.h"
#include "net.h"
#include "quakedef.h"
#include "sys.h"
#include "winquake.h"
/* Needs to be after winquake.h?? - FIXME (remove it) */
#include <wsipx.h>
#include "net_wipx.h"
/*
// FIXME - End fix for missing wsipx.h in MinGW?
struct sockaddr_ipx {
short sa_family;
char sa_netnum[4];
char sa_nodenum[6];
unsigned short sa_socket;
};
#define NSPROTO_IPX 1000
*/
#include "net_wipx.h"
static int net_acceptsocket = -1; // socket for fielding new connections
static int net_controlsocket;
static struct qsockaddr broadcastaddr;
#define IPXSOCKETS 18
static int ipxsocket[IPXSOCKETS];
static int sequence[IPXSOCKETS];
//=============================================================================
int
WIPX_Init(void)
{
int i;
char buff[MAXHOSTNAMELEN];
struct qsockaddr addr;
char *p;
int r;
WORD wVersionRequested;
if (COM_CheckParm("-noipx"))
return -1;
// make sure LoadLibrary has happened successfully
if (!winsock_lib_initialized)
return -1;
if (winsock_initialized == 0) {
wVersionRequested = MAKEWORD(1, 1);
r = pWSAStartup(MAKEWORD(1, 1), &winsockdata);
if (r) {
Con_Printf("Winsock initialization failed.\n");
return -1;
}
}
winsock_initialized++;
for (i = 0; i < IPXSOCKETS; i++)
ipxsocket[i] = 0;
// determine my name & address
if (pgethostname(buff, MAXHOSTNAMELEN) == 0) {
// if the quake hostname isn't set, set it to the machine name
if (strcmp(hostname.string, "UNNAMED") == 0) {
// see if it's a text IP address (well, close enough)
for (p = buff; *p; p++)
if ((*p < '0' || *p > '9') && *p != '.')
break;
// if it is a real name, strip off the domain; we only want the host
if (*p) {
for (i = 0; i < 15; i++)
if (buff[i] == '.')
break;
buff[i] = 0;
}
Cvar_Set("hostname", buff);
}
}
if ((net_controlsocket = WIPX_OpenSocket(0)) == -1) {
Con_Printf("WIPX_Init: Unable to open control socket\n");
if (--winsock_initialized == 0)
pWSACleanup();
return -1;
}
((struct sockaddr_ipx *)&broadcastaddr)->sa_family = AF_IPX;
memset(((struct sockaddr_ipx *)&broadcastaddr)->sa_netnum, 0, 4);
memset(((struct sockaddr_ipx *)&broadcastaddr)->sa_nodenum, 0xff, 6);
((struct sockaddr_ipx *)&broadcastaddr)->sa_socket =
htons((unsigned short)net_hostport);
WIPX_GetSocketAddr(net_controlsocket, &addr);
strcpy(my_ipx_address, WIPX_AddrToString(&addr));
p = strrchr(my_ipx_address, ':');
if (p)
*p = 0;
Con_Printf("Winsock IPX Initialized\n");
ipxAvailable = true;
return net_controlsocket;
}
//=============================================================================
void
WIPX_Shutdown(void)
{
WIPX_Listen(false);
WIPX_CloseSocket(net_controlsocket);
if (--winsock_initialized == 0)
pWSACleanup();
}
//=============================================================================
void
WIPX_Listen(qboolean state)
{
// enable listening
if (state) {
if (net_acceptsocket != -1)
return;
if ((net_acceptsocket = WIPX_OpenSocket(net_hostport)) == -1)
Sys_Error("%s: Unable to open accept socket", __func__);
return;
}
// disable listening
if (net_acceptsocket == -1)
return;
WIPX_CloseSocket(net_acceptsocket);
net_acceptsocket = -1;
}
//=============================================================================
int
WIPX_OpenSocket(int port)
{
int handle;
int newsocket;
struct sockaddr_ipx address;
u_long _true = 1;
for (handle = 0; handle < IPXSOCKETS; handle++)
if (ipxsocket[handle] == 0)
break;
if (handle == IPXSOCKETS)
return -1;
if ((newsocket =
psocket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET)
return -1;
if (pioctlsocket(newsocket, FIONBIO, &_true) == -1)
goto ErrorReturn;
if (psetsockopt
(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true,
sizeof(_true)) < 0)
goto ErrorReturn;
address.sa_family = AF_IPX;
memset(address.sa_netnum, 0, 4);
memset(address.sa_nodenum, 0, 6);;
address.sa_socket = htons((unsigned short)port);
if (bind(newsocket, (void *)&address, sizeof(address)) == 0) {
ipxsocket[handle] = newsocket;
sequence[handle] = 0;
return handle;
}
Sys_Error("Winsock IPX bind failed");
ErrorReturn:
pclosesocket(newsocket);
return -1;
}
//=============================================================================
int
WIPX_CloseSocket(int handle)
{
int socket = ipxsocket[handle];
int ret;
ret = pclosesocket(socket);
ipxsocket[handle] = 0;
return ret;
}
//=============================================================================
int
WIPX_Connect(int handle, struct qsockaddr *addr)
{
return 0;
}
//=============================================================================
int
WIPX_CheckNewConnections(void)
{
unsigned long available;
if (net_acceptsocket == -1)
return -1;
if (pioctlsocket(ipxsocket[net_acceptsocket], FIONREAD, &available) == -1)
Sys_Error("%s: ioctlsocket (FIONREAD) failed", __func__);
if (available)
return net_acceptsocket;
return -1;
}
//=============================================================================
static byte packetBuffer[NET_DATAGRAMSIZE + 4];
int
WIPX_Read(int handle, byte *buf, int len, struct qsockaddr *addr)
{
int addrlen = sizeof(struct qsockaddr);
int socket = ipxsocket[handle];
int ret;
ret =
precvfrom(socket, packetBuffer, len + 4, 0, (struct sockaddr *)addr,
&addrlen);
if (ret == -1) {
int err = pWSAGetLastError();
if (err == WSAEWOULDBLOCK || err == WSAECONNREFUSED)
return 0;
}
if (ret < 4)
return 0;
// remove sequence number, it's only needed for DOS IPX
ret -= 4;
memcpy(buf, packetBuffer + 4, ret);
return ret;
}
//=============================================================================
int
WIPX_Broadcast(int handle, byte *buf, int len)
{
return WIPX_Write(handle, buf, len, &broadcastaddr);
}
//=============================================================================
int
WIPX_Write(int handle, byte *buf, int len, struct qsockaddr *addr)
{
int socket = ipxsocket[handle];
int ret;
// build packet with sequence number
*(int *)(&packetBuffer[0]) = sequence[handle];
sequence[handle]++;
memcpy(&packetBuffer[4], buf, len);
len += 4;
ret =
psendto(socket, packetBuffer, len, 0, (struct sockaddr *)addr,
sizeof(struct qsockaddr));
if (ret == -1)
if (pWSAGetLastError() == WSAEWOULDBLOCK)
return 0;
return ret;
}
//=============================================================================
char *
WIPX_AddrToString(struct qsockaddr *addr)
{
static char buf[28];
sprintf(buf, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u",
((struct sockaddr_ipx *)addr)->sa_netnum[0] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[1] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[2] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[3] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[0] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[1] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[2] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[3] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[4] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[5] & 0xff,
ntohs(((struct sockaddr_ipx *)addr)->sa_socket)
);
return buf;
}
//=============================================================================
int
WIPX_StringToAddr(char *string, struct qsockaddr *addr)
{
int val;
char buf[3];
buf[2] = 0;
memset(addr, 0, sizeof(struct qsockaddr));
addr->sa_family = AF_IPX;
#define DO(src,dest) \
buf[0] = string[src]; \
buf[1] = string[src + 1]; \
if (sscanf (buf, "%x", &val) != 1) \
return -1; \
((struct sockaddr_ipx *)addr)->dest = val
DO(0, sa_netnum[0]);
DO(2, sa_netnum[1]);
DO(4, sa_netnum[2]);
DO(6, sa_netnum[3]);
DO(9, sa_nodenum[0]);
DO(11, sa_nodenum[1]);
DO(13, sa_nodenum[2]);
DO(15, sa_nodenum[3]);
DO(17, sa_nodenum[4]);
DO(19, sa_nodenum[5]);
#undef DO
sscanf(&string[22], "%u", &val);
((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)val);
return 0;
}
//=============================================================================
int
WIPX_GetSocketAddr(int handle, struct qsockaddr *addr)
{
int socket = ipxsocket[handle];
int addrlen = sizeof(struct qsockaddr);
memset(addr, 0, sizeof(struct qsockaddr));
if (pgetsockname(socket, (struct sockaddr *)addr, &addrlen) != 0) {
int err = pWSAGetLastError();
// FIXME - hack to get rid of unused warning
// - find out what should really be done
if (err)
err = 0;
}
return 0;
}
//=============================================================================
int
WIPX_GetNameFromAddr(struct qsockaddr *addr, char *name)
{
strcpy(name, WIPX_AddrToString(addr));
return 0;
}
//=============================================================================
int
WIPX_GetAddrFromName(char *name, struct qsockaddr *addr)
{
int n;
char buf[32];
n = strlen(name);
if (n == 12) {
sprintf(buf, "00000000:%s:%u", name, net_hostport);
return WIPX_StringToAddr(buf, addr);
}
if (n == 21) {
sprintf(buf, "%s:%u", name, net_hostport);
return WIPX_StringToAddr(buf, addr);
}
if (n > 21 && n <= 27)
return WIPX_StringToAddr(name, addr);
return -1;
}
//=============================================================================
int
WIPX_AddrCompare(struct qsockaddr *addr1, struct qsockaddr *addr2)
{
if (addr1->sa_family != addr2->sa_family)
return -1;
if (*((struct sockaddr_ipx *)addr1)->sa_netnum
&& *((struct sockaddr_ipx *)addr2)->sa_netnum)
if (memcmp
(((struct sockaddr_ipx *)addr1)->sa_netnum,
((struct sockaddr_ipx *)addr2)->sa_netnum, 4) != 0)
return -1;
if (memcmp
(((struct sockaddr_ipx *)addr1)->sa_nodenum,
((struct sockaddr_ipx *)addr2)->sa_nodenum, 6) != 0)
return -1;
if (((struct sockaddr_ipx *)addr1)->sa_socket !=
((struct sockaddr_ipx *)addr2)->sa_socket)
return 1;
return 0;
}
//=============================================================================
int
WIPX_GetSocketPort(struct qsockaddr *addr)
{
return ntohs(((struct sockaddr_ipx *)addr)->sa_socket);
}
int
WIPX_SetSocketPort(struct qsockaddr *addr, int port)
{
((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)port);
return 0;
}
//=============================================================================

47
NQ/net_wipx.h Normal file
View File

@ -0,0 +1,47 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef NET_WIPX_H
#define NET_WIPX_H
// net_wipx.h
#include "net.h"
int WIPX_Init(void);
void WIPX_Shutdown(void);
void WIPX_Listen(qboolean state);
int WIPX_OpenSocket(int port);
int WIPX_CloseSocket(int socket);
int WIPX_Connect(int socket, struct qsockaddr *addr);
int WIPX_CheckNewConnections(void);
int WIPX_Read(int socket, byte *buf, int len, struct qsockaddr *addr);
int WIPX_Write(int socket, byte *buf, int len, struct qsockaddr *addr);
int WIPX_Broadcast(int socket, byte *buf, int len);
char *WIPX_AddrToString(struct qsockaddr *addr);
int WIPX_StringToAddr(char *string, struct qsockaddr *addr);
int WIPX_GetSocketAddr(int socket, struct qsockaddr *addr);
int WIPX_GetNameFromAddr(struct qsockaddr *addr, char *name);
int WIPX_GetAddrFromName(char *name, struct qsockaddr *addr);
int WIPX_AddrCompare(struct qsockaddr *addr1, struct qsockaddr *addr2);
int WIPX_GetSocketPort(struct qsockaddr *addr);
int WIPX_SetSocketPort(struct qsockaddr *addr, int port);
#endif /* NET_WIPX_H */

1668
NQ/pr_cmds.c Normal file

File diff suppressed because it is too large Load Diff

1119
NQ/pr_edict.c Normal file

File diff suppressed because it is too large Load Diff

659
NQ/pr_exec.c Normal file
View File

@ -0,0 +1,659 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include "host.h"
#include "pr_comp.h"
#include "console.h"
#include "progs.h"
#include "server.h"
#include "sys.h"
typedef struct {
int s;
dfunction_t *f;
} prstack_t;
#define MAX_STACK_DEPTH 32
prstack_t pr_stack[MAX_STACK_DEPTH];
int pr_depth;
#define LOCALSTACK_SIZE 2048
int localstack[LOCALSTACK_SIZE];
int localstack_used;
qboolean pr_trace;
dfunction_t *pr_xfunction;
int pr_xstatement;
int pr_argc;
char *pr_opnames[] = {
"DONE",
"MUL_F",
"MUL_V",
"MUL_FV",
"MUL_VF",
"DIV",
"ADD_F",
"ADD_V",
"SUB_F",
"SUB_V",
"EQ_F",
"EQ_V",
"EQ_S",
"EQ_E",
"EQ_FNC",
"NE_F",
"NE_V",
"NE_S",
"NE_E",
"NE_FNC",
"LE",
"GE",
"LT",
"GT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"ADDRESS",
"STORE_F",
"STORE_V",
"STORE_S",
"STORE_ENT",
"STORE_FLD",
"STORE_FNC",
"STOREP_F",
"STOREP_V",
"STOREP_S",
"STOREP_ENT",
"STOREP_FLD",
"STOREP_FNC",
"RETURN",
"NOT_F",
"NOT_V",
"NOT_S",
"NOT_ENT",
"NOT_FNC",
"IF",
"IFNOT",
"CALL0",
"CALL1",
"CALL2",
"CALL3",
"CALL4",
"CALL5",
"CALL6",
"CALL7",
"CALL8",
"STATE",
"GOTO",
"AND",
"OR",
"BITAND",
"BITOR"
};
char *PR_GlobalString(int ofs);
char *PR_GlobalStringNoContents(int ofs);
//=============================================================================
/*
=================
PR_PrintStatement
=================
*/
void
PR_PrintStatement(dstatement_t *s)
{
int i;
if ((unsigned)s->op < sizeof(pr_opnames) / sizeof(pr_opnames[0])) {
Con_Printf("%s ", pr_opnames[s->op]);
i = strlen(pr_opnames[s->op]);
for (; i < 10; i++)
Con_Printf(" ");
}
if (s->op == OP_IF || s->op == OP_IFNOT)
Con_Printf("%sbranch %i", PR_GlobalString(s->a), s->b);
else if (s->op == OP_GOTO) {
Con_Printf("branch %i", s->a);
} else if ((unsigned)(s->op - OP_STORE_F) < 6) {
Con_Printf("%s", PR_GlobalString(s->a));
Con_Printf("%s", PR_GlobalStringNoContents(s->b));
} else {
if (s->a)
Con_Printf("%s", PR_GlobalString(s->a));
if (s->b)
Con_Printf("%s", PR_GlobalString(s->b));
if (s->c)
Con_Printf("%s", PR_GlobalStringNoContents(s->c));
}
Con_Printf("\n");
}
/*
============
PR_StackTrace
============
*/
void
PR_StackTrace(void)
{
dfunction_t *f;
int i;
if (pr_depth == 0) {
Con_Printf("<NO STACK>\n");
return;
}
pr_stack[pr_depth].f = pr_xfunction;
for (i = pr_depth; i >= 0; i--) {
f = pr_stack[i].f;
if (!f)
Con_Printf("<NO FUNCTION>\n");
else
Con_Printf("%12s : %s\n", pr_strings + f->s_file,
pr_strings + f->s_name);
}
}
/*
============
PR_Profile_f
============
*/
void
PR_Profile_f(void)
{
dfunction_t *f, *best;
int max;
int num;
int i;
// FIXME - progs get unloaded? if so, check that progs gets zero'd
if (!progs)
return;
num = 0;
do {
max = 0;
best = NULL;
for (i = 0; i < progs->numfunctions; i++) {
f = &pr_functions[i];
if (f->profile > max) {
max = f->profile;
best = f;
}
}
if (best) {
if (num < 10)
Con_Printf("%7i %s\n", best->profile,
pr_strings + best->s_name);
num++;
best->profile = 0;
}
} while (best);
}
/*
============
PR_RunError
Aborts the currently executing function
============
*/
void
PR_RunError(char *error, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, error);
vsprintf(string, error, argptr);
va_end(argptr);
PR_PrintStatement(pr_statements + pr_xstatement);
PR_StackTrace();
Con_Printf("%s\n", string);
pr_depth = 0; // dump the stack so host_error can shutdown functions
Host_Error("Program error");
}
/*
============================================================================
PR_ExecuteProgram
The interpretation main loop
============================================================================
*/
/*
====================
PR_EnterFunction
Returns the new program statement counter
====================
*/
int
PR_EnterFunction(dfunction_t *f)
{
int i, j, c, o;
pr_stack[pr_depth].s = pr_xstatement;
pr_stack[pr_depth].f = pr_xfunction;
pr_depth++;
if (pr_depth >= MAX_STACK_DEPTH)
PR_RunError("stack overflow");
// save off any locals that the new function steps on
c = f->locals;
if (localstack_used + c > LOCALSTACK_SIZE)
PR_RunError("PR_ExecuteProgram: locals stack overflow\n");
for (i = 0; i < c; i++)
localstack[localstack_used + i] =
((int *)pr_globals)[f->parm_start + i];
localstack_used += c;
// copy parameters
o = f->parm_start;
for (i = 0; i < f->numparms; i++) {
for (j = 0; j < f->parm_size[i]; j++) {
((int *)pr_globals)[o] =
((int *)pr_globals)[OFS_PARM0 + i * 3 + j];
o++;
}
}
pr_xfunction = f;
return f->first_statement - 1; // offset the s++
}
/*
====================
PR_LeaveFunction
====================
*/
int
PR_LeaveFunction(void)
{
int i, c;
if (pr_depth <= 0)
Sys_Error("prog stack underflow");
// restore locals from the stack
c = pr_xfunction->locals;
localstack_used -= c;
if (localstack_used < 0)
PR_RunError("PR_ExecuteProgram: locals stack underflow\n");
for (i = 0; i < c; i++)
((int *)pr_globals)[pr_xfunction->parm_start + i] =
localstack[localstack_used + i];
// up stack
pr_depth--;
pr_xfunction = pr_stack[pr_depth].f;
return pr_stack[pr_depth].s;
}
/*
====================
PR_ExecuteProgram
====================
*/
void
PR_ExecuteProgram(func_t fnum)
{
eval_t *a, *b, *c;
int s;
dstatement_t *st;
dfunction_t *f, *newf;
int runaway;
int i;
edict_t *ed;
int exitdepth;
eval_t *ptr;
if (!fnum || fnum >= progs->numfunctions) {
if (pr_global_struct->self)
ED_Print(PROG_TO_EDICT(pr_global_struct->self));
Host_Error("PR_ExecuteProgram: NULL function");
}
f = &pr_functions[fnum];
runaway = 100000;
pr_trace = false;
// make a stack frame
exitdepth = pr_depth;
s = PR_EnterFunction(f);
while (1) {
s++; // next statement
st = &pr_statements[s];
a = (eval_t *)&pr_globals[st->a];
b = (eval_t *)&pr_globals[st->b];
c = (eval_t *)&pr_globals[st->c];
if (!--runaway)
PR_RunError("runaway loop error");
if (runaway <= 50000 && !(runaway % 5000))
Con_DPrintf("%s: progs execution running away (%i left)\n",
__func__, runaway);
pr_xfunction->profile++;
pr_xstatement = s;
if (pr_trace)
PR_PrintStatement(st);
switch (st->op) {
case OP_ADD_F:
c->_float = a->_float + b->_float;
break;
case OP_ADD_V:
c->vector[0] = a->vector[0] + b->vector[0];
c->vector[1] = a->vector[1] + b->vector[1];
c->vector[2] = a->vector[2] + b->vector[2];
break;
case OP_SUB_F:
c->_float = a->_float - b->_float;
break;
case OP_SUB_V:
c->vector[0] = a->vector[0] - b->vector[0];
c->vector[1] = a->vector[1] - b->vector[1];
c->vector[2] = a->vector[2] - b->vector[2];
break;
case OP_MUL_F:
c->_float = a->_float * b->_float;
break;
case OP_MUL_V:
c->_float = a->vector[0] * b->vector[0]
+ a->vector[1] * b->vector[1]
+ a->vector[2] * b->vector[2];
break;
case OP_MUL_FV:
c->vector[0] = a->_float * b->vector[0];
c->vector[1] = a->_float * b->vector[1];
c->vector[2] = a->_float * b->vector[2];
break;
case OP_MUL_VF:
c->vector[0] = b->_float * a->vector[0];
c->vector[1] = b->_float * a->vector[1];
c->vector[2] = b->_float * a->vector[2];
break;
case OP_DIV_F:
c->_float = a->_float / b->_float;
break;
case OP_BITAND:
c->_float = (int)a->_float & (int)b->_float;
break;
case OP_BITOR:
c->_float = (int)a->_float | (int)b->_float;
break;
case OP_GE:
c->_float = a->_float >= b->_float;
break;
case OP_LE:
c->_float = a->_float <= b->_float;
break;
case OP_GT:
c->_float = a->_float > b->_float;
break;
case OP_LT:
c->_float = a->_float < b->_float;
break;
case OP_AND:
c->_float = a->_float && b->_float;
break;
case OP_OR:
c->_float = a->_float || b->_float;
break;
case OP_NOT_F:
c->_float = !a->_float;
break;
case OP_NOT_V:
c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2];
break;
case OP_NOT_S:
c->_float = !a->string || !pr_strings[a->string];
break;
case OP_NOT_FNC:
c->_float = !a->function;
break;
case OP_NOT_ENT:
c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts);
break;
case OP_EQ_F:
c->_float = a->_float == b->_float;
break;
case OP_EQ_V:
c->_float = (a->vector[0] == b->vector[0]) &&
(a->vector[1] == b->vector[1]) &&
(a->vector[2] == b->vector[2]);
break;
case OP_EQ_S:
c->_float =
!strcmp(pr_strings + a->string, pr_strings + b->string);
break;
case OP_EQ_E:
c->_float = a->_int == b->_int;
break;
case OP_EQ_FNC:
c->_float = a->function == b->function;
break;
case OP_NE_F:
c->_float = a->_float != b->_float;
break;
case OP_NE_V:
c->_float = (a->vector[0] != b->vector[0]) ||
(a->vector[1] != b->vector[1]) ||
(a->vector[2] != b->vector[2]);
break;
case OP_NE_S:
c->_float =
strcmp(pr_strings + a->string, pr_strings + b->string);
break;
case OP_NE_E:
c->_float = a->_int != b->_int;
break;
case OP_NE_FNC:
c->_float = a->function != b->function;
break;
//==================
case OP_STORE_F:
case OP_STORE_ENT:
case OP_STORE_FLD: // integers
case OP_STORE_S:
case OP_STORE_FNC: // pointers
b->_int = a->_int;
break;
case OP_STORE_V:
b->vector[0] = a->vector[0];
b->vector[1] = a->vector[1];
b->vector[2] = a->vector[2];
break;
case OP_STOREP_F:
case OP_STOREP_ENT:
case OP_STOREP_FLD: // integers
case OP_STOREP_S:
case OP_STOREP_FNC: // pointers
ptr = (eval_t *)((byte *)sv.edicts + b->_int);
ptr->_int = a->_int;
break;
case OP_STOREP_V:
ptr = (eval_t *)((byte *)sv.edicts + b->_int);
ptr->vector[0] = a->vector[0];
ptr->vector[1] = a->vector[1];
ptr->vector[2] = a->vector[2];
break;
case OP_ADDRESS:
ed = PROG_TO_EDICT(a->edict);
#ifdef PARANOID
NUM_FOR_EDICT(ed); // make sure it's in range
#endif
if (ed == (edict_t *)sv.edicts && sv.state == ss_active)
PR_RunError("assignment to world entity");
c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts;
break;
case OP_LOAD_F:
case OP_LOAD_FLD:
case OP_LOAD_ENT:
case OP_LOAD_S:
case OP_LOAD_FNC:
ed = PROG_TO_EDICT(a->edict);
#ifdef PARANOID
NUM_FOR_EDICT(ed); // make sure it's in range
#endif
a = (eval_t *)((int *)&ed->v + b->_int);
c->_int = a->_int;
break;
case OP_LOAD_V:
ed = PROG_TO_EDICT(a->edict);
#ifdef PARANOID
NUM_FOR_EDICT(ed); // make sure it's in range
#endif
a = (eval_t *)((int *)&ed->v + b->_int);
c->vector[0] = a->vector[0];
c->vector[1] = a->vector[1];
c->vector[2] = a->vector[2];
break;
//==================
case OP_IFNOT:
if (!a->_int)
s += st->b - 1; // offset the s++
break;
case OP_IF:
if (a->_int)
s += st->b - 1; // offset the s++
break;
case OP_GOTO:
s += st->a - 1; // offset the s++
break;
case OP_CALL0:
case OP_CALL1:
case OP_CALL2:
case OP_CALL3:
case OP_CALL4:
case OP_CALL5:
case OP_CALL6:
case OP_CALL7:
case OP_CALL8:
pr_argc = st->op - OP_CALL0;
if (!a->function)
PR_RunError("NULL function");
newf = &pr_functions[a->function];
/* negative statements are built in functions */
if (newf->first_statement < 0) {
i = -newf->first_statement;
if (i >= pr_numbuiltins)
PR_RunError("Bad builtin call number");
pr_builtins[i] ();
break;
}
s = PR_EnterFunction(newf);
break;
case OP_DONE:
case OP_RETURN:
pr_globals[OFS_RETURN] = pr_globals[st->a];
pr_globals[OFS_RETURN + 1] = pr_globals[st->a + 1];
pr_globals[OFS_RETURN + 2] = pr_globals[st->a + 2];
s = PR_LeaveFunction();
if (pr_depth == exitdepth)
return; // all done
break;
case OP_STATE:
ed = PROG_TO_EDICT(pr_global_struct->self);
ed->v.nextthink = pr_global_struct->time + 0.1;
if (a->_float != ed->v.frame) {
ed->v.frame = a->_float;
}
ed->v.think = b->function;
break;
default:
PR_RunError("Bad opcode %i", st->op);
}
}
}

27
NQ/progdefs.h Normal file
View File

@ -0,0 +1,27 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PROGDEFS_H
#define PROGDEFS_H
#include "mathlib.h"
#include "progdefs.q1"
#endif /* PROGDEFS_H */

143
NQ/progdefs.q1 Normal file
View File

@ -0,0 +1,143 @@
/* file generated by qcc, do not modify */
typedef struct
{ int pad[28];
int self;
int other;
int world;
float time;
float frametime;
float force_retouch;
string_t mapname;
float deathmatch;
float coop;
float teamplay;
float serverflags;
float total_secrets;
float total_monsters;
float found_secrets;
float killed_monsters;
float parm1;
float parm2;
float parm3;
float parm4;
float parm5;
float parm6;
float parm7;
float parm8;
float parm9;
float parm10;
float parm11;
float parm12;
float parm13;
float parm14;
float parm15;
float parm16;
vec3_t v_forward;
vec3_t v_up;
vec3_t v_right;
float trace_allsolid;
float trace_startsolid;
float trace_fraction;
vec3_t trace_endpos;
vec3_t trace_plane_normal;
float trace_plane_dist;
int trace_ent;
float trace_inopen;
float trace_inwater;
int msg_entity;
func_t main;
func_t StartFrame;
func_t PlayerPreThink;
func_t PlayerPostThink;
func_t ClientKill;
func_t ClientConnect;
func_t PutClientInServer;
func_t ClientDisconnect;
func_t SetNewParms;
func_t SetChangeParms;
} globalvars_t;
typedef struct
{
float modelindex;
vec3_t absmin;
vec3_t absmax;
float ltime;
float movetype;
float solid;
vec3_t origin;
vec3_t oldorigin;
vec3_t velocity;
vec3_t angles;
vec3_t avelocity;
vec3_t punchangle;
string_t classname;
string_t model;
float frame;
float skin;
float effects;
vec3_t mins;
vec3_t maxs;
vec3_t size;
func_t touch;
func_t use;
func_t think;
func_t blocked;
float nextthink;
int groundentity;
float health;
float frags;
float weapon;
string_t weaponmodel;
float weaponframe;
float currentammo;
float ammo_shells;
float ammo_nails;
float ammo_rockets;
float ammo_cells;
float items;
float takedamage;
int chain;
float deadflag;
vec3_t view_ofs;
float button0;
float button1;
float button2;
float impulse;
float fixangle;
vec3_t v_angle;
float idealpitch;
string_t netname;
int enemy;
float flags;
float colormap;
float team;
float max_health;
float teleport_time;
float armortype;
float armorvalue;
float waterlevel;
float watertype;
float ideal_yaw;
float yaw_speed;
int aiment;
int goalentity;
float spawnflags;
string_t target;
string_t targetname;
float dmg_take;
float dmg_save;
int dmg_inflictor;
int owner;
vec3_t movedir;
string_t message;
float sounds;
string_t noise;
string_t noise1;
string_t noise2;
string_t noise3;
} entvars_t;
#define PROGHEADER_CRC 5927

158
NQ/progdefs.q2 Normal file
View File

@ -0,0 +1,158 @@
/* file generated by qcc, do not modify */
typedef struct
{ int pad[28];
int self;
int other;
int world;
float time;
float frametime;
float force_retouch;
string_t mapname;
string_t startspot;
float deathmatch;
float coop;
float teamplay;
float serverflags;
float total_secrets;
float total_monsters;
float found_secrets;
float killed_monsters;
float parm1;
float parm2;
float parm3;
float parm4;
float parm5;
float parm6;
float parm7;
float parm8;
float parm9;
float parm10;
float parm11;
float parm12;
float parm13;
float parm14;
float parm15;
float parm16;
vec3_t v_forward;
vec3_t v_up;
vec3_t v_right;
float trace_allsolid;
float trace_startsolid;
float trace_fraction;
vec3_t trace_endpos;
vec3_t trace_plane_normal;
float trace_plane_dist;
int trace_ent;
float trace_inopen;
float trace_inwater;
int msg_entity;
string_t null;
func_t main;
func_t StartFrame;
func_t PlayerPreThink;
func_t PlayerPostThink;
func_t ClientKill;
func_t ClientConnect;
func_t PutClientInServer;
func_t ClientDisconnect;
func_t SetNewParms;
func_t SetChangeParms;
} globalvars_t;
typedef struct
{
float modelindex;
vec3_t absmin;
vec3_t absmax;
float ltime;
float movetype;
float solid;
vec3_t origin;
vec3_t oldorigin;
vec3_t velocity;
vec3_t angles;
vec3_t avelocity;
vec3_t basevelocity;
vec3_t punchangle;
string_t classname;
string_t model;
float frame;
float skin;
float effects;
float drawPercent;
float gravity;
float mass;
float light_level;
vec3_t mins;
vec3_t maxs;
vec3_t size;
func_t touch;
func_t use;
func_t think;
func_t blocked;
float nextthink;
int groundentity;
float health;
float frags;
float weapon;
string_t weaponmodel;
float weaponframe;
float currentammo;
float ammo_shells;
float ammo_nails;
float ammo_rockets;
float ammo_cells;
float items;
float items2;
float takedamage;
int chain;
float deadflag;
vec3_t view_ofs;
float button0;
float button1;
float button2;
float impulse;
float fixangle;
vec3_t v_angle;
float idealpitch;
float pitch_speed;
string_t netname;
int enemy;
float flags;
float colormap;
float team;
float max_health;
float teleport_time;
float armortype;
float armorvalue;
float waterlevel;
float watertype;
float ideal_yaw;
float yaw_speed;
int aiment;
int goalentity;
float spawnflags;
string_t target;
string_t targetname;
float dmg_take;
float dmg_save;
int dmg_inflictor;
int owner;
vec3_t movedir;
string_t message;
float sounds;
string_t noise;
string_t noise1;
string_t noise2;
string_t noise3;
float dmg;
float dmgtime;
float air_finished;
float pain_finished;
float radsuit_finished;
float speed;
} entvars_t;
#define PROGHEADER_CRC 31586

136
NQ/progs.h Normal file
View File

@ -0,0 +1,136 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PROGS_H
#define PROGS_H
#include "pr_comp.h" // defs shared with qcc
#include "progdefs.h" // generated by program cdefs
#include "common.h"
typedef union eval_s {
string_t string;
float _float;
float vector[3];
func_t function;
int _int;
int edict;
} eval_t;
#define MAX_ENT_LEAFS 16
typedef struct edict_s {
qboolean free;
link_t area; // linked to a division node or leaf
int num_leafs;
short leafnums[MAX_ENT_LEAFS];
entity_state_t baseline;
float freetime; // sv.time when the object was freed
entvars_t v; // C exported fields from progs
// other fields from progs come immediately after
} edict_t;
#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area)
//============================================================================
extern dprograms_t *progs;
extern dfunction_t *pr_functions;
extern char *pr_strings;
extern dstatement_t *pr_statements;
extern globalvars_t *pr_global_struct;
extern float *pr_globals; // same as pr_global_struct
// FIXME - this ok as global? Need to init before use...
extern char *pr_string_temp;
extern int pr_edict_size; // in bytes
//============================================================================
void PR_Init(void);
void PR_ExecuteProgram(func_t fnum);
void PR_LoadProgs(void);
void PR_Profile_f(void);
edict_t *ED_Alloc(void);
void ED_Free(edict_t *ed);
// returns a copy of the string allocated from the server's string heap
void ED_Print(edict_t *ed);
void ED_Write(FILE *f, edict_t *ed);
char *ED_ParseEdict(char *data, edict_t *ent);
void ED_WriteGlobals(FILE *f);
void ED_ParseGlobals(char *data);
void ED_LoadFromFile(char *data);
//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size))
//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size)
edict_t *EDICT_NUM(int n);
int NUM_FOR_EDICT(edict_t *e);
#define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size))
#define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts)
#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e))
//============================================================================
#define G_FLOAT(o) (pr_globals[o])
#define G_INT(o) (*(int *)&pr_globals[o])
#define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o]))
#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o))
#define G_VECTOR(o) (&pr_globals[o])
#define G_STRING(o) (pr_strings + *(string_t *)&pr_globals[o])
#define G_FUNCTION(o) (*(func_t *)&pr_globals[o])
#define E_FLOAT(e,o) (((float*)&e->v)[o])
#define E_INT(e,o) (*(int *)&((float*)&e->v)[o])
#define E_VECTOR(e,o) (&((float*)&e->v)[o])
#define E_STRING(e,o) (pr_strings + *(string_t *)&((float*)&e->v)[o])
typedef void (*builtin_t) (void);
extern builtin_t *pr_builtins;
extern int pr_numbuiltins;
extern int pr_argc;
extern qboolean pr_trace;
extern dfunction_t *pr_xfunction;
extern int pr_xstatement;
extern unsigned short pr_crc;
void PR_RunError(char *error, ...);
void ED_PrintEdicts(void);
void ED_PrintNum(int ent);
eval_t *GetEdictFieldValue(edict_t *ed, char *field);
#endif /* PROGS_H */

171
NQ/protocol.h Normal file
View File

@ -0,0 +1,171 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PROTOCOL_H
#define PROTOCOL_H
// protocol.h -- communications protocols
#define PROTOCOL_VERSION 15
// if the high bit of the servercmd is set, the low bits are fast update flags:
#define U_MOREBITS (1<<0)
#define U_ORIGIN1 (1<<1)
#define U_ORIGIN2 (1<<2)
#define U_ORIGIN3 (1<<3)
#define U_ANGLE2 (1<<4)
#define U_NOLERP (1<<5) // don't interpolate movement
#define U_FRAME (1<<6)
#define U_SIGNAL (1<<7) // just differentiates from other updates
// svc_update can pass all of the fast update bits, plus more
#define U_ANGLE1 (1<<8)
#define U_ANGLE3 (1<<9)
#define U_MODEL (1<<10)
#define U_COLORMAP (1<<11)
#define U_SKIN (1<<12)
#define U_EFFECTS (1<<13)
#define U_LONGENTITY (1<<14)
#define SU_VIEWHEIGHT (1<<0)
#define SU_IDEALPITCH (1<<1)
#define SU_PUNCH1 (1<<2)
#define SU_PUNCH2 (1<<3)
#define SU_PUNCH3 (1<<4)
#define SU_VELOCITY1 (1<<5)
#define SU_VELOCITY2 (1<<6)
#define SU_VELOCITY3 (1<<7)
//define SU_AIMENT (1<<8) AVAILABLE BIT
#define SU_ITEMS (1<<9)
#define SU_ONGROUND (1<<10) // no data follows, the bit is it
#define SU_INWATER (1<<11) // no data follows, the bit is it
#define SU_WEAPONFRAME (1<<12)
#define SU_ARMOR (1<<13)
#define SU_WEAPON (1<<14)
// a sound with no channel is a local only sound
#define SND_VOLUME (1<<0) // a byte
#define SND_ATTENUATION (1<<1) // a byte
#define SND_LOOPING (1<<2) // a long
// defaults for clientinfo messages
#define DEFAULT_VIEWHEIGHT 22
// game types sent by serverinfo
// these determine which intermission screen plays
#define GAME_COOP 0
#define GAME_DEATHMATCH 1
//==================
// note that there are some defs.qc that mirror to these numbers
// also related to svc_strings[] in cl_parse
//==================
//
// server to client
//
#define svc_bad 0
#define svc_nop 1
#define svc_disconnect 2
#define svc_updatestat 3 // [byte] [long]
#define svc_version 4 // [long] server version
#define svc_setview 5 // [short] entity number
#define svc_sound 6 // <see code>
#define svc_time 7 // [float] server time
#define svc_print 8 // [string] null terminated string
#define svc_stufftext 9 // [string] stuffed into client's console buffer
// the string should be \n terminated
#define svc_setangle 10 // [angle3] set the view angle to this absolute value
#define svc_serverinfo 11 // [long] version
// [string] signon string
// [string]..[0]model cache
// [string]...[0]sounds cache
#define svc_lightstyle 12 // [byte] [string]
#define svc_updatename 13 // [byte] [string]
#define svc_updatefrags 14 // [byte] [short]
#define svc_clientdata 15 // <shortbits + data>
#define svc_stopsound 16 // <see code>
#define svc_updatecolors 17 // [byte] [byte]
#define svc_particle 18 // [vec3] <variable>
#define svc_damage 19
#define svc_spawnstatic 20
// svc_spawnbinary 21
#define svc_spawnbaseline 22
#define svc_temp_entity 23
#define svc_setpause 24 // [byte] on / off
#define svc_signonnum 25 // [byte] used for the signon sequence
#define svc_centerprint 26 // [string] to put in center of the screen
#define svc_killedmonster 27
#define svc_foundsecret 28
#define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten
#define svc_intermission 30 // [string] music
#define svc_finale 31 // [string] music [string] text
#define svc_cdtrack 32 // [byte] track [byte] looptrack
#define svc_sellscreen 33
#define svc_cutscene 34
//
// client to server
//
#define clc_bad 0
#define clc_nop 1
#define clc_disconnect 2
#define clc_move 3 // [usercmd_t]
#define clc_stringcmd 4 // [string] message
//
// temp entity events
//
#define TE_SPIKE 0
#define TE_SUPERSPIKE 1
#define TE_GUNSHOT 2
#define TE_EXPLOSION 3
#define TE_TAREXPLOSION 4
#define TE_LIGHTNING1 5
#define TE_LIGHTNING2 6
#define TE_WIZSPIKE 7
#define TE_KNIGHTSPIKE 8
#define TE_LIGHTNING3 9
#define TE_LAVASPLASH 10
#define TE_TELEPORT 11
#define TE_EXPLOSION2 12
// PGM 01/21/97
#define TE_BEAM 13
// PGM 01/21/97
// FIXME - use this properly...
#define MAX_CLIENTS 16
#endif /* PROTOCOL_H */

BIN
NQ/qe3.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
NQ/quake.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
NQ/quake.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

233
NQ/quakedef.h Normal file
View File

@ -0,0 +1,233 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// quakedef.h -- primary header for client
// -- FIXME - needs splitting up into components...
#ifndef QUAKEDEF_H
#define QUAKEDEF_H
#include <math.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include "cvar.h"
#include "mathlib.h"
//define PARANOID // speed sapping error checking
#define GAMENAME "id1" // directory to look in by default
#if defined(__i386__)
#define id386 1
#else
#define id386 0
#endif
/* UNALIGNED_OK - undef if unaligned accesses are not supported */
#if id386
#define UNALIGNED_OK
#else
#undef UNALIGNED_OK
#endif
// !!! if this is changed, it must be changed in d_ifacea.h too !!!
#define CACHE_SIZE 32 // used to align key data structures
#define UNUSED(x) (x = x) // for pesky compiler / lint warnings
#define MINIMUM_MEMORY 0x550000
#define MINIMUM_MEMORY_LEVELPAK (MINIMUM_MEMORY + 0x100000)
#define MAX_NUM_ARGVS 50
// up / down
#define PITCH 0
// left / right
#define YAW 1
// fall over
#define ROLL 2
#define MAX_QPATH 64 // max length of a quake game pathname
#define MAX_OSPATH 128 // max length of a filesystem pathname
#define ON_EPSILON 0.1 // point on plane side epsilon
#define MAX_MSGLEN 8000 // max length of a reliable message
#define MAX_DATAGRAM 1024 // max length of unreliable message
//
// per-level limits
//
//#define MAX_EDICTS 600 // FIXME: ouch! ouch! ouch!
#define MAX_EDICTS 2048 // FIXME: Arbitrary increase, make dynamic?
#define MAX_LIGHTSTYLES 64
#define MAX_MODELS 256 // these are sent over the net as bytes
#define MAX_SOUNDS 256 // so they cannot be blindly increased
#define SAVEGAME_COMMENT_LENGTH 39
#define MAX_STYLESTRING 64
//
// stats are integers communicated to the client by the server
//
#define MAX_CL_STATS 32
#define STAT_HEALTH 0
#define STAT_FRAGS 1
#define STAT_WEAPON 2
#define STAT_AMMO 3
#define STAT_ARMOR 4
#define STAT_WEAPONFRAME 5
#define STAT_SHELLS 6
#define STAT_NAILS 7
#define STAT_ROCKETS 8
#define STAT_CELLS 9
#define STAT_ACTIVEWEAPON 10
#define STAT_TOTALSECRETS 11
#define STAT_TOTALMONSTERS 12
#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret
#define STAT_MONSTERS 14 // bumped by svc_killedmonster
// stock defines
#define IT_SHOTGUN 1
#define IT_SUPER_SHOTGUN 2
#define IT_NAILGUN 4
#define IT_SUPER_NAILGUN 8
#define IT_GRENADE_LAUNCHER 16
#define IT_ROCKET_LAUNCHER 32
#define IT_LIGHTNING 64
#define IT_SUPER_LIGHTNING 128
#define IT_SHELLS 256
#define IT_NAILS 512
#define IT_ROCKETS 1024
#define IT_CELLS 2048
#define IT_AXE 4096
#define IT_ARMOR1 8192
#define IT_ARMOR2 16384
#define IT_ARMOR3 32768
#define IT_SUPERHEALTH 65536
#define IT_KEY1 131072
#define IT_KEY2 262144
#define IT_INVISIBILITY 524288
#define IT_INVULNERABILITY 1048576
#define IT_SUIT 2097152
#define IT_QUAD 4194304
#define IT_SIGIL1 (1<<28)
#define IT_SIGIL2 (1<<29)
#define IT_SIGIL3 (1<<30)
#define IT_SIGIL4 (1<<31)
//===========================================
//rogue changed and added defines
#define RIT_SHELLS 128
#define RIT_NAILS 256
#define RIT_ROCKETS 512
#define RIT_CELLS 1024
#define RIT_AXE 2048
#define RIT_LAVA_NAILGUN 4096
#define RIT_LAVA_SUPER_NAILGUN 8192
#define RIT_MULTI_GRENADE 16384
#define RIT_MULTI_ROCKET 32768
#define RIT_PLASMA_GUN 65536
#define RIT_ARMOR1 8388608
#define RIT_ARMOR2 16777216
#define RIT_ARMOR3 33554432
#define RIT_LAVA_NAILS 67108864
#define RIT_PLASMA_AMMO 134217728
#define RIT_MULTI_ROCKETS 268435456
#define RIT_SHIELD 536870912
#define RIT_ANTIGRAV 1073741824
#define RIT_SUPERHEALTH 2147483648
//MED 01/04/97 added hipnotic defines
//===========================================
//hipnotic added defines
#define HIT_MJOLNIR_BIT 7
#define HIT_PROXIMITY_GUN_BIT 16
#define HIT_LASER_CANNON_BIT 23
#define HIT_MJOLNIR (1<<HIT_MJOLNIR_BIT)
#define HIT_PROXIMITY_GUN (1<<HIT_PROXIMITY_GUN_BIT)
#define HIT_LASER_CANNON (1<<HIT_LASER_CANNON_BIT)
#define HIT_WETSUIT (1<<(23+2))
#define HIT_EMPATHY_SHIELDS (1<<(23+3))
//===========================================
#define MAX_SCOREBOARD 16
#define SOUND_CHANNELS 8
// This makes anyone on id's net privileged
// Use for multiplayer testing only - VERY dangerous!!!
// #define IDGODS
typedef struct {
vec3_t origin;
vec3_t angles;
int modelindex;
int frame;
int colormap;
int skinnum;
int effects;
} entity_state_t;
//=============================================================================
// the host system specifies the base of the directory tree, the
// command line parms passed to the program, and the amount of memory
// available for the program to use
typedef struct {
char *basedir;
char *cachedir; // for development over ISDN lines
int argc;
char **argv;
void *membase;
int memsize;
} quakeparms_t;
//=============================================================================
extern qboolean noclip_anglehack;
//
// chase
//
extern cvar_t chase_active;
void Chase_Init(void);
void Chase_Reset(void);
void Chase_Update(void);
extern cvar_t r_lockfrustum; // FIXME - with rendering stuff please...
extern cvar_t r_lockpvs; // FIXME - with rendering stuff please...
extern cvar_t r_drawflat; // FIXME - with rendering stuff please...
#endif /* QUAKEDEF_H */

736
NQ/r_alias.c Normal file
View File

@ -0,0 +1,736 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_alias.c: routines for setting up to draw alias models
#include "console.h"
#include "quakedef.h"
#include "r_local.h"
#include "sys.h"
/* FIXME: shouldn't be needed (is needed for patch right now, but that should
move) */
#include "d_local.h"
/* lowest light value we'll allow, to avoid the need for inner-loop light
clamping */
#define LIGHT_MIN 5
mtriangle_t *ptriangles;
affinetridesc_t r_affinetridesc;
void *acolormap; // FIXME: should go away
trivertx_t *r_apverts;
// TODO: these probably will go away with optimized rasterization
mdl_t *pmdl;
vec3_t r_plightvec;
int r_ambientlight;
float r_shadelight;
aliashdr_t *paliashdr;
finalvert_t *pfinalverts;
auxvert_t *pauxverts;
static float ziscale;
static model_t *pmodel;
static vec3_t alias_forward, alias_right, alias_up;
static maliasskindesc_t *pskindesc;
int r_amodels_drawn;
int a_skinwidth;
int r_anumverts;
float aliastransform[3][4];
typedef struct {
int index0;
int index1;
} aedge_t;
static aedge_t aedges[12] = {
{0, 1}, {1, 2}, {2, 3}, {3, 0},
{4, 5}, {5, 6}, {6, 7}, {7, 4},
{0, 5}, {1, 4}, {2, 7}, {3, 6}
};
#define NUMVERTEXNORMALS 162
float r_avertexnormals[NUMVERTEXNORMALS][3] = {
#include "anorms.h"
};
void R_AliasTransformAndProjectFinalVerts(finalvert_t *fv,
stvert_t *pstverts);
void R_AliasSetUpTransform(int trivial_accept);
void R_AliasTransformVector(vec3_t in, vec3_t out);
void R_AliasTransformFinalVert(finalvert_t *fv, auxvert_t *av,
trivertx_t *pverts, stvert_t *pstverts);
void R_AliasProjectFinalVert(finalvert_t *fv, auxvert_t *av);
/*
================
R_AliasCheckBBox
================
*/
qboolean
R_AliasCheckBBox(void)
{
int i, flags, frame, numv;
aliashdr_t *pahdr;
float zi, basepts[8][3], v0, v1, frac;
finalvert_t *pv0, *pv1, viewpts[16];
auxvert_t *pa0, *pa1, viewaux[16];
maliasframedesc_t *pframedesc;
qboolean zclipped, zfullyclipped;
unsigned anyclip, allclip;
int minz;
// expand, rotate, and translate points into worldspace
currententity->trivial_accept = 0;
pmodel = currententity->model;
pahdr = Mod_Extradata(pmodel);
pmdl = (mdl_t *)((byte *)pahdr + pahdr->model);
R_AliasSetUpTransform(0);
// construct the base bounding box for this frame
frame = currententity->frame;
// TODO: don't repeat this check when drawing?
if ((frame >= pmdl->numframes) || (frame < 0)) {
Con_DPrintf("No such frame %d %s\n", frame, pmodel->name);
frame = 0;
}
pframedesc = &pahdr->frames[frame];
// x worldspace coordinates
basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] =
(float)pframedesc->bboxmin.v[0];
basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] =
(float)pframedesc->bboxmax.v[0];
// y worldspace coordinates
basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] =
(float)pframedesc->bboxmin.v[1];
basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] =
(float)pframedesc->bboxmax.v[1];
// z worldspace coordinates
basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] =
(float)pframedesc->bboxmin.v[2];
basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] =
(float)pframedesc->bboxmax.v[2];
zclipped = false;
zfullyclipped = true;
minz = 9999;
for (i = 0; i < 8; i++) {
R_AliasTransformVector(&basepts[i][0], &viewaux[i].fv[0]);
if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE) {
// we must clip points that are closer than the near clip plane
viewpts[i].flags = ALIAS_Z_CLIP;
zclipped = true;
} else {
if (viewaux[i].fv[2] < minz)
minz = viewaux[i].fv[2];
viewpts[i].flags = 0;
zfullyclipped = false;
}
}
if (zfullyclipped) {
return false; // everything was near-z-clipped
}
numv = 8;
if (zclipped) {
// organize points by edges, use edges to get new points (possible trivial
// reject)
for (i = 0; i < 12; i++) {
// edge endpoints
pv0 = &viewpts[aedges[i].index0];
pv1 = &viewpts[aedges[i].index1];
pa0 = &viewaux[aedges[i].index0];
pa1 = &viewaux[aedges[i].index1];
// if one end is clipped and the other isn't, make a new point
if (pv0->flags ^ pv1->flags) {
frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) /
(pa1->fv[2] - pa0->fv[2]);
viewaux[numv].fv[0] = pa0->fv[0] +
(pa1->fv[0] - pa0->fv[0]) * frac;
viewaux[numv].fv[1] = pa0->fv[1] +
(pa1->fv[1] - pa0->fv[1]) * frac;
viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE;
viewpts[numv].flags = 0;
numv++;
}
}
}
// project the vertices that remain after clipping
anyclip = 0;
allclip = ALIAS_XY_CLIP_MASK;
// TODO: probably should do this loop in ASM, especially if we use floats
for (i = 0; i < numv; i++) {
// we don't need to bother with vertices that were z-clipped
if (viewpts[i].flags & ALIAS_Z_CLIP)
continue;
zi = 1.0 / viewaux[i].fv[2];
// FIXME: do with chop mode in ASM, or convert to float
v0 = (viewaux[i].fv[0] * xscale * zi) + xcenter;
v1 = (viewaux[i].fv[1] * yscale * zi) + ycenter;
flags = 0;
if (v0 < r_refdef.fvrectx)
flags |= ALIAS_LEFT_CLIP;
if (v1 < r_refdef.fvrecty)
flags |= ALIAS_TOP_CLIP;
if (v0 > r_refdef.fvrectright)
flags |= ALIAS_RIGHT_CLIP;
if (v1 > r_refdef.fvrectbottom)
flags |= ALIAS_BOTTOM_CLIP;
anyclip |= flags;
allclip &= flags;
}
if (allclip)
return false; // trivial reject off one side
currententity->trivial_accept = !anyclip & !zclipped;
if (currententity->trivial_accept) {
if (minz > (r_aliastransition + (pmdl->size * r_resfudge))) {
currententity->trivial_accept |= 2;
}
}
return true;
}
/*
================
R_AliasTransformVector
================
*/
void
R_AliasTransformVector(vec3_t in, vec3_t out)
{
out[0] = DotProduct(in, aliastransform[0]) + aliastransform[0][3];
out[1] = DotProduct(in, aliastransform[1]) + aliastransform[1][3];
out[2] = DotProduct(in, aliastransform[2]) + aliastransform[2][3];
}
/*
================
R_AliasPreparePoints
General clipped case
================
*/
void
R_AliasPreparePoints(void)
{
int i;
stvert_t *pstverts;
finalvert_t *fv;
auxvert_t *av;
mtriangle_t *ptri;
finalvert_t *pfv[3];
pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts);
r_anumverts = pmdl->numverts;
fv = pfinalverts;
av = pauxverts;
for (i = 0; i < r_anumverts; i++, fv++, av++, r_apverts++, pstverts++) {
R_AliasTransformFinalVert(fv, av, r_apverts, pstverts);
if (av->fv[2] < ALIAS_Z_CLIP_PLANE)
fv->flags |= ALIAS_Z_CLIP;
else {
R_AliasProjectFinalVert(fv, av);
if (fv->v[0] < r_refdef.aliasvrect.x)
fv->flags |= ALIAS_LEFT_CLIP;
if (fv->v[1] < r_refdef.aliasvrect.y)
fv->flags |= ALIAS_TOP_CLIP;
if (fv->v[0] > r_refdef.aliasvrectright)
fv->flags |= ALIAS_RIGHT_CLIP;
if (fv->v[1] > r_refdef.aliasvrectbottom)
fv->flags |= ALIAS_BOTTOM_CLIP;
}
}
//
// clip and draw all triangles
//
r_affinetridesc.numtriangles = 1;
ptri = (mtriangle_t *)((byte *)paliashdr + paliashdr->triangles);
for (i = 0; i < pmdl->numtris; i++, ptri++) {
pfv[0] = &pfinalverts[ptri->vertindex[0]];
pfv[1] = &pfinalverts[ptri->vertindex[1]];
pfv[2] = &pfinalverts[ptri->vertindex[2]];
if (pfv[0]->flags & pfv[1]->flags & pfv[2]->
flags & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP))
continue; // completely clipped
if (!((pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP))) { // totally unclipped
r_affinetridesc.pfinalverts = pfinalverts;
r_affinetridesc.ptriangles = ptri;
D_PolysetDraw();
} else { // partially clipped
R_AliasClipTriangle(ptri);
}
}
}
/*
================
R_AliasSetUpTransform
================
*/
void
R_AliasSetUpTransform(int trivial_accept)
{
int i;
float rotationmatrix[3][4], t2matrix[3][4];
static float tmatrix[3][4];
static float viewmatrix[3][4];
vec3_t angles;
// TODO: should really be stored with the entity instead of being reconstructed
// TODO: should use a look-up table
// TODO: could cache lazily, stored in the entity
angles[ROLL] = currententity->angles[ROLL];
angles[PITCH] = -currententity->angles[PITCH];
angles[YAW] = currententity->angles[YAW];
AngleVectors(angles, alias_forward, alias_right, alias_up);
tmatrix[0][0] = pmdl->scale[0];
tmatrix[1][1] = pmdl->scale[1];
tmatrix[2][2] = pmdl->scale[2];
tmatrix[0][3] = pmdl->scale_origin[0];
tmatrix[1][3] = pmdl->scale_origin[1];
tmatrix[2][3] = pmdl->scale_origin[2];
// TODO: can do this with simple matrix rearrangement
for (i = 0; i < 3; i++) {
t2matrix[i][0] = alias_forward[i];
t2matrix[i][1] = -alias_right[i];
t2matrix[i][2] = alias_up[i];
}
t2matrix[0][3] = -modelorg[0];
t2matrix[1][3] = -modelorg[1];
t2matrix[2][3] = -modelorg[2];
// FIXME: can do more efficiently than full concatenation
R_ConcatTransforms(t2matrix, tmatrix, rotationmatrix);
// TODO: should be global, set when vright, etc., set
VectorCopy(vright, viewmatrix[0]);
VectorCopy(vup, viewmatrix[1]);
VectorInverse(viewmatrix[1]);
VectorCopy(vpn, viewmatrix[2]);
// viewmatrix[0][3] = 0;
// viewmatrix[1][3] = 0;
// viewmatrix[2][3] = 0;
R_ConcatTransforms(viewmatrix, rotationmatrix, aliastransform);
// do the scaling up of x and y to screen coordinates as part of the transform
// for the unclipped case (it would mess up clipping in the clipped case).
// Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y
// correspondingly so the projected x and y come out right
// FIXME: make this work for clipped case too?
if (trivial_accept) {
for (i = 0; i < 4; i++) {
aliastransform[0][i] *= aliasxscale *
(1.0 / ((float)0x8000 * 0x10000));
aliastransform[1][i] *= aliasyscale *
(1.0 / ((float)0x8000 * 0x10000));
aliastransform[2][i] *= 1.0 / ((float)0x8000 * 0x10000);
}
}
}
/*
================
R_AliasTransformFinalVert
================
*/
void
R_AliasTransformFinalVert(finalvert_t *fv, auxvert_t *av,
trivertx_t *pverts, stvert_t *pstverts)
{
int temp;
float lightcos, *plightnormal;
av->fv[0] = DotProduct(pverts->v, aliastransform[0]) +
aliastransform[0][3];
av->fv[1] = DotProduct(pverts->v, aliastransform[1]) +
aliastransform[1][3];
av->fv[2] = DotProduct(pverts->v, aliastransform[2]) +
aliastransform[2][3];
fv->v[2] = pstverts->s;
fv->v[3] = pstverts->t;
fv->flags = pstverts->onseam;
// lighting
plightnormal = r_avertexnormals[pverts->lightnormalindex];
lightcos = DotProduct(plightnormal, r_plightvec);
temp = r_ambientlight;
if (lightcos < 0) {
temp += (int)(r_shadelight * lightcos);
// clamp; because we limited the minimum ambient and shading light, we
// don't have to clamp low light, just bright
if (temp < 0)
temp = 0;
}
fv->v[4] = temp;
}
#if !id386
/*
================
R_AliasTransformAndProjectFinalVerts
================
*/
void
R_AliasTransformAndProjectFinalVerts(finalvert_t *fv, stvert_t *pstverts)
{
int i, temp;
float lightcos, *plightnormal, zi;
trivertx_t *pverts;
pverts = r_apverts;
for (i = 0; i < r_anumverts; i++, fv++, pverts++, pstverts++) {
// transform and project
zi = 1.0 / (DotProduct(pverts->v, aliastransform[2]) +
aliastransform[2][3]);
// x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is
// scaled up by 1/2**31, and the scaling cancels out for x and y in the
// projection
fv->v[5] = zi;
fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) +
aliastransform[0][3]) * zi) + aliasxcenter;
fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) +
aliastransform[1][3]) * zi) + aliasycenter;
fv->v[2] = pstverts->s;
fv->v[3] = pstverts->t;
fv->flags = pstverts->onseam;
// lighting
plightnormal = r_avertexnormals[pverts->lightnormalindex];
lightcos = DotProduct(plightnormal, r_plightvec);
temp = r_ambientlight;
if (lightcos < 0) {
temp += (int)(r_shadelight * lightcos);
// clamp; because we limited the minimum ambient and shading light, we
// don't have to clamp low light, just bright
if (temp < 0)
temp = 0;
}
fv->v[4] = temp;
}
}
#endif
/*
================
R_AliasProjectFinalVert
================
*/
void
R_AliasProjectFinalVert(finalvert_t *fv, auxvert_t *av)
{
float zi;
// project points
zi = 1.0 / av->fv[2];
fv->v[5] = zi * ziscale;
fv->v[0] = (av->fv[0] * aliasxscale * zi) + aliasxcenter;
fv->v[1] = (av->fv[1] * aliasyscale * zi) + aliasycenter;
}
/*
================
R_AliasPrepareUnclippedPoints
================
*/
void
R_AliasPrepareUnclippedPoints(void)
{
stvert_t *pstverts;
finalvert_t *fv;
pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts);
r_anumverts = pmdl->numverts;
// FIXME: just use pfinalverts directly?
fv = pfinalverts;
R_AliasTransformAndProjectFinalVerts(fv, pstverts);
if (r_affinetridesc.drawtype)
D_PolysetDrawFinalVerts(fv, r_anumverts);
r_affinetridesc.pfinalverts = pfinalverts;
r_affinetridesc.ptriangles = (mtriangle_t *)
((byte *)paliashdr + paliashdr->triangles);
r_affinetridesc.numtriangles = pmdl->numtris;
D_PolysetDraw();
}
/*
===============
R_AliasSetupSkin
===============
*/
void
R_AliasSetupSkin(void)
{
int skinnum;
int i, numskins;
maliasskingroup_t *paliasskingroup;
float *pskinintervals, fullskininterval;
float skintargettime, skintime;
skinnum = currententity->skinnum;
if ((skinnum >= pmdl->numskins) || (skinnum < 0)) {
Con_DPrintf("R_AliasSetupSkin: no such skin # %d\n", skinnum);
skinnum = 0;
}
pskindesc = ((maliasskindesc_t *)
((byte *)paliashdr + paliashdr->skindesc)) + skinnum;
a_skinwidth = pmdl->skinwidth;
if (pskindesc->type == ALIAS_SKIN_GROUP) {
paliasskingroup = (maliasskingroup_t *)((byte *)paliashdr +
pskindesc->skin);
pskinintervals = (float *)
((byte *)paliashdr + paliasskingroup->intervals);
numskins = paliasskingroup->numskins;
fullskininterval = pskinintervals[numskins - 1];
skintime = cl.time + currententity->syncbase;
// when loading in Mod_LoadAliasSkinGroup, we guaranteed all interval
// values are positive, so we don't have to worry about division by 0
skintargettime = skintime -
((int)(skintime / fullskininterval)) * fullskininterval;
for (i = 0; i < (numskins - 1); i++) {
if (pskinintervals[i] > skintargettime)
break;
}
pskindesc = &paliasskingroup->skindescs[i];
}
r_affinetridesc.pskindesc = pskindesc;
r_affinetridesc.pskin = (void *)((byte *)paliashdr + pskindesc->skin);
r_affinetridesc.skinwidth = a_skinwidth;
r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16;
r_affinetridesc.skinheight = pmdl->skinheight;
// FIXME - QW Scoreboard code here?
}
/*
================
R_AliasSetupLighting
================
*/
void
R_AliasSetupLighting(alight_t *plighting)
{
// guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have
// to clamp off the bottom
r_ambientlight = plighting->ambientlight;
if (r_ambientlight < LIGHT_MIN)
r_ambientlight = LIGHT_MIN;
r_ambientlight = (255 - r_ambientlight) << VID_CBITS;
if (r_ambientlight < LIGHT_MIN)
r_ambientlight = LIGHT_MIN;
r_shadelight = plighting->shadelight;
if (r_shadelight < 0)
r_shadelight = 0;
r_shadelight *= VID_GRADES;
// rotate the lighting vector into the model's frame of reference
r_plightvec[0] = DotProduct(plighting->plightvec, alias_forward);
r_plightvec[1] = -DotProduct(plighting->plightvec, alias_right);
r_plightvec[2] = DotProduct(plighting->plightvec, alias_up);
}
/*
=================
R_AliasSetupFrame
set r_apverts
=================
*/
void
R_AliasSetupFrame(void)
{
int frame;
int i, numframes;
maliasgroup_t *paliasgroup;
float *pintervals, fullinterval, targettime, time;
frame = currententity->frame;
if ((frame >= pmdl->numframes) || (frame < 0)) {
Con_DPrintf("R_AliasSetupFrame: no such frame %d\n", frame);
frame = 0;
}
if (paliashdr->frames[frame].type == ALIAS_SINGLE) {
r_apverts = (trivertx_t *)
((byte *)paliashdr + paliashdr->frames[frame].frame);
return;
}
paliasgroup = (maliasgroup_t *)
((byte *)paliashdr + paliashdr->frames[frame].frame);
pintervals = (float *)((byte *)paliashdr + paliasgroup->intervals);
numframes = paliasgroup->numframes;
fullinterval = pintervals[numframes - 1];
time = cl.time + currententity->syncbase;
//
// when loading in Mod_LoadAliasGroup, we guaranteed all interval values
// are positive, so we don't have to worry about division by 0
//
targettime = time - ((int)(time / fullinterval)) * fullinterval;
for (i = 0; i < (numframes - 1); i++) {
if (pintervals[i] > targettime)
break;
}
r_apverts = (trivertx_t *)
((byte *)paliashdr + paliasgroup->frames[i].frame);
}
/*
================
R_AliasDrawModel
================
*/
void
R_AliasDrawModel(alight_t *plighting)
{
finalvert_t finalverts[MAXALIASVERTS +
((CACHE_SIZE - 1) / sizeof(finalvert_t)) + 1];
auxvert_t auxverts[MAXALIASVERTS];
r_amodels_drawn++;
// cache align
pfinalverts = (finalvert_t *)
(((long)&finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
pauxverts = &auxverts[0];
paliashdr = (aliashdr_t *)Mod_Extradata(currententity->model);
pmdl = (mdl_t *)((byte *)paliashdr + paliashdr->model);
R_AliasSetupSkin();
R_AliasSetUpTransform(currententity->trivial_accept);
R_AliasSetupLighting(plighting);
R_AliasSetupFrame();
if (!currententity->colormap)
Sys_Error("%s: !currententity->colormap", __func__);
r_affinetridesc.drawtype = (currententity->trivial_accept == 3) &&
r_recursiveaffinetriangles;
if (r_affinetridesc.drawtype) {
D_PolysetUpdateTables(); // FIXME: precalc...
} else {
#if id386
D_Aff8Patch(currententity->colormap);
#endif
}
acolormap = currententity->colormap;
if (currententity != &cl.viewent)
ziscale = ((float)0x8000) * ((float)0x10000);
else
ziscale = ((float)0x8000) * ((float)0x10000) * 3.0;
if (currententity->trivial_accept)
R_AliasPrepareUnclippedPoints();
else
R_AliasPreparePoints();
}

617
NQ/r_bsp.c Normal file
View File

@ -0,0 +1,617 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_bsp.c
#include "quakedef.h"
#include "r_local.h"
#include "sys.h"
#include "console.h"
//
// current entity info
//
qboolean insubmodel;
entity_t *currententity;
// modelorg is the viewpoint reletive to
// the currently rendering entity
vec3_t modelorg, base_modelorg;
vec3_t r_entorigin; // the currently rendering entity in world coordinates
float entity_rotation[3][3];
vec3_t r_worldmodelorg;
int r_currentbkey;
typedef enum { touchessolid, drawnode, nodrawnode } solidstate_t;
#define MAX_BMODEL_VERTS 500 // 6K
#define MAX_BMODEL_EDGES 1000 // 12K
static mvertex_t *pbverts;
static bedge_t *pbedges;
static int numbverts, numbedges;
static mvertex_t *pfrontenter, *pfrontexit;
static qboolean makeclippededge;
//===========================================================================
/*
================
R_EntityRotate
================
*/
void
R_EntityRotate(vec3_t vec)
{
vec3_t tvec;
VectorCopy(vec, tvec);
vec[0] = DotProduct(entity_rotation[0], tvec);
vec[1] = DotProduct(entity_rotation[1], tvec);
vec[2] = DotProduct(entity_rotation[2], tvec);
}
/*
================
R_RotateBmodel
================
*/
void
R_RotateBmodel(void)
{
float angle, s, c, temp1[3][3], temp2[3][3], temp3[3][3];
// TODO: should use a look-up table
// TODO: should really be stored with the entity instead of being reconstructed
// TODO: could cache lazily, stored in the entity
// TODO: share work with R_SetUpAliasTransform
// yaw
angle = currententity->angles[YAW];
angle = angle * M_PI * 2 / 360;
s = sin(angle);
c = cos(angle);
temp1[0][0] = c;
temp1[0][1] = s;
temp1[0][2] = 0;
temp1[1][0] = -s;
temp1[1][1] = c;
temp1[1][2] = 0;
temp1[2][0] = 0;
temp1[2][1] = 0;
temp1[2][2] = 1;
// pitch
angle = currententity->angles[PITCH];
angle = angle * M_PI * 2 / 360;
s = sin(angle);
c = cos(angle);
temp2[0][0] = c;
temp2[0][1] = 0;
temp2[0][2] = -s;
temp2[1][0] = 0;
temp2[1][1] = 1;
temp2[1][2] = 0;
temp2[2][0] = s;
temp2[2][1] = 0;
temp2[2][2] = c;
R_ConcatRotations(temp2, temp1, temp3);
// roll
angle = currententity->angles[ROLL];
angle = angle * M_PI * 2 / 360;
s = sin(angle);
c = cos(angle);
temp1[0][0] = 1;
temp1[0][1] = 0;
temp1[0][2] = 0;
temp1[1][0] = 0;
temp1[1][1] = c;
temp1[1][2] = s;
temp1[2][0] = 0;
temp1[2][1] = -s;
temp1[2][2] = c;
R_ConcatRotations(temp1, temp3, entity_rotation);
//
// rotate modelorg and the transformation matrix
//
R_EntityRotate(modelorg);
R_EntityRotate(vpn);
R_EntityRotate(vright);
R_EntityRotate(vup);
R_TransformFrustum();
}
/*
================
R_RecursiveClipBPoly
================
*/
void
R_RecursiveClipBPoly(bedge_t *pedges, mnode_t *pnode, msurface_t *psurf)
{
bedge_t *psideedges[2], *pnextedge, *ptedge;
int i, side, lastside;
float dist, frac, lastdist;
mplane_t *splitplane, tplane;
mvertex_t *pvert, *plastvert, *ptvert;
mnode_t *pn;
psideedges[0] = psideedges[1] = NULL;
makeclippededge = false;
// transform the BSP plane into model space
// FIXME: cache these?
splitplane = pnode->plane;
tplane.dist = splitplane->dist -
DotProduct(r_entorigin, splitplane->normal);
tplane.normal[0] = DotProduct(entity_rotation[0], splitplane->normal);
tplane.normal[1] = DotProduct(entity_rotation[1], splitplane->normal);
tplane.normal[2] = DotProduct(entity_rotation[2], splitplane->normal);
// clip edges to BSP plane
for (; pedges; pedges = pnextedge) {
pnextedge = pedges->pnext;
// set the status for the last point as the previous point
// FIXME: cache this stuff somehow?
plastvert = pedges->v[0];
lastdist = DotProduct(plastvert->position, tplane.normal) -
tplane.dist;
if (lastdist > 0)
lastside = 0;
else
lastside = 1;
pvert = pedges->v[1];
dist = DotProduct(pvert->position, tplane.normal) - tplane.dist;
if (dist > 0)
side = 0;
else
side = 1;
if (side != lastside) {
// clipped
if (numbverts >= MAX_BMODEL_VERTS)
return;
// generate the clipped vertex
frac = lastdist / (lastdist - dist);
ptvert = &pbverts[numbverts++];
ptvert->position[0] = plastvert->position[0] +
frac * (pvert->position[0] - plastvert->position[0]);
ptvert->position[1] = plastvert->position[1] +
frac * (pvert->position[1] - plastvert->position[1]);
ptvert->position[2] = plastvert->position[2] +
frac * (pvert->position[2] - plastvert->position[2]);
// split into two edges, one on each side, and remember entering
// and exiting points
// FIXME: share the clip edge by having a winding direction flag?
if (numbedges >= (MAX_BMODEL_EDGES - 1)) {
Con_Printf("Out of edges for bmodel\n");
return;
}
ptedge = &pbedges[numbedges];
ptedge->pnext = psideedges[lastside];
psideedges[lastside] = ptedge;
ptedge->v[0] = plastvert;
ptedge->v[1] = ptvert;
ptedge = &pbedges[numbedges + 1];
ptedge->pnext = psideedges[side];
psideedges[side] = ptedge;
ptedge->v[0] = ptvert;
ptedge->v[1] = pvert;
numbedges += 2;
if (side == 0) {
// entering for front, exiting for back
pfrontenter = ptvert;
makeclippededge = true;
} else {
pfrontexit = ptvert;
makeclippededge = true;
}
} else {
// add the edge to the appropriate side
pedges->pnext = psideedges[side];
psideedges[side] = pedges;
}
}
// if anything was clipped, reconstitute and add the edges along the clip
// plane to both sides (but in opposite directions)
if (makeclippededge) {
if (numbedges >= (MAX_BMODEL_EDGES - 2)) {
Con_Printf("Out of edges for bmodel\n");
return;
}
ptedge = &pbedges[numbedges];
ptedge->pnext = psideedges[0];
psideedges[0] = ptedge;
ptedge->v[0] = pfrontexit;
ptedge->v[1] = pfrontenter;
ptedge = &pbedges[numbedges + 1];
ptedge->pnext = psideedges[1];
psideedges[1] = ptedge;
ptedge->v[0] = pfrontenter;
ptedge->v[1] = pfrontexit;
numbedges += 2;
}
// draw or recurse further
for (i = 0; i < 2; i++) {
if (psideedges[i]) {
/*
* draw if we've reached a non-solid leaf, done if all that's left
* is a solid leaf, and continue down the tree if it's not a leaf
*/
pn = pnode->children[i];
// we're done with this branch if the node or leaf isn't in the PVS
if (pn->visframe == r_visframecount) {
if (pn->contents < 0) {
if (pn->contents != CONTENTS_SOLID) {
r_currentbkey = ((mleaf_t *)pn)->key;
R_RenderBmodelFace(psideedges[i], psurf);
}
} else {
R_RecursiveClipBPoly(psideedges[i], pnode->children[i],
psurf);
}
}
}
}
}
/*
================
R_DrawSolidClippedSubmodelPolygons
================
*/
void
R_DrawSolidClippedSubmodelPolygons(model_t *pmodel)
{
int i, j, lindex;
vec_t dot;
msurface_t *psurf;
int numsurfaces;
mplane_t *pplane;
mvertex_t bverts[MAX_BMODEL_VERTS];
bedge_t bedges[MAX_BMODEL_EDGES], *pbedge;
medge_t *pedge, *pedges;
// FIXME: use bounding-box-based frustum clipping info?
psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
numsurfaces = pmodel->nummodelsurfaces;
pedges = pmodel->edges;
for (i = 0; i < numsurfaces; i++, psurf++) {
// find which side of the node we are on
pplane = psurf->plane;
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
// draw the polygon
if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) {
// FIXME: use bounding-box-based frustum clipping info?
// copy the edges to bedges, flipping if necessary so always
// clockwise winding
/*
* FIXME: if edges and vertices get caches, these assignments must
* move outside the loop, and overflow checking must be done here
*/
pbverts = bverts;
pbedges = bedges;
numbverts = numbedges = 0;
if (psurf->numedges > 0) {
pbedge = &bedges[numbedges];
numbedges += psurf->numedges;
for (j = 0; j < psurf->numedges; j++) {
lindex = pmodel->surfedges[psurf->firstedge + j];
if (lindex > 0) {
pedge = &pedges[lindex];
pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]];
pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]];
} else {
lindex = -lindex;
pedge = &pedges[lindex];
pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]];
pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]];
}
pbedge[j].pnext = &pbedge[j + 1];
}
pbedge[j - 1].pnext = NULL; // mark end of edges
R_RecursiveClipBPoly(pbedge, currententity->topnode, psurf);
} else {
Sys_Error("no edges in bmodel");
}
}
}
}
/*
================
R_DrawSubmodelPolygons
================
*/
void
R_DrawSubmodelPolygons(model_t *pmodel, int clipflags)
{
int i;
vec_t dot;
msurface_t *psurf;
int numsurfaces;
mplane_t *pplane;
// FIXME: use bounding-box-based frustum clipping info?
psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
numsurfaces = pmodel->nummodelsurfaces;
for (i = 0; i < numsurfaces; i++, psurf++) {
// find which side of the node we are on
pplane = psurf->plane;
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
// draw the polygon
if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) {
r_currentkey = ((mleaf_t *)currententity->topnode)->key;
// FIXME: use bounding-box-based frustum clipping info?
R_RenderFace(psurf, clipflags);
}
}
}
/*
================
R_RecursiveWorldNode
================
*/
static void
R_RecursiveWorldNode(mnode_t *node, int clipflags)
{
int i, c, side, *pindex;
vec3_t acceptpt, rejectpt;
mplane_t *plane;
msurface_t *surf, **mark;
mleaf_t *pleaf;
double d, dot;
if (node->contents == CONTENTS_SOLID)
return; // solid
if (node->visframe != r_visframecount)
return;
// cull the clipping planes if not trivial accept
// FIXME: the compiler is doing a lousy job of optimizing here; it could be
// twice as fast in ASM
if (clipflags) {
for (i = 0; i < 4; i++) {
if (!(clipflags & (1 << i)))
continue; // don't need to clip against it
// generate accept and reject points
// FIXME: do with fast look-ups or integer tests based on the sign
// bit of the floating point values
pindex = pfrustum_indexes[i];
rejectpt[0] = (float)node->minmaxs[pindex[0]];
rejectpt[1] = (float)node->minmaxs[pindex[1]];
rejectpt[2] = (float)node->minmaxs[pindex[2]];
d = DotProduct(rejectpt, view_clipplanes[i].normal);
d -= view_clipplanes[i].dist;
if (d <= 0)
return;
acceptpt[0] = (float)node->minmaxs[pindex[3 + 0]];
acceptpt[1] = (float)node->minmaxs[pindex[3 + 1]];
acceptpt[2] = (float)node->minmaxs[pindex[3 + 2]];
d = DotProduct(acceptpt, view_clipplanes[i].normal);
d -= view_clipplanes[i].dist;
if (d >= 0)
clipflags &= ~(1 << i); // node is entirely on screen
}
}
// if a leaf node, draw stuff
if (node->contents < 0) {
pleaf = (mleaf_t *)node;
mark = pleaf->firstmarksurface;
c = pleaf->nummarksurfaces;
if (c) {
do {
(*mark)->visframe = r_framecount;
mark++;
} while (--c);
}
// deal with model fragments in this leaf
if (pleaf->efrags) {
R_StoreEfrags(&pleaf->efrags);
}
pleaf->key = r_currentkey;
r_currentkey++; // all bmodels in a leaf share the same key
} else {
// node is just a decision point, so go down the apropriate sides
// find which side of the node we are on
plane = node->plane;
switch (plane->type) {
case PLANE_X:
dot = modelorg[0] - plane->dist;
break;
case PLANE_Y:
dot = modelorg[1] - plane->dist;
break;
case PLANE_Z:
dot = modelorg[2] - plane->dist;
break;
default:
dot = DotProduct(modelorg, plane->normal) - plane->dist;
break;
}
if (dot >= 0)
side = 0;
else
side = 1;
// recurse down the children, front side first
R_RecursiveWorldNode(node->children[side], clipflags);
// draw stuff
c = node->numsurfaces;
if (c) {
surf = cl.worldmodel->surfaces + node->firstsurface;
if (dot < -BACKFACE_EPSILON) {
do {
if ((surf->flags & SURF_PLANEBACK) &&
(surf->visframe == r_framecount)) {
if (r_drawpolys) {
if (r_worldpolysbacktofront) {
if (numbtofpolys < MAX_BTOFPOLYS) {
pbtofpolys[numbtofpolys].clipflags =
clipflags;
pbtofpolys[numbtofpolys].psurf = surf;
numbtofpolys++;
}
} else {
R_RenderPoly(surf, clipflags);
}
} else {
R_RenderFace(surf, clipflags);
}
}
surf++;
} while (--c);
} else if (dot > BACKFACE_EPSILON) {
do {
if (!(surf->flags & SURF_PLANEBACK) &&
(surf->visframe == r_framecount)) {
if (r_drawpolys) {
if (r_worldpolysbacktofront) {
if (numbtofpolys < MAX_BTOFPOLYS) {
pbtofpolys[numbtofpolys].clipflags =
clipflags;
pbtofpolys[numbtofpolys].psurf = surf;
numbtofpolys++;
}
} else {
R_RenderPoly(surf, clipflags);
}
} else {
R_RenderFace(surf, clipflags);
}
}
surf++;
} while (--c);
}
// all surfaces on the same node share the same sequence number
r_currentkey++;
}
// recurse down the back side
R_RecursiveWorldNode(node->children[!side], clipflags);
}
}
/*
================
R_RenderWorld
================
*/
void
R_RenderWorld(void)
{
int i;
model_t *clmodel;
btofpoly_t btofpolys[MAX_BTOFPOLYS];
pbtofpolys = btofpolys;
currententity = &cl_entities[0];
VectorCopy(r_origin, modelorg);
clmodel = currententity->model;
r_pcurrentvertbase = clmodel->vertexes;
R_RecursiveWorldNode(clmodel->nodes, 15);
// if the driver wants the polygons back to front, play the visible ones back
// in that order
if (r_worldpolysbacktofront) {
for (i = numbtofpolys - 1; i >= 0; i--) {
R_RenderPoly(btofpolys[i].psurf, btofpolys[i].clipflags);
}
}
}

1050
NQ/r_main.c Normal file

File diff suppressed because it is too large Load Diff

507
NQ/r_misc.c Normal file
View File

@ -0,0 +1,507 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_misc.c
#include "console.h"
#include "host.h"
#include "quakedef.h"
#include "r_local.h"
#include "render.h"
#include "sbar.h"
#include "screen.h"
#include "server.h"
#include "sys.h"
#include "view.h"
/*
===============
R_CheckVariables
===============
*/
static void
R_CheckVariables(void)
{
// FIXME - do it right (cvar callback)
static float oldbright;
if (r_fullbright.value != oldbright) {
oldbright = r_fullbright.value;
D_FlushCaches(); // so all lighting changes
}
}
/*
============
Show
Debugging use
============
*/
void
Show(void)
{
vrect_t vr;
vr.x = vr.y = 0;
vr.width = vid.width;
vr.height = vid.height;
vr.pnext = NULL;
VID_Update(&vr);
}
/*
====================
R_TimeRefresh_f
For program optimization
====================
*/
void
R_TimeRefresh_f(void)
{
int i;
float start, stop, time;
int startangle;
vrect_t vr;
startangle = r_refdef.viewangles[1];
start = Sys_DoubleTime();
for (i = 0; i < 128; i++) {
r_refdef.viewangles[1] = i / 128.0 * 360.0;
VID_LockBuffer();
R_RenderView();
VID_UnlockBuffer();
vr.x = r_refdef.vrect.x;
vr.y = r_refdef.vrect.y;
vr.width = r_refdef.vrect.width;
vr.height = r_refdef.vrect.height;
vr.pnext = NULL;
VID_Update(&vr);
}
stop = Sys_DoubleTime();
time = stop - start;
Con_Printf("%f seconds (%f fps)\n", time, 128 / time);
r_refdef.viewangles[1] = startangle;
}
/*
================
R_LineGraph
Only called by R_DisplayTime
================
*/
void
R_LineGraph(int x, int y, int h)
{
int i;
byte *dest;
int s;
// FIXME: should be disabled on no-buffer adapters, or should be in the driver
x += r_refdef.vrect.x;
y += r_refdef.vrect.y;
dest = vid.buffer + vid.rowbytes * y + x;
s = r_graphheight.value;
if (h > s)
h = s;
for (i = 0; i < h; i++, dest -= vid.rowbytes * 2) {
dest[0] = 0xff;
*(dest - vid.rowbytes) = 0x30;
}
for (; i < s; i++, dest -= vid.rowbytes * 2) {
dest[0] = 0x30;
*(dest - vid.rowbytes) = 0x30;
}
}
/*
==============
R_TimeGraph
Performance monitoring tool
==============
*/
#define MAX_TIMINGS 100
void
R_TimeGraph(void)
{
static int timex;
int a;
float r_time2;
static byte r_timings[MAX_TIMINGS];
int x;
r_time2 = Sys_DoubleTime();
a = (r_time2 - r_time1) / 0.01;
//a = fabs(mouse_y * 0.05);
//a = (int)((r_refdef.vieworg[2] + 1024)/1)%(int)r_graphheight.value;
//a = fabs(velocity[0])/20;
//a = ((int)fabs(origin[0])/8)%20;
//a = (cl.idealpitch + 30)/5;
r_timings[timex] = a;
a = timex;
if (r_refdef.vrect.width <= MAX_TIMINGS)
x = r_refdef.vrect.width - 1;
else
x = r_refdef.vrect.width - (r_refdef.vrect.width - MAX_TIMINGS) / 2;
do {
R_LineGraph(x, r_refdef.vrect.height - 2, r_timings[a]);
if (x == 0)
break; // screen too small to hold entire thing
x--;
a--;
if (a == -1)
a = MAX_TIMINGS - 1;
} while (a != timex);
timex = (timex + 1) % MAX_TIMINGS;
}
/*
=============
R_PrintTimes
=============
*/
void
R_PrintTimes(void)
{
float r_time2;
float ms;
r_time2 = Sys_DoubleTime();
ms = 1000 * (r_time2 - r_time1);
Con_Printf("%5.1f ms %3i/%3i/%3i poly %3i surf\n",
ms, c_faceclip, r_polycount, r_drawnpolycount, c_surf);
c_surf = 0;
}
/*
=============
R_PrintDSpeeds
=============
*/
void
R_PrintDSpeeds(void)
{
float ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, dv_time;
r_time2 = Sys_DoubleTime();
dp_time = (dp_time2 - dp_time1) * 1000;
rw_time = (rw_time2 - rw_time1) * 1000;
db_time = (db_time2 - db_time1) * 1000;
se_time = (se_time2 - se_time1) * 1000;
de_time = (de_time2 - de_time1) * 1000;
dv_time = (dv_time2 - dv_time1) * 1000;
ms = (r_time2 - r_time1) * 1000;
Con_Printf("%3i %4.1fp %3iw %4.1fb %3is %4.1fe %4.1fv\n",
(int)ms, dp_time, (int)rw_time, db_time, (int)se_time,
de_time, dv_time);
}
/*
=============
R_PrintAliasStats
=============
*/
void
R_PrintAliasStats(void)
{
Con_Printf("%3i polygon model drawn\n", r_amodels_drawn);
}
void
WarpPalette(void)
{
int i, j;
byte newpalette[768];
int basecolor[3];
basecolor[0] = 130;
basecolor[1] = 80;
basecolor[2] = 50;
// pull the colors halfway to bright brown
for (i = 0; i < 256; i++) {
for (j = 0; j < 3; j++) {
newpalette[i * 3 + j] =
(host_basepal[i * 3 + j] + basecolor[j]) / 2;
}
}
VID_ShiftPalette(newpalette);
}
/*
===================
R_TransformFrustum
===================
*/
void
R_TransformFrustum(void)
{
int i;
vec3_t v, v2;
if (r_lockfrustum.value)
return;
for (i = 0; i < 4; i++) {
v[0] = screenedge[i].normal[2];
v[1] = -screenedge[i].normal[0];
v[2] = screenedge[i].normal[1];
v2[0] = v[1] * vright[0] + v[2] * vup[0] + v[0] * vpn[0];
v2[1] = v[1] * vright[1] + v[2] * vup[1] + v[0] * vpn[1];
v2[2] = v[1] * vright[2] + v[2] * vup[2] + v[0] * vpn[2];
VectorCopy(v2, view_clipplanes[i].normal);
view_clipplanes[i].dist = DotProduct(modelorg, v2);
}
}
#if !id386
/*
================
TransformVector
================
*/
void
TransformVector(vec3_t in, vec3_t out)
{
out[0] = DotProduct(in, vright);
out[1] = DotProduct(in, vup);
out[2] = DotProduct(in, vpn);
}
#endif
/*
================
R_TransformPlane
================
*/
void
R_TransformPlane(mplane_t *p, float *normal, float *dist)
{
float d;
d = DotProduct(r_origin, p->normal);
*dist = p->dist - d;
// TODO: when we have rotating entities, this will need to use the view matrix
TransformVector(p->normal, normal);
}
/*
===============
R_SetUpFrustumIndexes
===============
*/
void
R_SetUpFrustumIndexes(void)
{
int i, j, *pindex;
pindex = r_frustum_indexes;
for (i = 0; i < 4; i++) {
for (j = 0; j < 3; j++) {
if (view_clipplanes[i].normal[j] < 0) {
pindex[j] = j;
pindex[j + 3] = j + 3;
} else {
pindex[j] = j + 3;
pindex[j + 3] = j;
}
}
// FIXME: do just once at start
pfrustum_indexes[i] = pindex;
pindex += 6;
}
}
/*
===============
R_SetupFrame
===============
*/
void
R_SetupFrame(void)
{
int edgecount;
vrect_t vrect;
float w, h;
// don't allow cheats in multiplayer
if (cl.maxclients > 1) {
Cvar_Set("r_draworder", "0");
Cvar_Set("r_fullbright", "0");
Cvar_Set("r_ambient", "0");
Cvar_Set("r_drawflat", "0");
}
if (r_numsurfs.value) {
if ((surface_p - surfaces) > r_maxsurfsseen)
r_maxsurfsseen = surface_p - surfaces;
Con_Printf("Used %d of %d surfs; %d max\n", surface_p - surfaces,
surf_max - surfaces, r_maxsurfsseen);
}
if (r_numedges.value) {
edgecount = edge_p - r_edges;
if (edgecount > r_maxedgesseen)
r_maxedgesseen = edgecount;
Con_Printf("Used %d of %d edges; %d max\n", edgecount,
r_numallocatededges, r_maxedgesseen);
}
r_refdef.ambientlight = r_ambient.value;
if (r_refdef.ambientlight < 0)
r_refdef.ambientlight = 0;
if (!sv.active)
r_draworder.value = 0; // don't let cheaters look behind walls
R_CheckVariables();
R_AnimateLight();
r_framecount++;
numbtofpolys = 0;
// debugging
#if 0
r_refdef.vieworg[0] = 80;
r_refdef.vieworg[1] = 64;
r_refdef.vieworg[2] = 40;
r_refdef.viewangles[0] = 0;
r_refdef.viewangles[1] = 46.763641357;
r_refdef.viewangles[2] = 0;
#endif
// build the transformation matrix for the given view angles
VectorCopy(r_refdef.vieworg, modelorg);
VectorCopy(r_refdef.vieworg, r_origin);
AngleVectors(r_refdef.viewangles, vpn, vright, vup);
// current viewleaf
r_oldviewleaf = r_viewleaf;
r_viewleaf = Mod_PointInLeaf(r_origin, cl.worldmodel);
r_dowarpold = r_dowarp;
r_dowarp = r_waterwarp.value && (r_viewleaf->contents <= CONTENTS_WATER);
if ((r_dowarp != r_dowarpold) || r_viewchanged || lcd_x.value) {
if (r_dowarp) {
if ((vid.width <= vid.maxwarpwidth) &&
(vid.height <= vid.maxwarpheight)) {
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height;
R_ViewChanged(&vrect, sb_lines, vid.aspect);
} else {
w = vid.width;
h = vid.height;
if (w > vid.maxwarpwidth) {
h *= (float)vid.maxwarpwidth / w;
w = vid.maxwarpwidth;
}
if (h > vid.maxwarpheight) {
h = vid.maxwarpheight;
w *= (float)vid.maxwarpheight / h;
}
vrect.x = 0;
vrect.y = 0;
vrect.width = (int)w;
vrect.height = (int)h;
R_ViewChanged(&vrect,
(int)((float)sb_lines *
(h / (float)vid.height)),
vid.aspect * (h / w) * ((float)vid.width /
(float)vid.height));
}
} else {
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height;
R_ViewChanged(&vrect, sb_lines, vid.aspect);
}
r_viewchanged = false;
}
// start off with just the four screen edge clip planes
R_TransformFrustum();
// save base values
VectorCopy(vpn, base_vpn);
VectorCopy(vright, base_vright);
VectorCopy(vup, base_vup);
VectorCopy(modelorg, base_modelorg);
R_SetSkyFrame();
R_SetUpFrustumIndexes();
r_cache_thrash = false;
// clear frame counts
c_faceclip = 0;
r_polycount = 0;
r_drawnpolycount = 0;
r_amodels_drawn = 0;
r_outofsurfaces = 0;
r_outofedges = 0;
D_SetupFrame();
}

742
NQ/r_part.c Normal file
View File

@ -0,0 +1,742 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "console.h"
#include "quakedef.h"
#include "server.h"
#ifdef GLQUAKE
#include "glquake.h"
#else
#include "r_local.h"
#endif
#define MAX_PARTICLES 2048 // default max # of particles at one
// time
#define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's
// on the command line
int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 };
int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 };
int ramp3[8] = { 0x6d, 0x6b, 6, 5, 4, 3 };
particle_t *active_particles, *free_particles;
particle_t *particles;
int r_numparticles;
vec3_t r_pright, r_pup, r_ppn;
/*
===============
R_InitParticles
===============
*/
void
R_InitParticles(void)
{
int i;
i = COM_CheckParm("-particles");
if (i) {
r_numparticles = (int)(Q_atoi(com_argv[i + 1]));
if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
r_numparticles = ABSOLUTE_MIN_PARTICLES;
} else {
r_numparticles = MAX_PARTICLES;
}
particles = (particle_t *)
Hunk_AllocName(r_numparticles * sizeof(particle_t), "particles");
}
/*
===============
R_EntityParticles
===============
*/
#define NUMVERTEXNORMALS 162
vec3_t avelocities[NUMVERTEXNORMALS];
float beamlength = 16;
vec3_t avelocity = { 23, 7, 3 };
float partstep = 0.01;
float timescale = 0.01;
void
R_EntityParticles(entity_t *ent)
{
int count;
int i;
particle_t *p;
float angle;
float sr, sp, sy, cr, cp, cy;
vec3_t forward;
float dist;
dist = 64;
count = 50;
if (!avelocities[0][0]) {
for (i = 0; i < NUMVERTEXNORMALS * 3; i++)
avelocities[0][i] = (rand() & 255) * 0.01;
}
for (i = 0; i < NUMVERTEXNORMALS; i++) {
angle = cl.time * avelocities[i][0];
sy = sin(angle);
cy = cos(angle);
angle = cl.time * avelocities[i][1];
sp = sin(angle);
cp = cos(angle);
angle = cl.time * avelocities[i][2];
sr = sin(angle);
cr = cos(angle);
forward[0] = cp * cy;
forward[1] = cp * sy;
forward[2] = -sp;
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = cl.time + 0.01;
p->color = 0x6f;
p->type = pt_explode;
p->org[0] =
ent->origin[0] + r_avertexnormals[i][0] * dist +
forward[0] * beamlength;
p->org[1] =
ent->origin[1] + r_avertexnormals[i][1] * dist +
forward[1] * beamlength;
p->org[2] =
ent->origin[2] + r_avertexnormals[i][2] * dist +
forward[2] * beamlength;
}
}
/*
===============
R_ClearParticles
===============
*/
void
R_ClearParticles(void)
{
int i;
free_particles = &particles[0];
active_particles = NULL;
for (i = 0; i < r_numparticles; i++)
particles[i].next = &particles[i + 1];
particles[r_numparticles - 1].next = NULL;
}
void
R_ReadPointFile_f(void)
{
FILE *f;
vec3_t org;
int r;
int c;
particle_t *p;
char name[MAX_OSPATH];
sprintf(name, "maps/%s.pts", sv.name);
COM_FOpenFile(name, &f);
if (!f) {
Con_Printf("couldn't open %s\n", name);
return;
}
Con_Printf("Reading %s...\n", name);
c = 0;
for (;;) {
r = fscanf(f, "%f %f %f\n", &org[0], &org[1], &org[2]);
if (r != 3)
break;
c++;
if (!free_particles) {
Con_Printf("Not enough free particles\n");
break;
}
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = 99999;
p->color = (-c) & 15;
p->type = pt_static;
VectorCopy(vec3_origin, p->vel);
VectorCopy(org, p->org);
}
fclose(f);
Con_Printf("%i points read\n", c);
}
/*
===============
R_ParseParticleEffect
Parse an effect out of the server message
===============
*/
void
R_ParseParticleEffect(void)
{
vec3_t org, dir;
int i, count, msgcount, color;
for (i = 0; i < 3; i++)
org[i] = MSG_ReadCoord();
for (i = 0; i < 3; i++)
dir[i] = MSG_ReadChar() * (1.0 / 16);
msgcount = MSG_ReadByte();
color = MSG_ReadByte();
if (msgcount == 255)
count = 1024;
else
count = msgcount;
R_RunParticleEffect(org, dir, color, count);
}
/*
===============
R_ParticleExplosion
===============
*/
void
R_ParticleExplosion(vec3_t org)
{
int i, j;
particle_t *p;
for (i = 0; i < 1024; i++) {
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = cl.time + 5;
p->color = ramp1[0];
p->ramp = rand() & 3;
if (i & 1) {
p->type = pt_explode;
for (j = 0; j < 3; j++) {
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
} else {
p->type = pt_explode2;
for (j = 0; j < 3; j++) {
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
}
/*
===============
R_ParticleExplosion2
===============
*/
void
R_ParticleExplosion2(vec3_t org, int colorStart, int colorLength)
{
int i, j;
particle_t *p;
int colorMod = 0;
for (i = 0; i < 512; i++) {
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = cl.time + 0.3;
p->color = colorStart + (colorMod % colorLength);
colorMod++;
p->type = pt_blob;
for (j = 0; j < 3; j++) {
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
/*
===============
R_BlobExplosion
===============
*/
void
R_BlobExplosion(vec3_t org)
{
int i, j;
particle_t *p;
for (i = 0; i < 1024; i++) {
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = cl.time + 1 + (rand() & 8) * 0.05;
if (i & 1) {
p->type = pt_blob;
p->color = 66 + rand() % 6;
for (j = 0; j < 3; j++) {
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
} else {
p->type = pt_blob2;
p->color = 150 + rand() % 6;
for (j = 0; j < 3; j++) {
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
}
/*
===============
R_RunParticleEffect
===============
*/
void
R_RunParticleEffect(vec3_t org, vec3_t dir, int color, int count)
{
int i, j;
particle_t *p;
for (i = 0; i < count; i++) {
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
if (count == 1024) { // rocket explosion
p->die = cl.time + 5;
p->color = ramp1[0];
p->ramp = rand() & 3;
if (i & 1) {
p->type = pt_explode;
for (j = 0; j < 3; j++) {
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
} else {
p->type = pt_explode2;
for (j = 0; j < 3; j++) {
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
} else {
p->die = cl.time + 0.1 * (rand() % 5);
p->color = (color & ~7) + (rand() & 7);
p->type = pt_slowgrav;
for (j = 0; j < 3; j++) {
p->org[j] = org[j] + ((rand() & 15) - 8);
p->vel[j] = dir[j] * 15; // + (rand()%300)-150;
}
}
}
}
/*
===============
R_LavaSplash
===============
*/
void
R_LavaSplash(vec3_t org)
{
int i, j, k;
particle_t *p;
float vel;
vec3_t dir;
for (i = -16; i < 16; i++)
for (j = -16; j < 16; j++)
for (k = 0; k < 1; k++) {
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = cl.time + 2 + (rand() & 31) * 0.02;
p->color = 224 + (rand() & 7);
p->type = pt_slowgrav;
dir[0] = j * 8 + (rand() & 7);
dir[1] = i * 8 + (rand() & 7);
dir[2] = 256;
p->org[0] = org[0] + dir[0];
p->org[1] = org[1] + dir[1];
p->org[2] = org[2] + (rand() & 63);
VectorNormalize(dir);
vel = 50 + (rand() & 63);
VectorScale(dir, vel, p->vel);
}
}
/*
===============
R_TeleportSplash
===============
*/
void
R_TeleportSplash(vec3_t org)
{
int i, j, k;
particle_t *p;
float vel;
vec3_t dir;
for (i = -16; i < 16; i += 4)
for (j = -16; j < 16; j += 4)
for (k = -24; k < 32; k += 4) {
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = cl.time + 0.2 + (rand() & 7) * 0.02;
p->color = 7 + (rand() & 7);
p->type = pt_slowgrav;
dir[0] = j * 8;
dir[1] = i * 8;
dir[2] = k * 8;
p->org[0] = org[0] + i + (rand() & 3);
p->org[1] = org[1] + j + (rand() & 3);
p->org[2] = org[2] + k + (rand() & 3);
VectorNormalize(dir);
vel = 50 + (rand() & 63);
VectorScale(dir, vel, p->vel);
}
}
void
R_RocketTrail(vec3_t start, vec3_t end, int type)
{
vec3_t vec;
float len;
int j;
particle_t *p;
int dec;
static int tracercount;
VectorSubtract(end, start, vec);
len = VectorNormalize(vec);
if (type < 128)
dec = 3;
else {
dec = 1;
type -= 128;
}
while (len > 0) {
len -= dec;
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
VectorCopy(vec3_origin, p->vel);
p->die = cl.time + 2;
switch (type) {
case 0: // rocket trail
p->ramp = (rand() & 3);
p->color = ramp3[(int)p->ramp];
p->type = pt_fire;
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 1: // smoke smoke
p->ramp = (rand() & 3) + 2;
p->color = ramp3[(int)p->ramp];
p->type = pt_fire;
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 2: // blood
p->type = pt_grav;
p->color = 67 + (rand() & 3);
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 3:
case 5: // tracer
p->die = cl.time + 0.5;
p->type = pt_static;
if (type == 3)
p->color = 52 + ((tracercount & 4) << 1);
else
p->color = 230 + ((tracercount & 4) << 1);
tracercount++;
VectorCopy(start, p->org);
if (tracercount & 1) {
p->vel[0] = 30 * vec[1];
p->vel[1] = 30 * -vec[0];
} else {
p->vel[0] = 30 * -vec[1];
p->vel[1] = 30 * vec[0];
}
break;
case 4: // slight blood
p->type = pt_grav;
p->color = 67 + (rand() & 3);
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() % 6) - 3);
len -= 3;
break;
case 6: // voor trail
p->color = 9 * 16 + 8 + (rand() & 3);
p->type = pt_static;
p->die = cl.time + 0.3;
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() & 15) - 8);
break;
}
VectorAdd(start, vec, start);
}
}
/*
===============
R_DrawParticles
===============
*/
void
R_DrawParticles(void)
{
particle_t *p, *kill;
float grav;
int i;
float time2, time3;
float time1;
float dvel;
float frametime;
#ifdef GLQUAKE
vec3_t up, right;
float scale;
/*
* FIXME - shouldn't need to do this, just get the caller to make sure
* multitexture is not enabled.
*/
GL_DisableMultitexture();
GL_Bind(particletexture);
glEnable(GL_BLEND);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBegin(GL_TRIANGLES);
VectorScale(vup, 1.5, up);
VectorScale(vright, 1.5, right);
#else
D_StartParticles();
VectorScale(vright, xscaleshrink, r_pright);
VectorScale(vup, yscaleshrink, r_pup);
VectorCopy(vpn, r_ppn);
#endif
frametime = cl.time - cl.oldtime;
time3 = frametime * 15;
time2 = frametime * 10; // 15;
time1 = frametime * 5;
grav = frametime * sv_gravity.value * 0.05;
dvel = 4 * frametime;
for (;;) {
kill = active_particles;
if (kill && kill->die < cl.time) {
active_particles = kill->next;
kill->next = free_particles;
free_particles = kill;
continue;
}
break;
}
for (p = active_particles; p; p = p->next) {
for (;;) {
kill = p->next;
if (kill && kill->die < cl.time) {
p->next = kill->next;
kill->next = free_particles;
free_particles = kill;
continue;
}
break;
}
#ifdef GLQUAKE
// hack a scale up to keep particles from disapearing
scale =
(p->org[0] - r_origin[0]) * vpn[0] + (p->org[1] -
r_origin[1]) * vpn[1]
+ (p->org[2] - r_origin[2]) * vpn[2];
if (scale < 20)
scale = 1;
else
scale = 1 + scale * 0.004;
glColor3ubv((byte *)&d_8to24table[(int)p->color]);
glTexCoord2f(0, 0);
glVertex3fv(p->org);
glTexCoord2f(1, 0);
glVertex3f(p->org[0] + up[0] * scale, p->org[1] + up[1] * scale,
p->org[2] + up[2] * scale);
glTexCoord2f(0, 1);
glVertex3f(p->org[0] + right[0] * scale,
p->org[1] + right[1] * scale,
p->org[2] + right[2] * scale);
#else
D_DrawParticle(p);
#endif
p->org[0] += p->vel[0] * frametime;
p->org[1] += p->vel[1] * frametime;
p->org[2] += p->vel[2] * frametime;
switch (p->type) {
case pt_static:
break;
case pt_fire:
p->ramp += time1;
if (p->ramp >= 6)
p->die = -1;
else
p->color = ramp3[(int)p->ramp];
p->vel[2] += grav;
break;
case pt_explode:
p->ramp += time2;
if (p->ramp >= 8)
p->die = -1;
else
p->color = ramp1[(int)p->ramp];
for (i = 0; i < 3; i++)
p->vel[i] += p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_explode2:
p->ramp += time3;
if (p->ramp >= 8)
p->die = -1;
else
p->color = ramp2[(int)p->ramp];
for (i = 0; i < 3; i++)
p->vel[i] -= p->vel[i] * frametime;
p->vel[2] -= grav;
break;
case pt_blob:
for (i = 0; i < 3; i++)
p->vel[i] += p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_blob2:
for (i = 0; i < 2; i++)
p->vel[i] -= p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_grav:
case pt_slowgrav:
p->vel[2] -= grav;
break;
}
}
#ifdef GLQUAKE
glEnd();
glDisable(GL_BLEND);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
#else
D_EndParticles();
#endif
}

158
NQ/r_shared.h Normal file
View File

@ -0,0 +1,158 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef R_SHARED_H
#define R_SHARED_H
#ifndef GLQUAKE
// r_shared.h: general refresh-related stuff shared between the refresh and the
// driver
#include "d_iface.h" /* polyvert_t */
#include "mathlib.h" /* vec3_t */
// FIXME: clean up and move into d_iface.h
#define MAXVERTS 16 // max points in a surface polygon
#define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate
// polygon (while processing)
// !!! if this is changed, it must be changed in d_ifacea.h too !!!
#define MAXHEIGHT 1024
#define MAXWIDTH 1280
#define INFINITE_DISTANCE 0x10000 // distance that's always guaranteed to
// be farther away than anything in
// the scene
//===================================================================
extern void R_DrawLine(polyvert_t *polyvert0, polyvert_t *polyvert1);
extern int cachewidth;
extern pixel_t *cacheblock;
extern int screenwidth;
extern float pixelAspect;
extern int r_drawnpolycount;
extern cvar_t r_clearcolor;
#define TURB_TABLE_SIZE (2*TURB_CYCLE)
extern int sintable[TURB_TABLE_SIZE];
extern int intsintable[TURB_TABLE_SIZE];
extern vec3_t vup, base_vup;
extern vec3_t vpn, base_vpn;
extern vec3_t vright, base_vright;
extern entity_t *currententity;
// FIXME - reasoning behind number choice?
#define NUMSTACKEDGES 3000
#define MINEDGES NUMSTACKEDGES
#define NUMSTACKSURFACES 1500
#define MINSURFACES NUMSTACKSURFACES
#define MAXSPANS 3000
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct espan_s {
int u, v, count;
struct espan_s *pnext;
} espan_t;
// FIXME: compress, make a union if that will help
// insubmodel is only 1, flags is fewer than 32, spanstate could be a byte
typedef struct surf_s {
struct surf_s *next; // active surface stack in r_edge.c
struct surf_s *prev; // used in r_edge.c for active surf stack
struct espan_s *spans; // pointer to linked list of spans to draw
int key; // sorting key (BSP order)
int last_u; // set during tracing
int spanstate; // 0 = not in span
// 1 = in span
// -1 = in inverted span (end before
// start)
int flags; // currentface flags
void *data; // associated data like msurface_t
entity_t *entity;
float nearzi; // nearest 1/z on surface, for mipmapping
qboolean insubmodel;
float d_ziorigin, d_zistepu, d_zistepv;
int pad[2]; // to 64 bytes
} surf_t;
extern surf_t *surfaces, *surface_p, *surf_max;
// surfaces are generated in back to front order by the bsp, so if a surf
// pointer is greater than another one, it should be drawn in front
// surfaces[1] is the background, and is used as the active surface stack.
// surfaces[0] is a dummy, because index 0 is used to indicate no surface
// attached to an edge_t
//===================================================================
extern vec3_t sxformaxis[4]; // s axis transformed into viewspace
extern vec3_t txformaxis[4]; // t axis transformed into viewspac
extern vec3_t modelorg, base_modelorg;
extern float xcenter, ycenter;
extern float xscale, yscale;
extern float xscaleinv, yscaleinv;
extern float xscaleshrink, yscaleshrink;
extern int d_lightstylevalue[256]; // 8.8 frac of base light value
extern void TransformVector(vec3_t in, vec3_t out);
extern void SetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv,
fixed8_t endvertu, fixed8_t endvertv);
extern int r_skymade;
extern void R_MakeSky(void);
extern int ubasestep, errorterm, erroradjustup, erroradjustdown;
// flags in finalvert_t.flags
#define ALIAS_LEFT_CLIP 0x0001
#define ALIAS_TOP_CLIP 0x0002
#define ALIAS_RIGHT_CLIP 0x0004
#define ALIAS_BOTTOM_CLIP 0x0008
#define ALIAS_Z_CLIP 0x0010
// !!! if this is changed, it must be changed in d_ifacea.h too !!!
#define ALIAS_ONSEAM 0x0020 // also defined in modelgen.h;
// must be kept in sync
#define ALIAS_XY_CLIP_MASK 0x000F
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct edge_s {
fixed16_t u;
fixed16_t u_step;
struct edge_s *prev, *next;
unsigned short surfs[2];
struct edge_s *nextremove;
float nearzi;
medge_t *owner;
} edge_t;
#endif // !GLQUAKE
#endif /* R_SHARED_H */

381
NQ/r_sprite.c Normal file
View File

@ -0,0 +1,381 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_sprite.c
#include "console.h"
#include "quakedef.h"
#include "r_local.h"
#include "sys.h"
static int clip_current;
static vec5_t clip_verts[2][MAXWORKINGVERTS];
static int sprite_width, sprite_height;
spritedesc_t r_spritedesc;
/*
================
R_RotateSprite
================
*/
void
R_RotateSprite(float beamlength)
{
vec3_t vec;
if (beamlength == 0.0)
return;
VectorScale(r_spritedesc.vpn, -beamlength, vec);
VectorAdd(r_entorigin, vec, r_entorigin);
VectorSubtract(modelorg, vec, modelorg);
}
/*
=============
R_ClipSpriteFace
Clips the winding at clip_verts[clip_current] and changes clip_current
Throws out the back side
==============
*/
int
R_ClipSpriteFace(int nump, clipplane_t *pclipplane)
{
int i, outcount;
float dists[MAXWORKINGVERTS + 1];
float frac, clipdist, *pclipnormal;
float *in, *instep, *outstep, *vert2;
clipdist = pclipplane->dist;
pclipnormal = pclipplane->normal;
// calc dists
if (clip_current) {
in = clip_verts[1][0];
outstep = clip_verts[0][0];
clip_current = 0;
} else {
in = clip_verts[0][0];
outstep = clip_verts[1][0];
clip_current = 1;
}
instep = in;
for (i = 0; i < nump; i++, instep += sizeof(vec5_t) / sizeof(float)) {
dists[i] = DotProduct(instep, pclipnormal) - clipdist;
}
// handle wraparound case
dists[nump] = dists[0];
memcpy(instep, in, sizeof(vec5_t));
// clip the winding
instep = in;
outcount = 0;
for (i = 0; i < nump; i++, instep += sizeof(vec5_t) / sizeof(float)) {
if (dists[i] >= 0) {
memcpy(outstep, instep, sizeof(vec5_t));
outstep += sizeof(vec5_t) / sizeof(float);
outcount++;
}
if (dists[i] == 0 || dists[i + 1] == 0)
continue;
if ((dists[i] > 0) == (dists[i + 1] > 0))
continue;
// split it into a new vertex
frac = dists[i] / (dists[i] - dists[i + 1]);
vert2 = instep + sizeof(vec5_t) / sizeof(float);
outstep[0] = instep[0] + frac * (vert2[0] - instep[0]);
outstep[1] = instep[1] + frac * (vert2[1] - instep[1]);
outstep[2] = instep[2] + frac * (vert2[2] - instep[2]);
outstep[3] = instep[3] + frac * (vert2[3] - instep[3]);
outstep[4] = instep[4] + frac * (vert2[4] - instep[4]);
outstep += sizeof(vec5_t) / sizeof(float);
outcount++;
}
return outcount;
}
/*
================
R_SetupAndDrawSprite
================
*/
void
R_SetupAndDrawSprite()
{
int i, nump;
float dot, scale, *pv;
vec5_t *pverts;
vec3_t left, up, right, down, transformed, local;
emitpoint_t outverts[MAXWORKINGVERTS + 1], *pout;
dot = DotProduct(r_spritedesc.vpn, modelorg);
// backface cull
if (dot >= 0)
return;
// build the sprite poster in worldspace
VectorScale(r_spritedesc.vright, r_spritedesc.pspriteframe->right, right);
VectorScale(r_spritedesc.vup, r_spritedesc.pspriteframe->up, up);
VectorScale(r_spritedesc.vright, r_spritedesc.pspriteframe->left, left);
VectorScale(r_spritedesc.vup, r_spritedesc.pspriteframe->down, down);
pverts = clip_verts[0];
pverts[0][0] = r_entorigin[0] + up[0] + left[0];
pverts[0][1] = r_entorigin[1] + up[1] + left[1];
pverts[0][2] = r_entorigin[2] + up[2] + left[2];
pverts[0][3] = 0;
pverts[0][4] = 0;
pverts[1][0] = r_entorigin[0] + up[0] + right[0];
pverts[1][1] = r_entorigin[1] + up[1] + right[1];
pverts[1][2] = r_entorigin[2] + up[2] + right[2];
pverts[1][3] = sprite_width;
pverts[1][4] = 0;
pverts[2][0] = r_entorigin[0] + down[0] + right[0];
pverts[2][1] = r_entorigin[1] + down[1] + right[1];
pverts[2][2] = r_entorigin[2] + down[2] + right[2];
pverts[2][3] = sprite_width;
pverts[2][4] = sprite_height;
pverts[3][0] = r_entorigin[0] + down[0] + left[0];
pverts[3][1] = r_entorigin[1] + down[1] + left[1];
pverts[3][2] = r_entorigin[2] + down[2] + left[2];
pverts[3][3] = 0;
pverts[3][4] = sprite_height;
// clip to the frustum in worldspace
nump = 4;
clip_current = 0;
for (i = 0; i < 4; i++) {
nump = R_ClipSpriteFace(nump, &view_clipplanes[i]);
if (nump < 3)
return;
if (nump >= MAXWORKINGVERTS)
Sys_Error("%s: too many points", __func__);
}
// transform vertices into viewspace and project
pv = &clip_verts[clip_current][0][0];
r_spritedesc.nearzi = -999999;
for (i = 0; i < nump; i++) {
VectorSubtract(pv, r_origin, local);
TransformVector(local, transformed);
if (transformed[2] < NEAR_CLIP)
transformed[2] = NEAR_CLIP;
pout = &outverts[i];
pout->zi = 1.0 / transformed[2];
if (pout->zi > r_spritedesc.nearzi)
r_spritedesc.nearzi = pout->zi;
pout->s = pv[3];
pout->t = pv[4];
scale = xscale * pout->zi;
pout->u = (xcenter + scale * transformed[0]);
scale = yscale * pout->zi;
pout->v = (ycenter - scale * transformed[1]);
pv += sizeof(vec5_t) / sizeof(*pv);
}
// draw it
r_spritedesc.nump = nump;
r_spritedesc.pverts = outverts;
D_DrawSprite();
}
/*
================
R_GetSpriteframe
================
*/
mspriteframe_t *
R_GetSpriteframe(msprite_t *psprite)
{
mspritegroup_t *pspritegroup;
mspriteframe_t *pspriteframe;
int i, numframes, frame;
float *pintervals, fullinterval, targettime, time;
frame = currententity->frame;
if ((frame >= psprite->numframes) || (frame < 0)) {
Con_Printf("R_DrawSprite: no such frame %d\n", frame);
frame = 0;
}
if (psprite->frames[frame].type == SPR_SINGLE) {
pspriteframe = psprite->frames[frame].frameptr;
} else {
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
pintervals = pspritegroup->intervals;
numframes = pspritegroup->numframes;
fullinterval = pintervals[numframes - 1];
time = cl.time + currententity->syncbase;
// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
// are positive, so we don't have to worry about division by 0
targettime = time - ((int)(time / fullinterval)) * fullinterval;
for (i = 0; i < (numframes - 1); i++) {
if (pintervals[i] > targettime)
break;
}
pspriteframe = pspritegroup->frames[i];
}
return pspriteframe;
}
/*
================
R_DrawSprite
================
*/
void
R_DrawSprite(void)
{
int i;
msprite_t *psprite;
vec3_t tvec;
float dot, angle, sr, cr;
psprite = currententity->model->cache.data;
r_spritedesc.pspriteframe = R_GetSpriteframe(psprite);
sprite_width = r_spritedesc.pspriteframe->width;
sprite_height = r_spritedesc.pspriteframe->height;
// TODO: make this caller-selectable
if (psprite->type == SPR_FACING_UPRIGHT) {
// generate the sprite's axes, with vup straight up in worldspace, and
// r_spritedesc.vright perpendicular to modelorg.
// This will not work if the view direction is very close to straight up or
// down, because the cross product will be between two nearly parallel
// vectors and starts to approach an undefined state, so we don't draw if
// the two vectors are less than 1 degree apart
tvec[0] = -modelorg[0];
tvec[1] = -modelorg[1];
tvec[2] = -modelorg[2];
VectorNormalize(tvec);
dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because
// r_spritedesc.vup is 0, 0, 1
if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
return;
r_spritedesc.vup[0] = 0;
r_spritedesc.vup[1] = 0;
r_spritedesc.vup[2] = 1;
r_spritedesc.vright[0] = tvec[1];
// CrossProduct(r_spritedesc.vup, -modelorg,
r_spritedesc.vright[1] = -tvec[0];
// r_spritedesc.vright)
r_spritedesc.vright[2] = 0;
VectorNormalize(r_spritedesc.vright);
r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
r_spritedesc.vpn[1] = r_spritedesc.vright[0];
r_spritedesc.vpn[2] = 0;
// CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
// r_spritedesc.vpn)
} else if (psprite->type == SPR_VP_PARALLEL) {
// generate the sprite's axes, completely parallel to the viewplane. There
// are no problem situations, because the sprite is always in the same
// position relative to the viewer
for (i = 0; i < 3; i++) {
r_spritedesc.vup[i] = vup[i];
r_spritedesc.vright[i] = vright[i];
r_spritedesc.vpn[i] = vpn[i];
}
} else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) {
// generate the sprite's axes, with vup straight up in worldspace, and
// r_spritedesc.vright parallel to the viewplane.
// This will not work if the view direction is very close to straight up or
// down, because the cross product will be between two nearly parallel
// vectors and starts to approach an undefined state, so we don't draw if
// the two vectors are less than 1 degree apart
dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) because
// r_spritedesc.vup is 0, 0, 1
if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
return;
r_spritedesc.vup[0] = 0;
r_spritedesc.vup[1] = 0;
r_spritedesc.vup[2] = 1;
r_spritedesc.vright[0] = vpn[1];
// CrossProduct (r_spritedesc.vup, vpn,
r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright)
r_spritedesc.vright[2] = 0;
VectorNormalize(r_spritedesc.vright);
r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
r_spritedesc.vpn[1] = r_spritedesc.vright[0];
r_spritedesc.vpn[2] = 0;
// CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
// r_spritedesc.vpn)
} else if (psprite->type == SPR_ORIENTED) {
// generate the sprite's axes, according to the sprite's world orientation
AngleVectors(currententity->angles, r_spritedesc.vpn,
r_spritedesc.vright, r_spritedesc.vup);
} else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) {
// generate the sprite's axes, parallel to the viewplane, but rotated in
// that plane around the center according to the sprite entity's roll
// angle. So vpn stays the same, but vright and vup rotate
angle = currententity->angles[ROLL] * (M_PI * 2 / 360);
sr = sin(angle);
cr = cos(angle);
for (i = 0; i < 3; i++) {
r_spritedesc.vpn[i] = vpn[i];
r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr;
r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr;
}
} else {
Sys_Error("%s: Bad sprite type %d", __func__, psprite->type);
}
R_RotateSprite(psprite->beamlength);
R_SetupAndDrawSprite();
}

159
NQ/render.h Normal file
View File

@ -0,0 +1,159 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef RENDER_H
#define RENDER_H
#include "mathlib.h"
#include "vid.h"
// render.h -- public interface to refresh functions
#define TOP_RANGE 16 // soldier uniform colors
#define BOTTOM_RANGE 96
//=============================================================================
typedef struct efrag_s {
struct mleaf_s *leaf;
struct efrag_s *leafnext;
struct entity_s *entity;
struct efrag_s *entnext;
} efrag_t;
typedef struct entity_s {
qboolean forcelink; // model changed
int update_type;
entity_state_t baseline; // to fill in defaults in updates
double msgtime; // time of last update
vec3_t msg_origins[2]; // last two updates (0 is newest)
vec3_t msg_angles[2]; // last two updates (0 is newest)
vec3_t origin;
vec3_t angles;
struct model_s *model; // NULL = no model
int frame;
byte *colormap;
int skinnum; // for Alias models
float syncbase; // for client-side animations
struct efrag_s *efrag; // linked list of efrags (FIXME)
int visframe; // last frame this entity was
// found in an active leaf
int effects; // light, particals, etc
int dlightframe; // dynamic lighting
int dlightbits;
// FIXME: could turn these into a union
int trivial_accept;
struct mnode_s *topnode; // for bmodels, first world node
// that splits bmodel, or NULL if
// not split
} entity_t;
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct {
vrect_t vrect; // subwindow in video for refresh
// FIXME: not need vrect next field here?
vrect_t aliasvrect; // scaled Alias version
int vrectright, vrectbottom; // right & bottom screen coords
int aliasvrectright, aliasvrectbottom; // scaled Alias versions
float vrectrightedge; // rightmost right edge we care about,
// for use in edge list
float fvrectx, fvrecty; // for floating-point compares
float fvrectx_adj, fvrecty_adj; // left and top edges, for clamping
int vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20
int vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20
float fvrectright_adj, fvrectbottom_adj;
// right and bottom edges, for clamping
float fvrectright; // rightmost edge, for Alias clamping
float fvrectbottom; // bottommost edge, for Alias clamping
float horizontalFieldOfView; // at Z = 1.0, this many X is visible
// 2.0 = 90 degrees
float xOrigin; // should probably allways be 0.5
float yOrigin; // between be around 0.3 to 0.5
vec3_t vieworg;
vec3_t viewangles;
float fov_x, fov_y;
int ambientlight;
} refdef_t;
//
// refresh
//
extern refdef_t r_refdef;
extern vec3_t r_origin, vpn, vright, vup;
extern struct texture_s *r_notexture_mip;
void R_Init(void);
void R_InitTextures(void);
void R_InitEfrags(void);
void R_RenderView(void); // must set r_refdef first
void R_ViewChanged(vrect_t *pvrect, int lineadj, float aspect);
// called whenever r_refdef or vid change
void R_InitSky(struct texture_s *mt); // called at level load
void R_AddEfrags(entity_t *ent);
void R_RemoveEfrags(entity_t *ent);
void R_NewMap(void);
void R_ParseParticleEffect(void);
void R_RunParticleEffect(vec3_t org, vec3_t dir, int color, int count);
void R_RocketTrail(vec3_t start, vec3_t end, int type);
void R_EntityParticles(entity_t *ent);
void R_BlobExplosion(vec3_t org);
void R_ParticleExplosion(vec3_t org);
void R_ParticleExplosion2(vec3_t org, int colorStart, int colorLength);
void R_LavaSplash(vec3_t org);
void R_TeleportSplash(vec3_t org);
void R_PushDlights(void);
void R_InitParticles(void);
void R_ClearParticles(void);
void R_DrawParticles(void);
//
// surface cache related
//
extern qboolean r_cache_thrash; // set if thrashing the surface cache
int D_SurfaceCacheForRes(int width, int height);
void D_FlushCaches(void);
void D_DeleteSurfaceCache(void);
void D_InitCaches(void *buffer, int size);
void R_SetVrect(vrect_t *pvrect, vrect_t *pvrectin, int lineadj);
#endif /* RENDER_H */

20
NQ/resource.h Normal file
View File

@ -0,0 +1,20 @@
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by winquake.rc
//
#define IDS_STRING1 1
#define IDI_ICON2 1
#define IDD_DIALOG1 108
#define IDD_PROGRESS 109
#define IDC_PROGRESS 1000
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 111
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1004
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

1282
NQ/sbar.c Normal file

File diff suppressed because it is too large Load Diff

974
NQ/screen.c Normal file
View File

@ -0,0 +1,974 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// screen.c -- master for refresh, status bar, console, chat, notify, etc
#include "screen.h"
#include "quakedef.h"
#include "r_local.h"
#include "host.h"
#include "wad.h"
#include "draw.h"
#include "keys.h"
#include "sys.h"
#include "sbar.h"
#include "cmd.h"
#include "console.h"
#include "sound.h"
#include "view.h" /* lcd_x */
#include "menu.h"
// only the refresh window will be updated unless these variables are flagged
int scr_copytop;
int scr_copyeverything;
float scr_con_current;
float scr_conlines; // lines of console to display
float oldscreensize, oldfov;
cvar_t scr_viewsize = { "viewsize", "100", true };
cvar_t scr_fov = { "fov", "90" }; // 10 - 170
cvar_t scr_conspeed = { "scr_conspeed", "300" };
cvar_t scr_centertime = { "scr_centertime", "2" };
cvar_t scr_showram = { "showram", "1" };
cvar_t scr_showturtle = { "showturtle", "0" };
cvar_t scr_showpause = { "showpause", "1" };
cvar_t scr_printspeed = { "scr_printspeed", "8" };
qboolean scr_initialized; // ready to draw
qpic_t *scr_ram;
qpic_t *scr_net;
qpic_t *scr_turtle;
int scr_fullupdate;
int clearconsole;
int clearnotify;
viddef_t vid; // global video state
vrect_t *pconupdate;
vrect_t scr_vrect;
qboolean scr_disabled_for_loading;
qboolean scr_drawloading;
float scr_disabled_time;
qboolean scr_skipupdate;
qboolean block_drawing;
void SCR_ScreenShot_f(void);
/*
===============================================================================
CENTER PRINTING
===============================================================================
*/
char scr_centerstring[1024];
float scr_centertime_start; // for slow victory printing
float scr_centertime_off;
int scr_center_lines;
int scr_erase_lines;
int scr_erase_center;
/*
==============
SCR_CenterPrint
Called for important messages that should stay in the center of the screen
for a few moments
==============
*/
void
SCR_CenterPrint(char *str)
{
strncpy(scr_centerstring, str, sizeof(scr_centerstring) - 1);
scr_centertime_off = scr_centertime.value;
scr_centertime_start = cl.time;
// count the number of lines for centering
scr_center_lines = 1;
while (*str) {
if (*str == '\n')
scr_center_lines++;
str++;
}
}
void
SCR_EraseCenterString(void)
{
int y;
if (scr_erase_center++ > vid.numpages) {
scr_erase_lines = 0;
return;
}
if (scr_center_lines <= 4)
y = vid.height * 0.35;
else
y = 48;
scr_copytop = 1;
Draw_TileClear(0, y, vid.width, 8 * scr_erase_lines);
}
void
SCR_DrawCenterString(void)
{
char *start;
int l;
int j;
int x, y;
int remaining;
// the finale prints the characters one at a time
if (cl.intermission)
remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
else
remaining = 9999;
scr_erase_center = 0;
start = scr_centerstring;
if (scr_center_lines <= 4)
y = vid.height * 0.35;
else
y = 48;
do {
// scan the width of the line
for (l = 0; l < 40; l++)
if (start[l] == '\n' || !start[l])
break;
x = (vid.width - l * 8) / 2;
for (j = 0; j < l; j++, x += 8) {
Draw_Character(x, y, start[j]);
if (!remaining--)
return;
}
y += 8;
while (*start && *start != '\n')
start++;
if (!*start)
break;
start++; // skip the \n
} while (1);
}
void
SCR_CheckDrawCenterString(void)
{
scr_copytop = 1;
if (scr_center_lines > scr_erase_lines)
scr_erase_lines = scr_center_lines;
scr_centertime_off -= host_frametime;
if (scr_centertime_off <= 0 && !cl.intermission)
return;
if (key_dest != key_game)
return;
SCR_DrawCenterString();
}
//=============================================================================
/*
====================
CalcFov
====================
*/
float
CalcFov(float fov_x, float width, float height)
{
float a;
float x;
if (fov_x < 1 || fov_x > 179)
Sys_Error("Bad fov: %f", fov_x);
x = width / tan(fov_x / 360 * M_PI);
a = atan(height / x);
a = a * 360 / M_PI;
return a;
}
/*
=================
SCR_CalcRefdef
Must be called whenever vid changes
Internal use only
=================
*/
static void
SCR_CalcRefdef(void)
{
vrect_t vrect;
float size;
scr_fullupdate = 0; // force a background redraw
vid.recalc_refdef = 0;
// force the status bar to redraw
Sbar_Changed();
//========================================
// bound viewsize
if (scr_viewsize.value < 30)
Cvar_Set("viewsize", "30");
if (scr_viewsize.value > 120)
Cvar_Set("viewsize", "120");
// bound field of view
if (scr_fov.value < 10)
Cvar_Set("fov", "10");
if (scr_fov.value > 170)
Cvar_Set("fov", "170");
r_refdef.fov_x = scr_fov.value;
r_refdef.fov_y =
CalcFov(r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
// intermission is always full screen
if (cl.intermission)
size = 120;
else
size = scr_viewsize.value;
if (size >= 120)
sb_lines = 0; // no status bar at all
else if (size >= 110)
sb_lines = 24; // no inventory
else
sb_lines = 24 + 16 + 8;
// these calculations mirror those in R_Init() for r_refdef, but take no
// account of water warping
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height;
R_SetVrect(&vrect, &scr_vrect, sb_lines);
// guard against going from one mode to another that's less than half the
// vertical resolution
if (scr_con_current > vid.height)
scr_con_current = vid.height;
// notify the refresh of the change
R_ViewChanged(&vrect, sb_lines, vid.aspect);
}
/*
=================
SCR_SizeUp_f
Keybinding command
=================
*/
void
SCR_SizeUp_f(void)
{
Cvar_SetValue("viewsize", scr_viewsize.value + 10);
vid.recalc_refdef = 1;
}
/*
=================
SCR_SizeDown_f
Keybinding command
=================
*/
void
SCR_SizeDown_f(void)
{
Cvar_SetValue("viewsize", scr_viewsize.value - 10);
vid.recalc_refdef = 1;
}
//============================================================================
/*
==================
SCR_Init
==================
*/
void
SCR_Init(void)
{
Cvar_RegisterVariable(&scr_fov);
Cvar_RegisterVariable(&scr_viewsize);
Cvar_RegisterVariable(&scr_conspeed);
Cvar_RegisterVariable(&scr_showram);
Cvar_RegisterVariable(&scr_showturtle);
Cvar_RegisterVariable(&scr_showpause);
Cvar_RegisterVariable(&scr_centertime);
Cvar_RegisterVariable(&scr_printspeed);
//
// register our commands
//
Cmd_AddCommand("screenshot", SCR_ScreenShot_f);
Cmd_AddCommand("sizeup", SCR_SizeUp_f);
Cmd_AddCommand("sizedown", SCR_SizeDown_f);
scr_ram = Draw_PicFromWad("ram");
scr_net = Draw_PicFromWad("net");
scr_turtle = Draw_PicFromWad("turtle");
scr_initialized = true;
}
/*
==============
SCR_DrawRam
==============
*/
void
SCR_DrawRam(void)
{
if (!scr_showram.value)
return;
if (!r_cache_thrash)
return;
Draw_Pic(scr_vrect.x + 32, scr_vrect.y, scr_ram);
}
/*
==============
SCR_DrawTurtle
==============
*/
void
SCR_DrawTurtle(void)
{
static int count;
if (!scr_showturtle.value)
return;
if (host_frametime < 0.1) {
count = 0;
return;
}
count++;
if (count < 3)
return;
Draw_Pic(scr_vrect.x, scr_vrect.y, scr_turtle);
}
/*
==============
SCR_DrawNet
==============
*/
void
SCR_DrawNet(void)
{
if (realtime - cl.last_received_message < 0.3)
return;
if (cls.demoplayback)
return;
Draw_Pic(scr_vrect.x + 64, scr_vrect.y, scr_net);
}
/*
==============
DrawPause
==============
*/
void
SCR_DrawPause(void)
{
qpic_t *pic;
if (!scr_showpause.value) // turn off for screenshots
return;
if (!cl.paused)
return;
pic = Draw_CachePic("gfx/pause.lmp");
Draw_Pic((vid.width - pic->width) / 2,
(vid.height - 48 - pic->height) / 2, pic);
}
/*
==============
SCR_DrawLoading
==============
*/
void
SCR_DrawLoading(void)
{
qpic_t *pic;
if (!scr_drawloading)
return;
pic = Draw_CachePic("gfx/loading.lmp");
Draw_Pic((vid.width - pic->width) / 2,
(vid.height - 48 - pic->height) / 2, pic);
}
//=============================================================================
/*
==================
SCR_SetUpToDrawConsole
==================
*/
void
SCR_SetUpToDrawConsole(void)
{
Con_CheckResize();
if (scr_drawloading)
return; // never a console with loading plaque
// decide on the height of the console
con_forcedup = !cl.worldmodel || cls.state != ca_active;
if (con_forcedup) {
scr_conlines = vid.height; // full screen
scr_con_current = scr_conlines;
} else if (key_dest == key_console)
scr_conlines = vid.height / 2; // half screen
else
scr_conlines = 0; // none visible
if (scr_conlines < scr_con_current) {
scr_con_current -= scr_conspeed.value * host_frametime;
if (scr_conlines > scr_con_current)
scr_con_current = scr_conlines;
} else if (scr_conlines > scr_con_current) {
scr_con_current += scr_conspeed.value * host_frametime;
if (scr_conlines < scr_con_current)
scr_con_current = scr_conlines;
}
if (clearconsole++ < vid.numpages) {
scr_copytop = 1;
Draw_TileClear(0, (int)scr_con_current, vid.width,
vid.height - (int)scr_con_current);
Sbar_Changed();
} else if (clearnotify++ < vid.numpages) {
scr_copytop = 1;
Draw_TileClear(0, 0, vid.width, con_notifylines);
} else
con_notifylines = 0;
}
/*
==================
SCR_DrawConsole
==================
*/
void
SCR_DrawConsole(void)
{
if (scr_con_current) {
scr_copyeverything = 1;
Con_DrawConsole(scr_con_current);
clearconsole = 0;
} else {
if (key_dest == key_game || key_dest == key_message)
Con_DrawNotify(); // only draw notify in game
}
}
/*
==============================================================================
SCREEN SHOTS
==============================================================================
*/
typedef struct {
char manufacturer;
char version;
char encoding;
char bits_per_pixel;
unsigned short xmin, ymin, xmax, ymax;
unsigned short hres, vres;
unsigned char palette[48];
char reserved;
char color_planes;
unsigned short bytes_per_line;
unsigned short palette_type;
char filler[58];
unsigned char data; // unbounded
} pcx_t;
/*
==============
WritePCXfile
==============
*/
void
WritePCXfile(char *filename, byte *data, int width, int height,
int rowbytes, byte *palette)
{
int i, j, length;
pcx_t *pcx;
byte *pack;
pcx = Hunk_TempAlloc(width * height * 2 + 1000);
if (pcx == NULL) {
Con_Printf("SCR_ScreenShot_f: not enough memory\n");
return;
}
pcx->manufacturer = 0x0a; // PCX id
pcx->version = 5; // 256 color
pcx->encoding = 1; // uncompressed
pcx->bits_per_pixel = 8; // 256 color
pcx->xmin = 0;
pcx->ymin = 0;
pcx->xmax = LittleShort((short)(width - 1));
pcx->ymax = LittleShort((short)(height - 1));
pcx->hres = LittleShort((short)width);
pcx->vres = LittleShort((short)height);
memset(pcx->palette, 0, sizeof(pcx->palette));
pcx->color_planes = 1; // chunky image
pcx->bytes_per_line = LittleShort((short)width);
pcx->palette_type = LittleShort(2); // not a grey scale
memset(pcx->filler, 0, sizeof(pcx->filler));
// pack the image
pack = &pcx->data;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
if ((*data & 0xc0) != 0xc0)
*pack++ = *data++;
else {
*pack++ = 0xc1;
*pack++ = *data++;
}
}
data += rowbytes - width;
}
// write the palette
*pack++ = 0x0c; // palette ID byte
for (i = 0; i < 768; i++)
*pack++ = *palette++;
// write output file
length = pack - (byte *)pcx;
COM_WriteFile(filename, pcx, length);
}
/*
==================
SCR_ScreenShot_f
==================
*/
void
SCR_ScreenShot_f(void)
{
int i;
char pcxname[80];
char checkname[MAX_OSPATH];
//
// find a file name to save it to
//
strcpy(pcxname, "quake00.pcx");
for (i = 0; i <= 99; i++) {
pcxname[5] = i / 10 + '0';
pcxname[6] = i % 10 + '0';
sprintf(checkname, "%s/%s", com_gamedir, pcxname);
if (Sys_FileTime(checkname) == -1)
break; // file doesn't exist
}
if (i == 100) {
Con_Printf("SCR_ScreenShot_f: Couldn't create a PCX file\n");
return;
}
//
// save the pcx file
//
D_EnableBackBufferAccess(); // enable direct drawing of console to back
// buffer
WritePCXfile(pcxname, vid.buffer, vid.width, vid.height, vid.rowbytes,
host_basepal);
D_DisableBackBufferAccess(); // for adapters that can't stay mapped in
// for linear writes all the time
Con_Printf("Wrote %s\n", pcxname);
}
//=============================================================================
/*
===============
SCR_BeginLoadingPlaque
================
*/
void
SCR_BeginLoadingPlaque(void)
{
S_StopAllSounds(true);
if (cls.state != ca_active)
return;
// redraw with no console and the loading plaque
Con_ClearNotify();
scr_centertime_off = 0;
scr_con_current = 0;
scr_drawloading = true;
scr_fullupdate = 0;
Sbar_Changed();
SCR_UpdateScreen();
scr_drawloading = false;
scr_disabled_for_loading = true;
scr_disabled_time = realtime;
scr_fullupdate = 0;
}
/*
===============
SCR_EndLoadingPlaque
================
*/
void
SCR_EndLoadingPlaque(void)
{
scr_disabled_for_loading = false;
scr_fullupdate = 0;
Con_ClearNotify();
}
//=============================================================================
char *scr_notifystring;
qboolean scr_drawdialog;
void
SCR_DrawNotifyString(void)
{
char *start;
int l;
int j;
int x, y;
start = scr_notifystring;
y = vid.height * 0.35;
do {
// scan the width of the line
for (l = 0; l < 40; l++)
if (start[l] == '\n' || !start[l])
break;
x = (vid.width - l * 8) / 2;
for (j = 0; j < l; j++, x += 8)
Draw_Character(x, y, start[j]);
y += 8;
while (*start && *start != '\n')
start++;
if (!*start)
break;
start++; // skip the \n
} while (1);
}
/*
==================
SCR_ModalMessage
Displays a text string in the center of the screen and waits for a Y or N
keypress.
==================
*/
int
SCR_ModalMessage(char *text)
{
if (cls.state == ca_dedicated)
return true;
scr_notifystring = text;
// draw a fresh screen
scr_fullupdate = 0;
scr_drawdialog = true;
SCR_UpdateScreen();
scr_drawdialog = false;
S_ClearBuffer(); // so dma doesn't loop current sound
do {
key_count = -1; // wait for a key down and up
Sys_SendKeyEvents();
} while (key_lastpress != 'y' && key_lastpress != 'n'
&& key_lastpress != K_ESCAPE);
scr_fullupdate = 0;
SCR_UpdateScreen();
return key_lastpress == 'y';
}
//=============================================================================
/*
===============
SCR_BringDownConsole
Brings the console down and fades the palettes back to normal
================
*/
void
SCR_BringDownConsole(void)
{
int i;
scr_centertime_off = 0;
for (i = 0; i < 20 && scr_conlines != scr_con_current; i++)
SCR_UpdateScreen();
cl.cshifts[0].percent = 0; // no area contents palette on next frame
VID_SetPalette(host_basepal);
}
/*
==================
SCR_UpdateScreen
This is called every frame, and can also be called explicitly to flush
text to the screen.
WARNING: be very careful calling this from elsewhere, because the refresh
needs almost the entire 256k of stack space!
==================
*/
void
SCR_UpdateScreen(void)
{
static float oldscr_viewsize;
static float oldlcd_x;
vrect_t vrect;
if (scr_skipupdate || block_drawing)
return;
scr_copytop = 0;
scr_copyeverything = 0;
if (scr_disabled_for_loading) {
if (realtime - scr_disabled_time > 60) {
scr_disabled_for_loading = false;
Con_Printf("load failed.\n");
} else
return;
}
if (cls.state == ca_dedicated)
return; // stdout only
if (!scr_initialized || !con_initialized)
return; // not initialized yet
if (scr_viewsize.value != oldscr_viewsize) {
oldscr_viewsize = scr_viewsize.value;
vid.recalc_refdef = 1;
}
//
// check for vid changes
//
if (oldfov != scr_fov.value) {
oldfov = scr_fov.value;
vid.recalc_refdef = true;
}
if (oldlcd_x != lcd_x.value) {
oldlcd_x = lcd_x.value;
vid.recalc_refdef = true;
}
if (oldscreensize != scr_viewsize.value) {
oldscreensize = scr_viewsize.value;
vid.recalc_refdef = true;
}
if (vid.recalc_refdef) {
// something changed, so reorder the screen
SCR_CalcRefdef();
}
//
// do 3D refresh drawing, and then update the screen
//
D_EnableBackBufferAccess(); // of all overlay stuff if drawing directly
if (scr_fullupdate++ < vid.numpages) { // clear the entire screen
scr_copyeverything = 1;
Draw_TileClear(0, 0, vid.width, vid.height);
Sbar_Changed();
}
pconupdate = NULL;
SCR_SetUpToDrawConsole();
SCR_EraseCenterString();
D_DisableBackBufferAccess(); // for adapters that can't stay mapped in
// for linear writes all the time
VID_LockBuffer();
V_RenderView();
VID_UnlockBuffer();
D_EnableBackBufferAccess(); // of all overlay stuff if drawing directly
if (scr_drawdialog) {
Sbar_Draw();
Draw_FadeScreen();
SCR_DrawNotifyString();
scr_copyeverything = true;
} else if (scr_drawloading) {
SCR_DrawLoading();
Sbar_Draw();
} else if (cl.intermission == 1 && key_dest == key_game) {
Sbar_IntermissionOverlay();
} else if (cl.intermission == 2 && key_dest == key_game) {
Sbar_FinaleOverlay();
SCR_CheckDrawCenterString();
} else if (cl.intermission == 3 && key_dest == key_game) {
SCR_CheckDrawCenterString();
} else {
SCR_DrawRam();
SCR_DrawNet();
SCR_DrawTurtle();
SCR_DrawPause();
SCR_CheckDrawCenterString();
Sbar_Draw();
SCR_DrawConsole();
M_Draw();
}
D_DisableBackBufferAccess(); // for adapters that can't stay mapped in
// for linear writes all the time
if (pconupdate) {
D_UpdateRects(pconupdate);
}
V_UpdatePalette();
//
// update one of three areas
//
if (scr_copyeverything) {
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height;
vrect.pnext = 0;
VID_Update(&vrect);
} else if (scr_copytop) {
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height - sb_lines;
vrect.pnext = 0;
VID_Update(&vrect);
} else {
vrect.x = scr_vrect.x;
vrect.y = scr_vrect.y;
vrect.width = scr_vrect.width;
vrect.height = scr_vrect.height;
vrect.pnext = 0;
VID_Update(&vrect);
}
}
/*
==================
SCR_UpdateWholeScreen
==================
*/
void
SCR_UpdateWholeScreen(void)
{
scr_fullupdate = 0;
SCR_UpdateScreen();
}

244
NQ/server.h Normal file
View File

@ -0,0 +1,244 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef SERVER_H
#define SERVER_H
#include "progs.h"
#include "client.h"
typedef enum { ss_loading, ss_active } server_state_t;
typedef struct {
qboolean active; // false if only a net client
qboolean paused;
qboolean loadgame; // handle connections specially
double time;
int lastcheck; // used by PF_checkclient
double lastchecktime;
char name[64]; // map name
char modelname[64]; // maps/<name>.bsp, for model_precache[0]
struct model_s *worldmodel;
char *model_precache[MAX_MODELS]; // NULL terminated
struct model_s *models[MAX_MODELS];
char *sound_precache[MAX_SOUNDS]; // NULL terminated
char *lightstyles[MAX_LIGHTSTYLES];
int num_edicts;
int max_edicts;
edict_t *edicts; /* can NOT be array indexed, because
edict_t is variable sized, but can
be used to reference the world ent */
server_state_t state; // some actions are only valid during load
sizebuf_t datagram;
byte datagram_buf[MAX_DATAGRAM];
sizebuf_t reliable_datagram; // copied to all clients at end of frame
byte reliable_datagram_buf[MAX_DATAGRAM];
sizebuf_t signon;
byte signon_buf[8192];
} server_t;
#define NUM_PING_TIMES 16
#define NUM_SPAWN_PARMS 16
typedef struct client_s {
qboolean active; // false = client is free
qboolean spawned; // false = don't send datagrams
qboolean dropasap; // has been told to go to another level
qboolean privileged; // can execute any host command
qboolean sendsignon; // only valid before spawned
double last_message; // reliable messages must be sent
// periodically
struct qsocket_s *netconnection; // communications handle
usercmd_t cmd; // movement
vec3_t wishdir; // intended motion calced from cmd
sizebuf_t message; // can be added to at any time,
// copied and clear once per frame
byte msgbuf[MAX_MSGLEN];
edict_t *edict; // EDICT_NUM(clientnum+1)
char name[32]; // for printing to other people
int colors;
float ping_times[NUM_PING_TIMES];
int num_pings; // ping_times[num_pings%NUM_PING_TIMES]
// spawn parms are carried from level to level
float spawn_parms[NUM_SPAWN_PARMS];
// client known data for deltas
int old_frags;
} client_t;
//=============================================================================
typedef struct {
int maxclients;
int maxclientslimit;
struct client_s *clients; // [maxclients]
int serverflags; // episode completion information
qboolean changelevel_issued; // cleared when at SV_SpawnServer
} server_static_t;
//=============================================================================
// edict->movetype values
#define MOVETYPE_NONE 0 // never moves
#define MOVETYPE_ANGLENOCLIP 1
#define MOVETYPE_ANGLECLIP 2
#define MOVETYPE_WALK 3 // gravity
#define MOVETYPE_STEP 4 // gravity, special edge handling
#define MOVETYPE_FLY 5
#define MOVETYPE_TOSS 6 // gravity
#define MOVETYPE_PUSH 7 // no clip to world, push and crush
#define MOVETYPE_NOCLIP 8
#define MOVETYPE_FLYMISSILE 9 // extra size to monsters
#define MOVETYPE_BOUNCE 10
// edict->solid values
#define SOLID_NOT 0 // no interaction with other objects
#define SOLID_TRIGGER 1 // touch on edge, but not blocking
#define SOLID_BBOX 2 // touch on edge, block
#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground
#define SOLID_BSP 4 // bsp clip, touch on edge, block
// edict->deadflag values
#define DEAD_NO 0
#define DEAD_DYING 1
#define DEAD_DEAD 2
#define DAMAGE_NO 0
#define DAMAGE_YES 1
#define DAMAGE_AIM 2
// edict->flags
#define FL_FLY 1
#define FL_SWIM 2
//#define FL_GLIMPSE 4
#define FL_CONVEYOR 4
#define FL_CLIENT 8
#define FL_INWATER 16
#define FL_MONSTER 32
#define FL_GODMODE 64
#define FL_NOTARGET 128
#define FL_ITEM 256
#define FL_ONGROUND 512
#define FL_PARTIALGROUND 1024 // not all corners are valid
#define FL_WATERJUMP 2048 // player jumping out of water
#define FL_JUMPRELEASED 4096 // for jump debouncing
// entity effects
#define EF_BRIGHTFIELD 1
#define EF_MUZZLEFLASH 2
#define EF_BRIGHTLIGHT 4
#define EF_DIMLIGHT 8
#define SPAWNFLAG_NOT_EASY 256
#define SPAWNFLAG_NOT_MEDIUM 512
#define SPAWNFLAG_NOT_HARD 1024
#define SPAWNFLAG_NOT_DEATHMATCH 2048
//============================================================================
extern cvar_t teamplay;
extern cvar_t skill;
extern cvar_t deathmatch;
extern cvar_t coop;
extern cvar_t fraglimit;
extern cvar_t timelimit;
extern cvar_t pausable;
extern cvar_t sv_maxvelocity;
extern cvar_t sv_gravity;
extern cvar_t sv_nostep;
extern cvar_t sv_friction;
extern cvar_t sv_edgefriction;
extern cvar_t sv_stopspeed;
extern cvar_t sv_maxspeed;
extern cvar_t sv_accelerate;
extern cvar_t sv_idealpitchscale;
extern cvar_t sv_aim;
extern server_static_t svs; // persistant server info
extern server_t sv; // local server
extern client_t *host_client;
extern jmp_buf host_abortserver;
extern double host_time;
extern edict_t *sv_player;
//===========================================================
void SV_Init(void);
void SV_StartParticle(vec3_t org, vec3_t dir, int color, int count);
void SV_StartSound(edict_t *entity, int channel, char *sample,
int volume, float attenuation);
void SV_DropClient(qboolean crash);
void SV_SendClientMessages(void);
void SV_ClearDatagram(void);
int SV_ModelIndex(char *name);
void SV_SetIdealPitch(void);
void SV_AddUpdates(void);
void SV_ClientThink(void);
void SV_AddClientToServer(struct qsocket_s *ret);
void SV_ClientPrintf(char *fmt, ...);
void SV_BroadcastPrintf(char *fmt, ...);
void SV_Physics(void);
qboolean SV_CheckBottom(edict_t *ent);
qboolean SV_movestep(edict_t *ent, vec3_t move, qboolean relink);
void SV_WriteClientdataToMessage(edict_t *ent, sizebuf_t *msg);
void SV_MoveToGoal(void);
void SV_CheckForNewClients(void);
void SV_RunClients(void);
void SV_SaveSpawnparms();
void SV_SpawnServer(char *server);
#endif /* SERVER_H */

1165
NQ/sv_main.c Normal file

File diff suppressed because it is too large Load Diff

407
NQ/sv_move.c Normal file
View File

@ -0,0 +1,407 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// sv_move.c -- monster movement
#include "quakedef.h"
#include "progs.h"
#include "world.h"
#include "server.h"
#define STEPSIZE 18
/*
=============
SV_CheckBottom
Returns false if any part of the bottom of the entity is off an edge that
is not a staircase.
=============
*/
int c_yes, c_no;
qboolean
SV_CheckBottom(edict_t *ent)
{
vec3_t mins, maxs, start, stop;
trace_t trace;
int x, y;
float mid, bottom;
VectorAdd(ent->v.origin, ent->v.mins, mins);
VectorAdd(ent->v.origin, ent->v.maxs, maxs);
// if all of the points under the corners are solid world, don't bother
// with the tougher checks
// the corners must be within 16 of the midpoint
start[2] = mins[2] - 1;
for (x = 0; x <= 1; x++)
for (y = 0; y <= 1; y++) {
start[0] = x ? maxs[0] : mins[0];
start[1] = y ? maxs[1] : mins[1];
if (SV_PointContents(start) != CONTENTS_SOLID)
goto realcheck;
}
c_yes++;
return true; // we got out easy
realcheck:
c_no++;
//
// check it for real...
//
start[2] = mins[2];
// the midpoint must be within 16 of the bottom
start[0] = stop[0] = (mins[0] + maxs[0]) * 0.5;
start[1] = stop[1] = (mins[1] + maxs[1]) * 0.5;
stop[2] = start[2] - 2 * STEPSIZE;
trace = SV_Move(start, vec3_origin, vec3_origin, stop, true, ent);
if (trace.fraction == 1.0)
return false;
mid = bottom = trace.endpos[2];
// the corners must be within 16 of the midpoint
for (x = 0; x <= 1; x++)
for (y = 0; y <= 1; y++) {
start[0] = stop[0] = x ? maxs[0] : mins[0];
start[1] = stop[1] = y ? maxs[1] : mins[1];
trace = SV_Move(start, vec3_origin, vec3_origin, stop, true, ent);
if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
bottom = trace.endpos[2];
if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
return false;
}
c_yes++;
return true;
}
/*
=============
SV_movestep
Called by monster program code.
The move will be adjusted for slopes and stairs, but if the move isn't
possible, no move is done, false is returned, and
pr_global_struct->trace_normal is set to the normal of the blocking wall
=============
*/
qboolean
SV_movestep(edict_t *ent, vec3_t move, qboolean relink)
{
float dz;
vec3_t oldorg, neworg, end;
trace_t trace;
int i;
edict_t *enemy;
// try the move
VectorCopy(ent->v.origin, oldorg);
VectorAdd(ent->v.origin, move, neworg);
// flying monsters don't step up
if ((int)ent->v.flags & (FL_SWIM | FL_FLY)) {
// try one move with vertical motion, then one without
for (i = 0; i < 2; i++) {
VectorAdd(ent->v.origin, move, neworg);
enemy = PROG_TO_EDICT(ent->v.enemy);
if (i == 0 && enemy != sv.edicts) {
dz = ent->v.origin[2] -
PROG_TO_EDICT(ent->v.enemy)->v.origin[2];
if (dz > 40)
neworg[2] -= 8;
if (dz < 30)
neworg[2] += 8;
}
trace =
SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, neworg,
false, ent);
if (trace.fraction == 1) {
if (((int)ent->v.flags & FL_SWIM)
&& SV_PointContents(trace.endpos) == CONTENTS_EMPTY)
return false; // swim monster left water
VectorCopy(trace.endpos, ent->v.origin);
if (relink)
SV_LinkEdict(ent, true);
return true;
}
if (enemy == sv.edicts)
break;
}
return false;
}
// push down from a step height above the wished position
neworg[2] += STEPSIZE;
VectorCopy(neworg, end);
end[2] -= STEPSIZE * 2;
trace = SV_Move(neworg, ent->v.mins, ent->v.maxs, end, false, ent);
if (trace.allsolid)
return false;
if (trace.startsolid) {
neworg[2] -= STEPSIZE;
trace = SV_Move(neworg, ent->v.mins, ent->v.maxs, end, false, ent);
if (trace.allsolid || trace.startsolid)
return false;
}
if (trace.fraction == 1) {
// if monster had the ground pulled out, go ahead and fall
if ((int)ent->v.flags & FL_PARTIALGROUND) {
VectorAdd(ent->v.origin, move, ent->v.origin);
if (relink)
SV_LinkEdict(ent, true);
ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
// Con_Printf ("fall down\n");
return true;
}
return false; // walked off an edge
}
// check point traces down for dangling corners
VectorCopy(trace.endpos, ent->v.origin);
if (!SV_CheckBottom(ent)) {
if ((int)ent->v.flags & FL_PARTIALGROUND) { // entity had floor mostly pulled out from underneath it
// and is trying to correct
if (relink)
SV_LinkEdict(ent, true);
return true;
}
VectorCopy(oldorg, ent->v.origin);
return false;
}
if ((int)ent->v.flags & FL_PARTIALGROUND) {
// Con_Printf ("back on ground\n");
ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND;
}
ent->v.groundentity = EDICT_TO_PROG(trace.ent);
// the move is ok
if (relink)
SV_LinkEdict(ent, true);
return true;
}
//============================================================================
/*
======================
SV_StepDirection
Turns to the movement direction, and walks the current distance if
facing it.
======================
*/
void PF_changeyaw(void);
qboolean
SV_StepDirection(edict_t *ent, float yaw, float dist)
{
vec3_t move, oldorigin;
float delta;
ent->v.ideal_yaw = yaw;
PF_changeyaw();
yaw = yaw * M_PI * 2 / 360;
move[0] = cos(yaw) * dist;
move[1] = sin(yaw) * dist;
move[2] = 0;
VectorCopy(ent->v.origin, oldorigin);
if (SV_movestep(ent, move, false)) {
delta = ent->v.angles[YAW] - ent->v.ideal_yaw;
if (delta > 45 && delta < 315) { // not turned far enough, so don't take the step
VectorCopy(oldorigin, ent->v.origin);
}
SV_LinkEdict(ent, true);
return true;
}
SV_LinkEdict(ent, true);
return false;
}
/*
======================
SV_FixCheckBottom
======================
*/
void
SV_FixCheckBottom(edict_t *ent)
{
// Con_Printf ("SV_FixCheckBottom\n");
ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND;
}
/*
================
SV_NewChaseDir
================
*/
#define DI_NODIR -1
void
SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist)
{
float deltax, deltay;
float d[3];
float tdir, olddir, turnaround;
olddir = anglemod((int)(actor->v.ideal_yaw / 45) * 45);
turnaround = anglemod(olddir - 180);
deltax = enemy->v.origin[0] - actor->v.origin[0];
deltay = enemy->v.origin[1] - actor->v.origin[1];
if (deltax > 10)
d[1] = 0;
else if (deltax < -10)
d[1] = 180;
else
d[1] = DI_NODIR;
if (deltay < -10)
d[2] = 270;
else if (deltay > 10)
d[2] = 90;
else
d[2] = DI_NODIR;
// try direct route
if (d[1] != DI_NODIR && d[2] != DI_NODIR) {
if (d[1] == 0)
tdir = d[2] == 90 ? 45 : 315;
else
tdir = d[2] == 90 ? 135 : 215;
if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
return;
}
// try other directions
if (((rand() & 3) & 1) || abs(deltay) > abs(deltax)) {
tdir = d[1];
d[1] = d[2];
d[2] = tdir;
}
if (d[1] != DI_NODIR && d[1] != turnaround
&& SV_StepDirection(actor, d[1], dist))
return;
if (d[2] != DI_NODIR && d[2] != turnaround
&& SV_StepDirection(actor, d[2], dist))
return;
/* there is no direct path to the player, so pick another direction */
if (olddir != DI_NODIR && SV_StepDirection(actor, olddir, dist))
return;
if (rand() & 1) { /*randomly determine direction of search */
for (tdir = 0; tdir <= 315; tdir += 45)
if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
return;
} else {
for (tdir = 315; tdir >= 0; tdir -= 45)
if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
return;
}
if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist))
return;
actor->v.ideal_yaw = olddir; // can't move
// if a bridge was pulled out from underneath a monster, it may not have
// a valid standing position at all
if (!SV_CheckBottom(actor))
SV_FixCheckBottom(actor);
}
/*
======================
SV_CloseEnough
======================
*/
qboolean
SV_CloseEnough(edict_t *ent, edict_t *goal, float dist)
{
int i;
for (i = 0; i < 3; i++) {
if (goal->v.absmin[i] > ent->v.absmax[i] + dist)
return false;
if (goal->v.absmax[i] < ent->v.absmin[i] - dist)
return false;
}
return true;
}
/*
======================
SV_MoveToGoal
======================
*/
void
SV_MoveToGoal(void)
{
edict_t *ent, *goal;
float dist;
ent = PROG_TO_EDICT(pr_global_struct->self);
goal = PROG_TO_EDICT(ent->v.goalentity);
dist = G_FLOAT(OFS_PARM0);
if (!((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) {
G_FLOAT(OFS_RETURN) = 0;
return;
}
// if the next step hits the enemy, return immediately
if (PROG_TO_EDICT(ent->v.enemy) != sv.edicts
&& SV_CloseEnough(ent, goal, dist))
return;
// bump around...
if ((rand() & 3) == 1 || !SV_StepDirection(ent, ent->v.ideal_yaw, dist)) {
SV_NewChaseDir(ent, goal, dist);
}
}

1191
NQ/sv_phys.c Normal file

File diff suppressed because it is too large Load Diff

630
NQ/sv_user.c Normal file
View File

@ -0,0 +1,630 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// sv_user.c -- server code for moving users
#include "client.h"
#include "cmd.h"
#include "console.h"
#include "host.h"
#include "keys.h"
#include "net.h"
#include "progs.h"
#include "protocol.h"
#include "quakedef.h"
#include "server.h"
#include "sys.h"
#include "view.h"
#include "world.h"
edict_t *sv_player;
cvar_t sv_edgefriction = { "edgefriction", "2" };
static vec3_t forward, right, up;
vec3_t wishdir;
float wishspeed;
// world
float *angles;
float *origin;
float *velocity;
qboolean onground;
usercmd_t cmd;
cvar_t sv_idealpitchscale = { "sv_idealpitchscale", "0.8" };
/*
===============
SV_SetIdealPitch
===============
*/
#define MAX_FORWARD 6
void
SV_SetIdealPitch(void)
{
float angleval, sinval, cosval;
trace_t tr;
vec3_t top, bottom;
float z[MAX_FORWARD];
int i, j;
int step, dir, steps;
if (!((int)sv_player->v.flags & FL_ONGROUND))
return;
angleval = sv_player->v.angles[YAW] * M_PI * 2 / 360;
sinval = sin(angleval);
cosval = cos(angleval);
for (i = 0; i < MAX_FORWARD; i++) {
top[0] = sv_player->v.origin[0] + cosval * (i + 3) * 12;
top[1] = sv_player->v.origin[1] + sinval * (i + 3) * 12;
top[2] = sv_player->v.origin[2] + sv_player->v.view_ofs[2];
bottom[0] = top[0];
bottom[1] = top[1];
bottom[2] = top[2] - 160;
tr = SV_Move(top, vec3_origin, vec3_origin, bottom, 1, sv_player);
if (tr.allsolid)
return; // looking at a wall, leave ideal the way is was
if (tr.fraction == 1)
return; // near a dropoff
z[i] = top[2] + tr.fraction * (bottom[2] - top[2]);
}
dir = 0;
steps = 0;
for (j = 1; j < i; j++) {
step = z[j] - z[j - 1];
if (step > -ON_EPSILON && step < ON_EPSILON)
continue;
if (dir && (step - dir > ON_EPSILON || step - dir < -ON_EPSILON))
return; // mixed changes
steps++;
dir = step;
}
if (!dir) {
sv_player->v.idealpitch = 0;
return;
}
if (steps < 2)
return;
sv_player->v.idealpitch = -dir * sv_idealpitchscale.value;
}
/*
==================
SV_UserFriction
==================
*/
void
SV_UserFriction(void)
{
float *vel;
float speed, newspeed, control;
vec3_t start, stop;
float friction;
trace_t trace;
vel = velocity;
speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]);
if (!speed)
return;
// if the leading edge is over a dropoff, increase friction
start[0] = stop[0] = origin[0] + vel[0] / speed * 16;
start[1] = stop[1] = origin[1] + vel[1] / speed * 16;
start[2] = origin[2] + sv_player->v.mins[2];
stop[2] = start[2] - 34;
trace = SV_Move(start, vec3_origin, vec3_origin, stop, true, sv_player);
if (trace.fraction == 1.0)
friction = sv_friction.value * sv_edgefriction.value;
else
friction = sv_friction.value;
// apply friction
control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed;
newspeed = speed - host_frametime * control * friction;
if (newspeed < 0)
newspeed = 0;
newspeed /= speed;
vel[0] = vel[0] * newspeed;
vel[1] = vel[1] * newspeed;
vel[2] = vel[2] * newspeed;
}
/*
==============
SV_Accelerate
==============
*/
cvar_t sv_maxspeed = { "sv_maxspeed", "320", false, true };
cvar_t sv_accelerate = { "sv_accelerate", "10" };
#if 0
void
SV_Accelerate(vec3_t wishvel)
{
int i;
float addspeed, accelspeed;
vec3_t pushvec;
if (wishspeed == 0)
return;
VectorSubtract(wishvel, velocity, pushvec);
addspeed = VectorNormalize(pushvec);
accelspeed = sv_accelerate.value * host_frametime * addspeed;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i = 0; i < 3; i++)
velocity[i] += accelspeed * pushvec[i];
}
#endif
void
SV_Accelerate(void)
{
int i;
float addspeed, accelspeed, currentspeed;
currentspeed = DotProduct(velocity, wishdir);
addspeed = wishspeed - currentspeed;
if (addspeed <= 0)
return;
accelspeed = sv_accelerate.value * host_frametime * wishspeed;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i = 0; i < 3; i++)
velocity[i] += accelspeed * wishdir[i];
}
void
SV_AirAccelerate(vec3_t wishveloc)
{
int i;
float addspeed, wishspd, accelspeed, currentspeed;
wishspd = VectorNormalize(wishveloc);
if (wishspd > 30)
wishspd = 30;
currentspeed = DotProduct(velocity, wishveloc);
addspeed = wishspd - currentspeed;
if (addspeed <= 0)
return;
// accelspeed = sv_accelerate.value * host_frametime;
accelspeed = sv_accelerate.value * wishspeed * host_frametime;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i = 0; i < 3; i++)
velocity[i] += accelspeed * wishveloc[i];
}
void
DropPunchAngle(void)
{
float len;
len = VectorNormalize(sv_player->v.punchangle);
len -= 10 * host_frametime;
if (len < 0)
len = 0;
VectorScale(sv_player->v.punchangle, len, sv_player->v.punchangle);
}
/*
===================
SV_WaterMove
===================
*/
void
SV_WaterMove(void)
{
int i;
vec3_t wishvel;
float speed, newspeed, wishspeed, addspeed, accelspeed;
//
// user intentions
//
AngleVectors(sv_player->v.v_angle, forward, right, up);
for (i = 0; i < 3; i++)
wishvel[i] = forward[i] * cmd.forwardmove + right[i] * cmd.sidemove;
if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove)
wishvel[2] -= 60; // drift towards bottom
else
wishvel[2] += cmd.upmove;
wishspeed = Length(wishvel);
if (wishspeed > sv_maxspeed.value) {
VectorScale(wishvel, sv_maxspeed.value / wishspeed, wishvel);
wishspeed = sv_maxspeed.value;
}
wishspeed *= 0.7;
//
// water friction
//
speed = Length(velocity);
if (speed) {
newspeed = speed - host_frametime * speed * sv_friction.value;
if (newspeed < 0)
newspeed = 0;
VectorScale(velocity, newspeed / speed, velocity);
} else
newspeed = 0;
//
// water acceleration
//
if (!wishspeed)
return;
addspeed = wishspeed - newspeed;
if (addspeed <= 0)
return;
VectorNormalize(wishvel);
accelspeed = sv_accelerate.value * wishspeed * host_frametime;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i = 0; i < 3; i++)
velocity[i] += accelspeed * wishvel[i];
}
void
SV_WaterJump(void)
{
if (sv.time > sv_player->v.teleport_time || !sv_player->v.waterlevel) {
sv_player->v.flags = (int)sv_player->v.flags & ~FL_WATERJUMP;
sv_player->v.teleport_time = 0;
}
sv_player->v.velocity[0] = sv_player->v.movedir[0];
sv_player->v.velocity[1] = sv_player->v.movedir[1];
}
/*
===================
SV_AirMove
===================
*/
void
SV_AirMove(void)
{
int i;
vec3_t wishvel;
float fmove, smove;
AngleVectors(sv_player->v.angles, forward, right, up);
fmove = cmd.forwardmove;
smove = cmd.sidemove;
// hack to not let you back into teleporter
if (sv.time < sv_player->v.teleport_time && fmove < 0)
fmove = 0;
for (i = 0; i < 3; i++)
wishvel[i] = forward[i] * fmove + right[i] * smove;
if ((int)sv_player->v.movetype != MOVETYPE_WALK)
wishvel[2] = cmd.upmove;
else
wishvel[2] = 0;
VectorCopy(wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
if (wishspeed > sv_maxspeed.value) {
VectorScale(wishvel, sv_maxspeed.value / wishspeed, wishvel);
wishspeed = sv_maxspeed.value;
}
if (sv_player->v.movetype == MOVETYPE_NOCLIP) { // noclip
VectorCopy(wishvel, velocity);
} else if (onground) {
SV_UserFriction();
SV_Accelerate();
} else { // not on ground, so little effect on velocity
SV_AirAccelerate(wishvel);
}
}
/*
===================
SV_ClientThink
the move fields specify an intended velocity in pix/sec
the angle fields specify an exact angular motion in degrees
===================
*/
void
SV_ClientThink(void)
{
vec3_t v_angle;
if (sv_player->v.movetype == MOVETYPE_NONE)
return;
onground = (int)sv_player->v.flags & FL_ONGROUND;
origin = sv_player->v.origin;
velocity = sv_player->v.velocity;
DropPunchAngle();
//
// if dead, behave differently
//
if (sv_player->v.health <= 0)
return;
//
// angles
// show 1/3 the pitch angle and all the roll angle
cmd = host_client->cmd;
angles = sv_player->v.angles;
VectorAdd(sv_player->v.v_angle, sv_player->v.punchangle, v_angle);
angles[ROLL] = V_CalcRoll(sv_player->v.angles, sv_player->v.velocity) * 4;
if (!sv_player->v.fixangle) {
angles[PITCH] = -v_angle[PITCH] / 3;
angles[YAW] = v_angle[YAW];
}
if ((int)sv_player->v.flags & FL_WATERJUMP) {
SV_WaterJump();
return;
}
//
// walk
//
if ((sv_player->v.waterlevel >= 2)
&& (sv_player->v.movetype != MOVETYPE_NOCLIP)) {
SV_WaterMove();
return;
}
SV_AirMove();
}
/*
===================
SV_ReadClientMove
===================
*/
void
SV_ReadClientMove(usercmd_t *move)
{
int i;
vec3_t angle;
int bits;
// read ping time
host_client->ping_times[host_client->num_pings % NUM_PING_TIMES]
= sv.time - MSG_ReadFloat();
host_client->num_pings++;
// read current angles
for (i = 0; i < 3; i++)
angle[i] = MSG_ReadAngle();
VectorCopy(angle, host_client->edict->v.v_angle);
// read movement
move->forwardmove = MSG_ReadShort();
move->sidemove = MSG_ReadShort();
move->upmove = MSG_ReadShort();
// read buttons
bits = MSG_ReadByte();
host_client->edict->v.button0 = bits & 1;
host_client->edict->v.button2 = (bits & 2) >> 1;
i = MSG_ReadByte();
if (i)
host_client->edict->v.impulse = i;
#ifdef QUAKE2
// read light level
host_client->edict->v.light_level = MSG_ReadByte();
#endif
}
/*
===================
SV_ReadClientMessage
Returns false if the client should be killed
===================
*/
qboolean
SV_ReadClientMessage(void)
{
int ret;
int cmd;
char *s;
do {
nextmsg:
ret = NET_GetMessage(host_client->netconnection);
if (ret == -1) {
Sys_Printf("%s: NET_GetMessage failed\n", __func__);
return false;
}
if (!ret)
return true;
MSG_BeginReading();
while (1) {
if (!host_client->active)
return false; // a command caused an error
if (msg_badread) {
Sys_Printf("%s: badread\n", __func__);
return false;
}
cmd = MSG_ReadChar();
switch (cmd) {
case -1:
goto nextmsg; // end of message
default:
Sys_Printf("%s: unknown command char\n", __func__);
return false;
case clc_nop:
//Sys_Printf ("clc_nop\n");
break;
case clc_stringcmd:
s = MSG_ReadString();
if (host_client->privileged)
ret = 2;
else
ret = 0;
if (strncasecmp(s, "status", 6) == 0)
ret = 1;
else if (strncasecmp(s, "god", 3) == 0)
ret = 1;
else if (strncasecmp(s, "notarget", 8) == 0)
ret = 1;
else if (strncasecmp(s, "fly", 3) == 0)
ret = 1;
else if (strncasecmp(s, "name", 4) == 0)
ret = 1;
else if (strncasecmp(s, "noclip", 6) == 0)
ret = 1;
else if (strncasecmp(s, "say", 3) == 0)
ret = 1;
else if (strncasecmp(s, "say_team", 8) == 0)
ret = 1;
else if (strncasecmp(s, "tell", 4) == 0)
ret = 1;
else if (strncasecmp(s, "color", 5) == 0)
ret = 1;
else if (strncasecmp(s, "kill", 4) == 0)
ret = 1;
else if (strncasecmp(s, "pause", 5) == 0)
ret = 1;
else if (strncasecmp(s, "spawn", 5) == 0)
ret = 1;
else if (strncasecmp(s, "begin", 5) == 0)
ret = 1;
else if (strncasecmp(s, "prespawn", 8) == 0)
ret = 1;
else if (strncasecmp(s, "kick", 4) == 0)
ret = 1;
else if (strncasecmp(s, "ping", 4) == 0)
ret = 1;
else if (strncasecmp(s, "give", 4) == 0)
ret = 1;
else if (strncasecmp(s, "ban", 3) == 0)
ret = 1;
if (ret == 1)
Cmd_ExecuteString(s, src_client);
else if (ret == 2)
Cbuf_InsertText(s);
else
Con_DPrintf("%s tried to %s\n", host_client->name, s);
break;
case clc_disconnect:
//Sys_Printf ("%s: client disconnected\n", __func__);
return false;
case clc_move:
SV_ReadClientMove(&host_client->cmd);
break;
}
}
} while (ret == 1);
return true;
}
/*
==================
SV_RunClients
==================
*/
void
SV_RunClients(void)
{
int i;
for (i = 0, host_client = svs.clients; i < svs.maxclients;
i++, host_client++) {
if (!host_client->active)
continue;
sv_player = host_client->edict;
if (!SV_ReadClientMessage()) {
/* client misbehaved... */
SV_DropClient(false);
continue;
}
if (!host_client->spawned) {
/* clear client movement until a new packet is received */
memset(&host_client->cmd, 0, sizeof(host_client->cmd));
continue;
}
/* always pause in single player if in console or menus */
if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game))
SV_ClientThink();
}
}

434
NQ/sys_linux.c Normal file
View File

@ -0,0 +1,434 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <errno.h>
#include "client.h"
#include "common.h"
#include "host.h"
#include "net_vcr.h"
#include "quakedef.h"
#include "sys.h"
qboolean isDedicated;
static qboolean noconinput = false;
static qboolean nostdout = false;
char *basedir = ".";
char *cachedir = "/tmp";
// FIXME - Used in NQ, not QW... why?
// set for entity display
cvar_t sys_linerefresh = { "sys_linerefresh", "0" };
// =======================================================================
// General routines
// =======================================================================
void
Sys_Printf(char *fmt, ...)
{
va_list argptr;
char text[2048];
unsigned char *p;
int cnt;
va_start(argptr, fmt);
cnt = vsnprintf(text, sizeof(text) - 1, fmt, argptr);
va_end(argptr);
// FIXME - require glibc >= 2.1 for C99 standard return value
if (cnt >= sizeof(text))
Sys_Error("memory overwrite in Sys_Printf");
if (nostdout)
return;
// FIXME - compare with NQ + use ctype functions?
for (p = (unsigned char *)text; *p; p++) {
if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)
printf("[%02x]", *p);
else
putc(*p, stdout);
}
}
#if 0
static char end1[] =
"\x1b[?7h\x1b[40m\x1b[2J\x1b[0;1;41m\x1b[1;1H QUAKE: The Doomed Dimension \x1b[33mby \x1b[44mid\x1b[41m Software \x1b[2;1H ---------------------------------------------------------------------------- \x1b[3;1H CALL 1-800-IDGAMES TO ORDER OR FOR TECHNICAL SUPPORT \x1b[4;1H PRICE: $45.00 (PRICES MAY VARY OUTSIDE THE US.) \x1b[5;1H \x1b[6;1H \x1b[37mYes! You only have one fourth of this incredible epic. That is because most \x1b[7;1H of you have paid us nothing or at most, very little. You could steal the \x1b[8;1H game from a friend. But we both know you'll be punished by God if you do. \x1b[9;1H \x1b[33mWHY RISK ETERNAL DAMNATION? CALL 1-800-IDGAMES AND BUY NOW! \x1b[10;1H \x1b[37mRemember, we love you almost as much as He does. \x1b[11;1H \x1b[12;1H \x1b[33mProgramming: \x1b[37mJohn Carmack, Michael Abrash, John Cash \x1b[13;1H \x1b[33mDesign: \x1b[37mJohn Romero, Sandy Petersen, American McGee, Tim Willits \x1b[14;1H \x1b[33mArt: \x1b[37mAdrian Carmack, Kevin Cloud \x1b[15;1H \x1b[33mBiz: \x1b[37mJay Wilbur, Mike Wilson, Donna Jackson \x1b[16;1H \x1b[33mProjects: \x1b[37mShawn Green \x1b[33mSupport: \x1b[37mBarrett Alexander \x1b[17;1H \x1b[33mSound Effects: \x1b[37mTrent Reznor and Nine Inch Nails \x1b[18;1H For other information or details on ordering outside the US, check out the \x1b[19;1H files accompanying QUAKE or our website at http://www.idsoftware.com. \x1b[20;1H \x1b[0;41mQuake is a trademark of Id Software, inc., (c)1996 Id Software, inc. \x1b[21;1H All rights reserved. NIN logo is a registered trademark licensed to \x1b[22;1H Nothing Interactive, Inc. All rights reserved. \x1b[40m\x1b[23;1H\x1b[0m";
static char end2[] =
"\x1b[?7h\x1b[40m\x1b[2J\x1b[0;1;41m\x1b[1;1H QUAKE \x1b[33mby \x1b[44mid\x1b[41m Software \x1b[2;1H ----------------------------------------------------------------------------- \x1b[3;1H \x1b[37mWhy did you quit from the registered version of QUAKE? Did the \x1b[4;1H scary monsters frighten you? Or did Mr. Sandman tug at your \x1b[5;1H little lids? No matter! What is important is you love our \x1b[6;1H game, and gave us your money. Congratulations, you are probably \x1b[7;1H not a thief. \x1b[8;1H Thank You. \x1b[9;1H \x1b[33;44mid\x1b[41m Software is: \x1b[10;1H PROGRAMMING: \x1b[37mJohn Carmack, Michael Abrash, John Cash \x1b[11;1H \x1b[33mDESIGN: \x1b[37mJohn Romero, Sandy Petersen, American McGee, Tim Willits \x1b[12;1H \x1b[33mART: \x1b[37mAdrian Carmack, Kevin Cloud \x1b[13;1H \x1b[33mBIZ: \x1b[37mJay Wilbur, Mike Wilson \x1b[33mPROJECTS MAN: \x1b[37mShawn Green \x1b[14;1H \x1b[33mBIZ ASSIST: \x1b[37mDonna Jackson \x1b[33mSUPPORT: \x1b[37mBarrett Alexander \x1b[15;1H \x1b[33mSOUND EFFECTS AND MUSIC: \x1b[37mTrent Reznor and Nine Inch Nails \x1b[16;1H \x1b[17;1H If you need help running QUAKE refer to the text files in the \x1b[18;1H QUAKE directory, or our website at http://www.idsoftware.com. \x1b[19;1H If all else fails, call our technical support at 1-800-IDGAMES. \x1b[20;1H \x1b[0;41mQuake is a trademark of Id Software, inc., (c)1996 Id Software, inc. \x1b[21;1H All rights reserved. NIN logo is a registered trademark licensed \x1b[22;1H to Nothing Interactive, Inc. All rights reserved. \x1b[23;1H\x1b[40m\x1b[0m";
#endif
void
Sys_Quit(void)
{
Host_Shutdown();
fcntl(STDIN_FILENO, F_SETFL,
fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
#if 0
if (registered.value)
printf("%s", end2);
else
printf("%s", end1);
#endif
fflush(stdout);
exit(0);
}
void
Sys_Init(void)
{
#if id386
Sys_SetFPCW();
#endif
}
void
Sys_Error(char *error, ...)
{
va_list argptr;
char string[1024];
// change stdin to non blocking
fcntl(STDIN_FILENO, F_SETFL,
fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
va_start(argptr, error);
vsprintf(string, error, argptr);
va_end(argptr);
fprintf(stderr, "Error: %s\n", string);
Host_Shutdown();
exit(1);
}
/*
============
Sys_FileTime
returns -1 if not present
============
*/
int
Sys_FileTime(const char *path)
{
struct stat buf;
if (stat(path, &buf) == -1)
return -1;
return buf.st_mtime;
}
void
Sys_mkdir(const char *path)
{
mkdir(path, 0777);
}
int
Sys_FileOpenRead(const char *path, int *handle)
{
int h;
struct stat fileinfo;
h = open(path, O_RDONLY, 0666);
*handle = h;
if (h == -1)
return -1;
if (fstat(h, &fileinfo) == -1)
Sys_Error("Error fstating %s", path);
return fileinfo.st_size;
}
int
Sys_FileOpenWrite(const char *path)
{
int handle;
umask(0);
handle = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (handle == -1)
Sys_Error("Error opening %s: %s", path, strerror(errno));
return handle;
}
int
Sys_FileWrite(int handle, const void *src, int count)
{
return write(handle, src, count);
}
void
Sys_FileClose(int handle)
{
close(handle);
}
void
Sys_FileSeek(int handle, int position)
{
lseek(handle, position, SEEK_SET);
}
int
Sys_FileRead(int handle, void *dest, int count)
{
return read(handle, dest, count);
}
void
Sys_DebugLog(char *file, char *fmt, ...)
{
va_list argptr;
static char data[1024];
int fd;
va_start(argptr, fmt);
vsprintf(data, fmt, argptr);
va_end(argptr);
// fd = open(file, O_WRONLY | O_BINARY | O_CREAT | O_APPEND, 0666);
fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
write(fd, data, strlen(data));
close(fd);
}
void
Sys_EditFile(char *filename)
{
char cmd[256];
char *term;
char *editor;
term = getenv("TERM");
if (term && !strcmp(term, "xterm")) {
editor = getenv("VISUAL");
if (!editor)
editor = getenv("EDITOR");
if (!editor)
editor = getenv("EDIT");
if (!editor)
editor = "vi";
sprintf(cmd, "xterm -e %s %s", editor, filename);
system(cmd);
}
}
double
Sys_DoubleTime(void)
{
struct timeval tp;
struct timezone tzp;
static int secbase;
gettimeofday(&tp, &tzp);
if (!secbase) {
secbase = tp.tv_sec;
return tp.tv_usec / 1000000.0;
}
return (tp.tv_sec - secbase) + tp.tv_usec / 1000000.0;
}
// =======================================================================
// Sleeps for microseconds
// =======================================================================
/* FIXME - Unused only in QW? */
static void
Sys_LineRefresh(void)
{
}
static void
floating_point_exception_handler(int whatever)
{
// Sys_Warn("floating point exception\n");
signal(SIGFPE, floating_point_exception_handler);
}
// FIXME - need this at all? (see QW)
char *
Sys_ConsoleInput(void)
{
static char text[256];
int len;
fd_set fdset;
struct timeval timeout;
if (cls.state == ca_dedicated) {
FD_ZERO(&fdset);
FD_SET(STDIN_FILENO, &fdset); // stdin
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (select(STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout) == -1
|| !FD_ISSET(STDIN_FILENO, &fdset))
return NULL;
len = read(STDIN_FILENO, text, sizeof(text));
if (len < 1)
return NULL;
text[len - 1] = 0; // rip off the /n and terminate
return text;
}
return NULL;
}
#if !id386
void
Sys_HighFPPrecision(void)
{
}
void
Sys_LowFPPrecision(void)
{
}
#endif
int
main(int c, char **v)
{
double time, oldtime, newtime;
quakeparms_t parms;
int j;
// signal(SIGFPE, floating_point_exception_handler);
signal(SIGFPE, SIG_IGN);
memset(&parms, 0, sizeof(parms));
COM_InitArgv(c, v);
parms.argc = com_argc;
parms.argv = com_argv;
#ifdef GLQUAKE
parms.memsize = 16 * 1024 * 1024;
#else
parms.memsize = 8 * 1024 * 1024;
#endif
j = COM_CheckParm("-mem");
if (j)
parms.memsize = (int)(Q_atof(com_argv[j + 1]) * 1024 * 1024);
parms.membase = malloc(parms.memsize);
parms.basedir = basedir;
// caching is disabled by default, use -cachedir to enable
// parms.cachedir = cachedir;
fcntl(STDIN_FILENO, F_SETFL,
fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK);
Host_Init(&parms);
Sys_Init();
if (COM_CheckParm("-nostdout"))
nostdout = true;
// Make stdin non-blocking
// FIXME - check both return values
if (!noconinput)
fcntl(STDIN_FILENO, F_SETFL,
fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK);
if (!nostdout)
printf("Tyr-Quake -- Version %s\n", stringify(TYR_VERSION));
oldtime = Sys_DoubleTime() - 0.1;
while (1) {
// find time spent rendering last frame
newtime = Sys_DoubleTime();
time = newtime - oldtime;
if (cls.state == ca_dedicated) { // play vcrfiles at max speed
if (time < sys_ticrate.value && (vcrFile == -1 || recording)) {
usleep(1);
continue; // not time to run a server only tic yet
}
time = sys_ticrate.value;
}
if (time > sys_ticrate.value * 2)
oldtime = newtime;
else
oldtime += time;
Host_Frame(time);
// graphic debugging aids
if (sys_linerefresh.value)
Sys_LineRefresh();
}
}
/*
================
Sys_MakeCodeWriteable
================
*/
void
Sys_MakeCodeWriteable(unsigned long startaddr, unsigned long length)
{
int r;
unsigned long addr;
int psize = getpagesize();
addr = (startaddr & ~(psize - 1)) - psize;
// fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr,
// addr, startaddr+length, length);
r = mprotect((char *)addr, length + startaddr - addr + psize, 7);
if (r < 0)
Sys_Error("Protection change failed");
}

937
NQ/sys_win.c Normal file
View File

@ -0,0 +1,937 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// sys_win.c -- Win32 system interface code
#include <direct.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include "client.h"
#include "common.h"
#include "conproc.h"
#include "host.h"
#include "quakedef.h"
#include "resource.h"
#include "screen.h"
#include "sys.h"
#include "winquake.h"
#define MINIMUM_WIN_MEMORY 0x0C00000 /* 12 MB */
#define MAXIMUM_WIN_MEMORY 0x2000000 /* 32 MB */
#define CONSOLE_ERROR_TIMEOUT 60.0 // # of seconds to wait on Sys_Error
// running dedicated before exiting
#define PAUSE_SLEEP 50 // sleep time on pause or minimization
#define NOT_FOCUS_SLEEP 20 // sleep time when not focus
qboolean ActiveApp;
qboolean WinNT;
static double pfreq;
static double curtime = 0.0;
static double lastcurtime = 0.0;
static int lowshift;
qboolean isDedicated;
static qboolean sc_return_on_enter = false;
HANDLE hinput, houtput;
static HANDLE tevent;
static HANDLE hFile;
static HANDLE heventParent;
static HANDLE heventChild;
void MaskExceptions(void);
void Sys_InitFloatTime(void);
void Sys_PushFPCW_SetHigh(void);
void Sys_PopFPCW(void);
volatile int sys_checksum;
// TYR - debugging...
static void printf_sys_error(DWORD err);
void
Sys_DebugLog(char *file, char *fmt, ...)
{
va_list argptr;
static char data[1024];
int fd;
va_start(argptr, fmt);
vsprintf(data, fmt, argptr);
va_end(argptr);
fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
write(fd, data, strlen(data));
close(fd);
};
/*
================
Sys_PageIn
================
*/
void
Sys_PageIn(void *ptr, int size)
{
byte *x;
int m, n;
// touch all the memory to make sure it's there. The 16-page skip is to
// keep Win 95 from thinking we're trying to page ourselves in (we are
// doing that, of course, but there's no reason we shouldn't)
x = (byte *)ptr;
for (n = 0; n < 4; n++) {
for (m = 0; m < (size - 16 * 0x1000); m += 4) {
sys_checksum += *(int *)&x[m];
sys_checksum += *(int *)&x[m + 16 * 0x1000];
}
}
}
/*
===============================================================================
FILE IO
===============================================================================
*/
#define MAX_HANDLES 10
FILE *sys_handles[MAX_HANDLES];
static int
findhandle(void)
{
int i;
for (i = 1; i < MAX_HANDLES; i++)
if (!sys_handles[i])
return i;
Sys_Error("out of handles");
return -1;
}
/*
================
Sys_filelength
================
*/
int
Sys_filelength(FILE *f)
{
int pos;
int end;
int t;
t = VID_ForceUnlockedAndReturnState();
pos = ftell(f);
fseek(f, 0, SEEK_END);
end = ftell(f);
fseek(f, pos, SEEK_SET);
VID_ForceLockState(t);
return end;
}
int
Sys_FileOpenRead(const char *path, int *hndl)
{
FILE *f;
int i, retval;
int t;
t = VID_ForceUnlockedAndReturnState();
i = findhandle();
f = fopen(path, "rb");
if (!f) {
*hndl = -1;
retval = -1;
} else {
sys_handles[i] = f;
*hndl = i;
retval = Sys_filelength(f);
}
VID_ForceLockState(t);
return retval;
}
int
Sys_FileOpenWrite(const char *path)
{
FILE *f;
int i;
int t;
t = VID_ForceUnlockedAndReturnState();
i = findhandle();
f = fopen(path, "wb");
if (!f)
Sys_Error("Error opening %s: %s", path, strerror(errno));
sys_handles[i] = f;
VID_ForceLockState(t);
return i;
}
void
Sys_FileClose(int handle)
{
int t;
t = VID_ForceUnlockedAndReturnState();
fclose(sys_handles[handle]);
sys_handles[handle] = NULL;
VID_ForceLockState(t);
}
void
Sys_FileSeek(int handle, int position)
{
int t;
t = VID_ForceUnlockedAndReturnState();
fseek(sys_handles[handle], position, SEEK_SET);
VID_ForceLockState(t);
}
int
Sys_FileRead(int handle, void *dest, int count)
{
int t, x;
t = VID_ForceUnlockedAndReturnState();
x = fread(dest, 1, count, sys_handles[handle]);
VID_ForceLockState(t);
return x;
}
int
Sys_FileWrite(int handle, const void *data, int count)
{
int t, x;
t = VID_ForceUnlockedAndReturnState();
x = fwrite(data, 1, count, sys_handles[handle]);
VID_ForceLockState(t);
return x;
}
int
Sys_FileTime(const char *path)
{
FILE *f;
int t, retval;
t = VID_ForceUnlockedAndReturnState();
f = fopen(path, "rb");
if (f) {
fclose(f);
retval = 1;
} else {
retval = -1;
}
VID_ForceLockState(t);
return retval;
}
void
Sys_mkdir(const char *path)
{
_mkdir(path);
}
/*
===============================================================================
SYSTEM IO
===============================================================================
*/
/*
================
Sys_MakeCodeWriteable
================
*/
void
Sys_MakeCodeWriteable(unsigned long startaddr, unsigned long length)
{
DWORD flOldProtect;
if (!VirtualProtect
((LPVOID)startaddr, length, PAGE_READWRITE, &flOldProtect))
Sys_Error("Protection change failed");
}
#if !id386
void
Sys_SetFPCW(void)
{
}
void
Sys_PushFPCW_SetHigh(void)
{
}
void
Sys_PopFPCW(void)
{
}
void
MaskExceptions(void)
{
}
#endif
/*
================
Sys_Init
================
*/
void
Sys_Init(void)
{
LARGE_INTEGER PerformanceFreq;
unsigned int lowpart, highpart;
OSVERSIONINFO vinfo;
MaskExceptions();
Sys_SetFPCW();
if (!QueryPerformanceFrequency(&PerformanceFreq))
Sys_Error("No hardware timer available");
// get 32 out of the 64 time bits such that we have around
// 1 microsecond resolution
lowpart = (unsigned int)PerformanceFreq.LowPart;
highpart = (unsigned int)PerformanceFreq.HighPart;
lowshift = 0;
while (highpart || (lowpart > 2000000.0)) {
lowshift++;
lowpart >>= 1;
lowpart |= (highpart & 1) << 31;
highpart >>= 1;
}
pfreq = 1.0 / (double)lowpart;
Sys_InitFloatTime();
vinfo.dwOSVersionInfoSize = sizeof(vinfo);
if (!GetVersionEx(&vinfo))
Sys_Error("Couldn't get OS info");
if ((vinfo.dwMajorVersion < 4) ||
(vinfo.dwPlatformId == VER_PLATFORM_WIN32s)) {
Sys_Error("Tyr-Quake requires at least Win95 or NT 4.0");
}
if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
WinNT = true;
else
WinNT = false;
}
void
Sys_Error(char *error, ...)
{
va_list argptr;
char text[1024], text2[1024];
char *text3 = "Press Enter to exit\n";
char *text4 = "***********************************\n";
char *text5 = "\n";
DWORD dummy;
double starttime;
static int in_sys_error0 = 0;
static int in_sys_error1 = 0;
static int in_sys_error2 = 0;
static int in_sys_error3 = 0;
if (!in_sys_error3) {
in_sys_error3 = 1;
VID_ForceUnlockedAndReturnState();
}
va_start(argptr, error);
vsprintf(text, error, argptr);
va_end(argptr);
if (isDedicated) {
va_start(argptr, error);
vsprintf(text, error, argptr);
va_end(argptr);
sprintf(text2, "ERROR: %s\n", text);
WriteFile(houtput, text5, strlen(text5), &dummy, NULL);
WriteFile(houtput, text4, strlen(text4), &dummy, NULL);
WriteFile(houtput, text2, strlen(text2), &dummy, NULL);
WriteFile(houtput, text3, strlen(text3), &dummy, NULL);
WriteFile(houtput, text4, strlen(text4), &dummy, NULL);
starttime = Sys_DoubleTime();
sc_return_on_enter = true; // so Enter will get us out of here
while (!Sys_ConsoleInput() &&
((Sys_DoubleTime() - starttime) < CONSOLE_ERROR_TIMEOUT)) {
}
} else {
// switch to windowed so the message box is visible, unless we already
// tried that and failed
if (!in_sys_error0) {
in_sys_error0 = 1;
VID_SetDefaultMode();
MessageBox(NULL, text, "Quake Error",
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
} else {
MessageBox(NULL, text, "Double Quake Error",
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
}
}
if (!in_sys_error1) {
in_sys_error1 = 1;
Host_Shutdown();
}
// shut down QHOST hooks if necessary
if (!in_sys_error2) {
in_sys_error2 = 1;
DeinitConProc();
}
exit(1);
}
void
Sys_Printf(char *fmt, ...)
{
va_list argptr;
char text[1024];
DWORD dummy;
if (isDedicated) {
va_start(argptr, fmt);
vsprintf(text, fmt, argptr);
va_end(argptr);
WriteFile(houtput, text, strlen(text), &dummy, NULL);
}
}
void
Sys_Quit(void)
{
VID_ForceUnlockedAndReturnState();
Host_Shutdown();
if (tevent)
CloseHandle(tevent);
if (isDedicated)
FreeConsole();
// shut down QHOST hooks if necessary
DeinitConProc();
exit(0);
}
/*
================
Sys_DoubleTime
================
*/
double
Sys_DoubleTime(void)
{
static int sametimecount;
static unsigned int oldtime;
static int first = 1;
LARGE_INTEGER PerformanceCount;
unsigned int temp, t2;
double time;
Sys_PushFPCW_SetHigh();
QueryPerformanceCounter(&PerformanceCount);
temp = ((unsigned int)PerformanceCount.LowPart >> lowshift) |
((unsigned int)PerformanceCount.HighPart << (32 - lowshift));
if (first) {
oldtime = temp;
first = 0;
} else {
// check for turnover or backward time
if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000)) {
oldtime = temp; // so we can't get stuck
} else {
t2 = temp - oldtime;
time = (double)t2 *pfreq;
oldtime = temp;
curtime += time;
if (curtime == lastcurtime) {
sametimecount++;
if (sametimecount > 100000) {
curtime += 1.0;
sametimecount = 0;
}
} else {
sametimecount = 0;
}
lastcurtime = curtime;
}
}
Sys_PopFPCW();
return curtime;
}
/*
================
Sys_InitFloatTime
================
*/
void
Sys_InitFloatTime(void)
{
int j;
Sys_DoubleTime();
j = COM_CheckParm("-starttime");
if (j) {
curtime = (double)(Q_atof(com_argv[j + 1]));
} else {
curtime = 0.0;
}
lastcurtime = curtime;
}
char *
Sys_ConsoleInput(void)
{
static char text[256];
static int len;
INPUT_RECORD recs[1024];
DWORD dummy;
int ch;
DWORD numread, numevents;
if (!isDedicated)
return NULL;
for (;;) {
if (!GetNumberOfConsoleInputEvents(hinput, &numevents)) {
DWORD err = GetLastError();
printf("GetNumberOfConsoleInputEvents: ");
printf_sys_error(err);
Sys_Error("Error getting # of console events");
}
if (numevents <= 0)
break;
if (!ReadConsoleInput(hinput, recs, 1, &numread))
Sys_Error("Error reading console input");
if (numread != 1)
Sys_Error("Couldn't read console input");
if (recs[0].EventType == KEY_EVENT) {
if (!recs[0].Event.KeyEvent.bKeyDown) {
ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
switch (ch) {
case '\r':
WriteFile(houtput, "\r\n", 2, &dummy, NULL);
if (len) {
text[len] = 0;
len = 0;
return text;
} else if (sc_return_on_enter) {
// special case to allow exiting from the error handler on Enter
text[0] = '\r';
len = 0;
return text;
}
break;
case '\b':
WriteFile(houtput, "\b \b", 3, &dummy, NULL);
if (len) {
len--;
}
break;
default:
if (ch >= ' ') {
WriteFile(houtput, &ch, 1, &dummy, NULL);
text[len] = ch;
len = (len + 1) & 0xff;
}
break;
}
}
}
}
return NULL;
}
void
Sys_Sleep(void)
{
Sleep(1);
}
void
Sys_SendKeyEvents(void)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
// we always update if there are any event, even if we're paused
scr_skipupdate = 0;
if (!GetMessage(&msg, NULL, 0, 0))
Sys_Quit();
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/*
==============================================================================
WINDOWS CRAP
==============================================================================
*/
/*
==================
WinMain
==================
*/
void
SleepUntilInput(int time)
{
MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT);
}
// TYR - used for debugging...
static void
printf_sys_error(DWORD err)
{
static PVOID buf;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM,
NULL, err, 0, (LPTSTR)(&buf), 0, NULL)) {
printf("System - %s\n", (LPTSTR)buf);
fflush(stdout);
LocalFree(buf);
}
}
/*
==================
WinMain
==================
*/
HINSTANCE global_hInstance;
int global_nCmdShow;
char *argv[MAX_NUM_ARGVS];
static char *empty_string = "";
HWND hwnd_dialog;
int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
quakeparms_t parms;
double time, oldtime, newtime;
MEMORYSTATUS lpBuffer;
static char cwd[1024];
int t;
RECT rect;
DWORD err;
/* previous instances do not exist in Win32 */
if (hPrevInstance)
return 0;
global_hInstance = hInstance;
global_nCmdShow = nCmdShow;
lpBuffer.dwLength = sizeof(MEMORYSTATUS);
GlobalMemoryStatus(&lpBuffer);
if (!GetCurrentDirectory(sizeof(cwd), cwd))
Sys_Error("Couldn't determine current directory");
if (cwd[strlen(cwd) - 1] == '/')
cwd[strlen(cwd) - 1] = 0;
parms.basedir = cwd;
parms.cachedir = NULL;
parms.argc = 1;
argv[0] = empty_string;
while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS)) {
while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
lpCmdLine++;
if (*lpCmdLine) {
argv[parms.argc] = lpCmdLine;
parms.argc++;
while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
lpCmdLine++;
if (*lpCmdLine) {
*lpCmdLine = 0;
lpCmdLine++;
}
}
}
parms.argv = argv;
COM_InitArgv(parms.argc, parms.argv);
parms.argc = com_argc;
parms.argv = com_argv;
isDedicated = (COM_CheckParm("-dedicated") != 0);
if (!isDedicated) {
hwnd_dialog =
CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL);
if (hwnd_dialog) {
if (GetWindowRect(hwnd_dialog, &rect)) {
if (rect.left > (rect.top * 2)) {
SetWindowPos(hwnd_dialog, 0,
(rect.left / 2) -
((rect.right - rect.left) / 2), rect.top, 0,
0, SWP_NOZORDER | SWP_NOSIZE);
}
}
ShowWindow(hwnd_dialog, SW_SHOWDEFAULT);
UpdateWindow(hwnd_dialog);
SetForegroundWindow(hwnd_dialog);
}
}
/*
* Take the greater of all the available memory or half the total
* memory, but at least MINIMUM_WIN_MEMORY and no more than
* MAXIMUM_WIN_MEMORY, unless explicitly requested otherwise
*/
parms.memsize = lpBuffer.dwAvailPhys;
if (parms.memsize < MINIMUM_WIN_MEMORY)
parms.memsize = MINIMUM_WIN_MEMORY;
if (parms.memsize < (lpBuffer.dwTotalPhys >> 1))
parms.memsize = lpBuffer.dwTotalPhys >> 1;
if (parms.memsize > MAXIMUM_WIN_MEMORY)
parms.memsize = MAXIMUM_WIN_MEMORY;
if (COM_CheckParm("-heapsize")) {
t = COM_CheckParm("-heapsize") + 1;
if (t < com_argc)
parms.memsize = Q_atoi(com_argv[t]) * 1024;
}
parms.membase = malloc(parms.memsize);
if (!parms.membase)
Sys_Error("Not enough memory free; check disk space");
Sys_PageIn(parms.membase, parms.memsize);
tevent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!tevent)
Sys_Error("Couldn't create event");
if (isDedicated) {
if (!AllocConsole()) {
DWORD err = GetLastError();
printf("AllocConsole Failed: ");
printf_sys_error(err);
// Already have one? - Try free it and get a new one...
// FIXME - Keep current console or get new one...
FreeConsole();
if (!AllocConsole()) {
err = GetLastError();
printf("AllocConsole (2nd try): Error %i\n", (int)err);
fflush(stdout);
// FIXME - might not have stdout or stderr here for Sys_Error.
Sys_Error("Couldn't create dedicated server console");
}
}
// FIXME - these can fail...
// FIXME - the whole console creation thing is pretty screwy...
// FIXME - well, at least from cygwin rxvt it sucks...
hinput = GetStdHandle(STD_INPUT_HANDLE);
if (!hinput) {
err = GetLastError();
printf("GetStdHandle(STD_INPUT_HANDLE): Error %i\n", (int)err);
fflush(stdout);
}
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
if (!hinput) {
err = GetLastError();
printf("GetStdHandle(STD_OUTPUT_HANDLE): Error %i\n", (int)err);
fflush(stdout);
}
// give QHOST a chance to hook into the console
// FIXME - What is QHOST?
if ((t = COM_CheckParm("-HFILE")) > 0) {
if (t < com_argc)
hFile = (HANDLE)Q_atoi(com_argv[t + 1]);
}
if ((t = COM_CheckParm("-HPARENT")) > 0) {
if (t < com_argc)
heventParent = (HANDLE)Q_atoi(com_argv[t + 1]);
}
if ((t = COM_CheckParm("-HCHILD")) > 0) {
if (t < com_argc)
heventChild = (HANDLE)Q_atoi(com_argv[t + 1]);
}
InitConProc(hFile, heventParent, heventChild);
}
Sys_Init();
// because sound is off until we become active
S_BlockSound();
Sys_Printf("Host_Init\n");
Host_Init(&parms);
oldtime = Sys_DoubleTime();
/* main window message loop */
while (1) {
if (isDedicated) {
newtime = Sys_DoubleTime();
time = newtime - oldtime;
while (time < sys_ticrate.value) {
Sys_Sleep();
newtime = Sys_DoubleTime();
time = newtime - oldtime;
}
} else {
// yield the CPU for a little while when paused, minimized, or not the focus
if ((cl.paused && (!ActiveApp && !DDActive)) || !window_visible()
|| block_drawing) {
SleepUntilInput(PAUSE_SLEEP);
scr_skipupdate = 1; // no point in bothering to draw
} else if (!ActiveApp && !DDActive) {
SleepUntilInput(NOT_FOCUS_SLEEP);
}
newtime = Sys_DoubleTime();
time = newtime - oldtime;
}
Host_Frame(time);
oldtime = newtime;
}
/* return success of application */
return TRUE;
}
#if !id386
void
Sys_HighFPPrecision(void)
{
}
void
Sys_LowFPPrecision(void)
{
}
#endif

3182
NQ/vid_win.c Normal file

File diff suppressed because it is too large Load Diff

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