diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml new file mode 100644 index 0000000..2787f3f --- /dev/null +++ b/.github/workflows/build-and-release.yml @@ -0,0 +1,118 @@ +name: Build EBOOT and Publish Release +on: [push] +jobs: + Compile-EBOOT: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + container: + image: pspdev/pspdev + steps: + - uses: actions/checkout@v2 + - name: Get container ready + run: | + apk add bash zip git gmp mpc1 mpfr4 make + - name: Build + working-directory: ./ + run: | + cd source/psp/libpspmath + make && make install + cd ../../../ + make -f Makefile.psp install + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: psp-nzp-eboot + path: ./build/psp/bin/EBOOT.PBP + Compile-3DSX: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + outputs: + o3ds_link: ${{ steps.zip.outputs.zip }} + container: + image: devkitpro/devkitarm + steps: + - uses: actions/checkout@v2 + - name: Install Packages and update picaGL + run: | + git clone https://github.com/masterfeizz/picaGL.git + cd picaGL + git checkout revamp + mkdir clean + make install + - name: Build + working-directory: ./ + run: | + make -f Makefile.ctr + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: ctr-nzp-3dsx + path: ./build/3ds/bin/nzportable.3dsx + Unify-and-Release: + runs-on: ubuntu-latest + needs: [Compile-EBOOT, Compile-3DSX] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Wait for GitHub to keep up.. + run: sleep 2s + shell: bash + - name: Download Artifacts + uses: actions/download-artifact@v4 + with: + path: ./ + - name: Turn Artifacts into .ZIP archives + run: | + zip -r -j psp-nzp-eboot.zip psp-nzp-eboot/* + zip -r -j ctr-nzp-3dsx.zip ctr-nzp-3dsx/* + - name: Generate Build Date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%d-%H-%M-%S')" + - name: Delete Old Release + uses: dev-drprasad/delete-tag-and-release@v0.2.1 + with: + delete_release: true + tag_name: bleeding-edge + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: bleeding-edge + release_name: Automated Release ${{ steps.date.outputs.date }} + body: | + This is a **bleeding edge** NZ:P NX/VITA release, stability is not guarenteed. + + To install: + - Grab the .ZIP archive for your platform + - PSP: Extract the `EBOOT.PBP` to `/PSP/GAME/nzportable/` + - 3DS: Extract `nzportable.3dsx` to `/3ds/nzportable/`. + draft: true + prerelease: false + - name: Upload PSP Archive + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./psp-nzp-eboot.zip + asset_name: psp-nzp-eboot.zip + asset_content_type: application/zip + - name: Upload 3DS Archive + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./ctr-nzp-3dsx.zip + asset_name: ctr-nzp-3dsx.zip + asset_content_type: application/zip + - name: Publish Release + uses: StuYarrow/publish-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + id: ${{ steps.create_release.outputs.id }} \ No newline at end of file diff --git a/.github/workflows/build-eboot-and-release.yml b/.github/workflows/build-eboot-and-release.yml deleted file mode 100644 index 8098d6b..0000000 --- a/.github/workflows/build-eboot-and-release.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Build EBOOT and Publish Release -on: [push] -jobs: - Compile-EBOOT: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' - container: - image: pspdev/pspdev - steps: - - uses: actions/checkout@v2 - - name: Get container ready - run: | - apk add bash zip git gmp mpc1 mpfr4 make - - name: Build - working-directory: ./ - run: | - cd source/psp/libpspmath - make && make install - cd ../../../ - make -f Makefile.psp install - - name: Generate Build Date - id: date - run: echo "::set-output name=date::$(date +'%Y-%m-%d-%H-%M-%S')" - - name: Zip EBOOT - working-directory: ./build - run: | - zip -r -j psp-nzp-eboot.zip build/psp/bin/EBOOT.PBP - - name: Delete Old Release - uses: dev-drprasad/delete-tag-and-release@v0.2.1 - with: - delete_release: true - tag_name: bleeding-edge - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: bleeding-edge - release_name: Automated Release ${{ steps.date.outputs.date }} - body: | - This is a **bleeding edge** NZ:P PSP EBOOT release, stability is not guarenteed. - - To install: - - Grab the .ZIP archive (psp-nzp-eboot.zip) - - Extract the contents of the .ZIP archive to `PSP/GAME/nzportable`. - draft: true - prerelease: false - - name: Upload EBOOT Archive - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./build/psp-nzp-eboot.zip - asset_name: psp-nzp-eboot.zip - asset_content_type: application/zip - - name: Publish Release - uses: StuYarrow/publish-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - id: ${{ steps.create_release.outputs.id }} - diff --git a/Makefile.ctr b/Makefile.ctr new file mode 100644 index 0000000..086a4ca --- /dev/null +++ b/Makefile.ctr @@ -0,0 +1,265 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + +TARGET := nzportable +BUILD := build +SOURCES := source +#DATA := data +INCLUDES := include +ICON := source/ctr/art/icon.png +#ROMFS := romfs + +APP_AUTHOR := NZ:P Team +APP_TITLE := Nazi Zombies: Portable +APP_DESCRIPTION := Call of Duty Zombies remake + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft + +CFLAGS := -g -fpermissive -Wall -O3 -mword-relocations \ + -fomit-frame-pointer -ffunction-sections \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DGLQUAKE + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lpicaGL -lctru -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) $(DEVKITPRO)/picaGL + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +COMMON_OBJS = chase.c \ + cl_hud.c \ + cl_demo.c \ + cl_input.c \ + cl_main.c \ + cl_parse.c \ + cl_tent.c \ + cl_slist.c \ + ctr/bsp_strlcpy.c \ + cmd.c \ + ctr/common.c \ + console.c \ + crc.c \ + cvar.c \ + host.c \ + host_cmd.c \ + ctr/keys.c \ + mathlib.c \ + matrixlib.c \ + ctr/menu.c \ + ctr/net_dgrm.c \ + ctr/net_udpctr.c \ + net_loop.c \ + ctr/net_bsd.c \ + ctr/net_main.c \ + net_vcr.c \ + pr_cmds.c \ + pr_edict.c \ + pr_exec.c \ + ctr/sbar.c \ + sv_main.c \ + sv_move.c \ + sv_phys.c \ + sv_user.c \ + view.c \ + wad.c \ + world.c \ + zone.c \ + ctr/sys_ctr.c \ + snd_dma.c \ + snd_mix.c \ + snd_mem.c \ + ctr/snd_ctr.c \ + ctr/in_ctr.c \ + ctr/cd_null.c \ + ctr/gl/gl_qmb.c \ + ctr/gl/gl_decal.c \ + ctr/gl/gl_draw.c \ + ctr/gl/gl_fog.c \ + ctr/gl/gl_mesh.c \ + ctr/gl/gl_model.c \ + ctr/gl/gl_refrag.c \ + ctr/gl/gl_rlight.c \ + ctr/gl/gl_rmain.c \ + ctr/gl/gl_rmisc.c \ + ctr/gl/gl_rsurf.c \ + ctr/gl/gl_screen.c \ + ctr/gl/gl_warp.c \ + ctr/gl/gl_vidctr.c \ + ctr/r_part.c \ + ctr/touch_ctr.c \ + crypter.c + +CFILES := $(COMMON_OBJS) +CPPFILES := +SFILES := +PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) +SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) +#BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ + $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) + +export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) + +export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.png) + ifneq (,$(findstring $(TARGET).png,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).png + else + ifneq (,$(findstring icon.png,$(icons))) + export APP_ICON := $(TOPDIR)/icon.png + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_SMDH)),) + export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh +endif + +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @mkdir -p $(BUILD)/ctr + @mkdir -p $(BUILD)/ctr/gl + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.ctr + @mkdir -p $(BUILD)/3ds + @mkdir -p $(BUILD)/3ds/bin/ + @mv $(BUILD)/*.o $(BUILD)/3ds/ + @mv $(BUILD)/*.d $(BUILD)/3ds/ + @mv $(BUILD)/ctr $(BUILD)/3ds/ + @mv $(BUILD)/$(TARGET).lst $(BUILD)/3ds/ + @mv $(BUILD)/$(TARGET).map $(BUILD)/3ds/ + @mv $(TARGET).* $(BUILD)/3ds/bin/ + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +ifeq ($(strip $(NO_SMDH)),) +$(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh +else +$(OUTPUT).3dsx : $(OUTPUT).elf +endif + +$(OFILES_SOURCES) : $(HFILES) + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +# rules for assembling GPU shaders +#--------------------------------------------------------------------------------- +define shader-as + $(eval CURBIN := $*.shbin) + $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) + echo "$(CURBIN).o: $< $1" > $(DEPSFILE) + echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h + echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h + echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h + picasso -o $(CURBIN) $1 + bin2s $(CURBIN) | $(AS) -o $*.shbin.o +endef + +%.shbin.o %_shbin.h : %.v.pica %.g.pica + @echo $(notdir $^) + @$(call shader-as,$^) + +%.shbin.o %_shbin.h : %.v.pica + @echo $(notdir $<) + @$(call shader-as,$<) + +%.shbin.o %_shbin.h : %.shlist + @echo $(notdir $<) + @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/Makefile.psp b/Makefile.psp index 53dee62..fd9dd3b 100644 --- a/Makefile.psp +++ b/Makefile.psp @@ -1,4 +1,4 @@ -#MAKEFLAGS+="-j -l $(shell grep -c ^processor /proc/cpuinfo) " +MAKEFLAGS+="-j -l $(shell grep -c ^processor /proc/cpuinfo) " PSPSDK = $(shell psp-config --pspsdk-path) PSPLIBSDIR = $(PSPSDK)/.. @@ -127,17 +127,13 @@ clean: rm source/psp/*.o rm source/*.o -clean-keep-eboot: - rm -rf build/exec/ - rm source/psp/*.o - rm source/*.o - install: EBOOT.PBP - mkdir -p build/exec/ - mv EBOOT.PBP build/ + mkdir -p build/psp/ + mkdir -p build/psp/bin + mv EBOOT.PBP build/psp/bin/ ifeq ($(DEBUG),1) - mv *.prx build/exec/ + mv *.prx build/psp/ endif - mv *.elf build/exec/ - mv *.SFO build/exec/ + mv *.elf build/psp/ + mv *.SFO build/psp/ @echo DONE \ No newline at end of file diff --git a/README.md b/README.md index 46ed6ff..a3f9d10 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Nazi Zombies: Portable dQuakePlus ## About -This repository contains the PSP engine for NZ:P, based on dQuakePlus and containing optimizations from the NZ:P Team, adQuake, and Xash3D-PSP, as well as NZ:P-specific feature implementation. It has also been modified to build on the latest versions of the [PSPSDK](https://github.com/pspdev/pspsdk). +This repository contains the PlayStation Portable and Nintendo 3DS engine for NZ:P, based on dQuakePlus and ctrQuake, containing optimizations from the NZ:P Team, adQuake, and Xash3D-PSP, as well as NZ:P-specific feature implementation. It has also been modified to build on the latest versions of the [PSPSDK](https://github.com/pspdev/pspsdk). ## Building for PlayStation Portable Building requires a full install of [psptoolchain](https://github.com/pspdev/psptoolchain/). You can either follow the instructions on the GitHub repository or use a Docker container (we recommend [the official one](https://hub.docker.com/r/pspdev/pspdev))! @@ -18,4 +18,22 @@ cd ../../ make -f Makefile.psp install ``` -We also provide a prebuilt EBOOT on the [Releases](https://github.com/nzp-team/dquakeplus/releases/tag/bleeding-edge) page. \ No newline at end of file +We also provide a prebuilt EBOOT on the [Releases](https://github.com/nzp-team/dquakeplus/releases/tag/bleeding-edge) page. + +## Building for Nintendo 3DS +Building requires a full install of [libctru](https://github.com/devkitPro/libctru). You can either follow the instructions on the GitHub repository or use a Docker container (we recommend [the official one](devkitpro/devkitarm))! + +With the psptoolchain installed, you now need to install the latest `picaGL`, which needs cloned from the official GitHub repository: +```bash +git clone https://github.com/masterfeizz/picaGL.git -b revamp +cd picaGL +mkdir clean +make install +``` +Now you can navigate to the root of the repository and build the `.3dsx`. + +```bash +make -f Makefile.ctr +``` + +We also provide prebuilt .3dsx files on the [Releases](https://github.com/nzp-team/dquakeplus/releases/tag/bleeding-edge) page. \ No newline at end of file diff --git a/source/ctr/art/banner.png b/source/ctr/art/banner.png new file mode 100644 index 0000000..560ab17 Binary files /dev/null and b/source/ctr/art/banner.png differ diff --git a/source/ctr/art/icon.png b/source/ctr/art/icon.png new file mode 100644 index 0000000..63098c8 Binary files /dev/null and b/source/ctr/art/icon.png differ diff --git a/source/ctr/bsp_strlcpy.c b/source/ctr/bsp_strlcpy.c new file mode 100644 index 0000000..3f15e8c --- /dev/null +++ b/source/ctr/bsp_strlcpy.c @@ -0,0 +1,54 @@ +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "strl_fn.h" + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ + +size_t +q_strlcpy (char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/source/ctr/cd_null.c b/source/ctr/cd_null.c new file mode 100644 index 0000000..a9aa3ca --- /dev/null +++ b/source/ctr/cd_null.c @@ -0,0 +1,55 @@ +/* +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" + +void CDAudio_Play(byte track, qboolean looping) +{ +} + + +void CDAudio_Stop(void) +{ +} + + +void CDAudio_Pause(void) +{ +} + + +void CDAudio_Resume(void) +{ +} + + +void CDAudio_Update(void) +{ +} + + +int CDAudio_Init(void) +{ + return 0; +} + + +void CDAudio_Shutdown(void) +{ +} \ No newline at end of file diff --git a/source/ctr/client.h b/source/ctr/client.h new file mode 100644 index 0000000..da9875a --- /dev/null +++ b/source/ctr/client.h @@ -0,0 +1,425 @@ +/* +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. + +*/ +// client.h + +typedef struct +{ + vec3_t viewangles; + +// intended velocities + float forwardmove; + float sidemove; + float upmove; +#ifdef QUAKE2 + byte lightlevel; +#endif +} usercmd_t; + +typedef struct +{ + int length; + char map[MAX_STYLESTRING]; + char average; //johnfitz + char peak; //johnfitz +} lightstyle_t; + +typedef struct +{ + char name[MAX_SCOREBOARDNAME]; + float entertime; + int points; + int maxpoints; + int kills; + int headshots; +} scoreboard_t; + +typedef struct +{ + int destcolor[3]; + int percent; // 0-256 +} cshift_t; + +typedef enum +{ + lt_default, lt_muzzleflash, lt_explosion, lt_rocket, + lt_red, lt_blue, lt_redblue, lt_green, NUM_DLIGHTTYPES, + lt_explosion2, lt_explosion3, lt_rayred, lt_raygreen +} dlighttype_t; + +#define CSHIFT_CONTENTS 0 +#define CSHIFT_DAMAGE 1 +#define CSHIFT_BONUS 2 +#define CSHIFT_POWERUP 3 +#define NUM_CSHIFTS 4 + +#define NAME_LENGTH 64 + + +// +// client_state_t should hold all pieces of the client state +// + +#define SIGNONS 4 // signon messages to receive before connected + +#define MAX_DLIGHTS 32 +typedef struct +{ + 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 + int key; + qboolean dark; // subtracts light instead of adding + vec3_t color; //LordHavoc Lit. Support + int type; // color +} dlight_t; + + +#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 + +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, talking to a server +} 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 perks; // Perk icons. + int progress_bar; // Perk icons. + 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 + vec3_t gun_kick; // temporary kick + +// 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 + double ctime; // joe: copy of cl.time, to avoid incidents caused by rewind + + + float last_received_message; // (realtime) for net trouble icon + double laser_point_time; + +// +// 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 + entity_t viewent2; // the second gun model + + int cdtrack, looptrack; // cd audio + +// frag scoreboard + scoreboard_t *scores; // [cl.maxclients] + +#ifdef QUAKE2 +// light level at player's position including dlights +// this is sent back to the server each frame +// architectually ugly but it works + int light_level; +#endif +} client_state_t; + + +// +// cvars +// +extern cvar_t cl_name; +extern cvar_t cl_color; + +extern cvar_t cl_upspeed; +extern float cl_forwardspeed; +extern float cl_backspeed; +extern float 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 in_tolerance; +extern cvar_t in_acceleration; +extern cvar_t in_aimassist; +extern cvar_t in_analog_strafe; + +extern cvar_t m_pitch; +extern cvar_t m_yaw; +extern cvar_t m_forward; +extern cvar_t m_side; +extern cvar_t in_mlook; + + +#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]; +extern int cl_numstaticbrushmodels; +extern entity_t *cl_staticbrushmodels[MAX_VISEDICTS]; + +// model indexes +typedef enum modelindex_s +{ + mi_player, + mi_eyes, + mi_flame0, + mi_flame1, + mi_flame2, + mi_q3torso, + mi_q3head, +/* + mi_vw_light, + mi_vw_nail1, + mi_vw_nail2, + mi_vw_rock1, + mi_vw_rock2, + mi_vw_shot1, + mi_vw_shot2, + mi_vw_player, +*/ + NUM_MODELINDEX +} modelindex_t; + +extern modelindex_t cl_modelindex[NUM_MODELINDEX]; +extern char *cl_modelnames[NUM_MODELINDEX]; + +// +// 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_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); + + +float CL_KeyState (kbutton_t *key); +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); +void CL_NewTranslation (int slot); + +// +// view +// +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); + +qboolean TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal); \ No newline at end of file diff --git a/source/ctr/common.c b/source/ctr/common.c new file mode 100644 index 0000000..0e9ac43 --- /dev/null +++ b/source/ctr/common.c @@ -0,0 +1,1744 @@ +/* +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.c -- misc functions used in client and server + +#include "../quakedef.h" + +#define NUM_SAFE_ARGVS 7 + +static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1]; +static char *argvdummy = " "; + +static char *safeargvs[NUM_SAFE_ARGVS] = + {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"}; + +cvar_t cmdline = {"cmdline","0", false, true}; + +qboolean com_modified; // set true if using non-id files + +qboolean proghack; + +qboolean msg_suppress_1 = 0; + +void COM_InitFilesystem (void); + +// if a packfile directory differs from this, it is assumed to be hacked +#define PAK0_COUNT 339 +#define PAK0_CRC 32981 + +char com_token[1024]; +int com_argc; +char **com_argv; + +#define CMDLINE_LENGTH 256 +char com_cmdline[CMDLINE_LENGTH]; + +qboolean standard_quake = true, rogue, hipnotic; + +// this graphic needs to be in the pak file to use registered features +unsigned short pop[] = +{ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 +,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000 +,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000 +,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600 +,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563 +,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564 +,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564 +,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563 +,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500 +,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200 +,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000 +,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000 +,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000 +,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000 +,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000 +,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000 +}; + +/* + + +All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. + +The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is +only used during filesystem initialization. + +The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't. + +The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory +specified, when a file is found by the normal search path, it will be mirrored +into the cache directory, then opened there. + + + +FIXME: +The file "parms.txt" will be read out of the game directory and appended to the current command line arguments to allow different games to initialize startup parms differently. This could be used to add a "-sspeed 22050" for the high quality sound edition. Because they are added at the end, they will not override an explicit setting on the original command line. + +*/ + +//============================================================================ + + +// ClearLink is used for new headnodes +void ClearLink (link_t *l) +{ + l->prev = l->next = l; +} + +void RemoveLink (link_t *l) +{ + l->next->prev = l->prev; + l->prev->next = l->next; +} + +void InsertLinkBefore (link_t *l, link_t *before) +{ + l->next = before; + l->prev = before->prev; + l->prev->next = l; + l->next->prev = l; +} +void InsertLinkAfter (link_t *l, link_t *after) +{ + l->next = after->next; + l->prev = after; + l->prev->next = l; + l->next->prev = l; +} + +/* +============================================================================ + + LIBRARY REPLACEMENT FUNCTIONS + +============================================================================ +*/ + +#define snprintf_func snprintf +#define vsnprintf_func vsnprintf + +int q_vsnprintf(char *str, size_t size, const char *format, va_list args) +{ + int ret; + + ret = vsnprintf_func (str, size, format, args); + + if (ret < 0) + ret = (int)size; + if (size == 0) /* no buffer */ + return ret; + if ((size_t)ret >= size) + str[size - 1] = '\0'; + + return ret; +} + +int q_snprintf (char *str, size_t size, const char *format, ...) +{ + int ret; + va_list argptr; + + va_start (argptr, format); + ret = q_vsnprintf (str, size, format, argptr); + va_end (argptr); + + return ret; +} + +void Q_memset (void *dest, int fill, int count) +{ + int i; + + if ( (((long)dest | count) & 3) == 0) + { + count >>= 2; + fill = fill | (fill<<8) | (fill<<16) | (fill<<24); + for (i=0 ; i>=2; + for (i=0 ; i= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + if (!c1) + return 0; // strings are equal +// s1++; +// s2++; + } + + return -1; +} + +int Q_strcasecmp (char *s1, char *s2) +{ + return Q_strncasecmp (s1, s2, 99999); +} + +int Q_atoi (char *str) +{ + int val; + int sign; + int c; + + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val<<4) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val<<4) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val<<4) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + while (1) + { + c = *str++; + if (c <'0' || c > '9') + return val*sign; + val = val*10 + c - '0'; + } + + return 0; +} + + +float Q_atof (char *str) +{ + double val; + int sign; + int c; + int decimal, total; + + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val*16) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val*16) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val*16) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + decimal = -1; + total = 0; + while (1) + { + c = *str++; + if (c == '.') + { + decimal = total; + continue; + } + if (c <'0' || c > '9') + break; + val = val*10 + c - '0'; + total++; + } + + if (decimal == -1) + return val*sign; + while (total > decimal) + { + val /= 10; + total--; + } + + return val*sign; +} + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +qboolean bigendien; + +short (*BigShort) (short l); +int (*BigLong) (int l); +float (*BigFloat) (float l); + +short ShortSwap (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +int LongSwap (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +float FloatSwap (float f) +{ + union + { + float f; + byte b[4]; + } dat1, dat2; + + + dat1.f = f; + dat2.b[0] = dat1.b[3]; + dat2.b[1] = dat1.b[2]; + dat2.b[2] = dat1.b[1]; + dat2.b[3] = dat1.b[0]; + return dat2.f; +} + +/* +============================================================================== + + MESSAGE IO FUNCTIONS + +Handles byte ordering and avoids alignment errors +============================================================================== +*/ + +// +// writing functions +// + +void MSG_WriteChar (sizebuf_t *sb, int c) +{ + byte *buf; + +#ifdef PARANOID + if (c < -128 || c > 127) + Sys_Error ("MSG_WriteChar: range error"); +#endif + + buf = SZ_GetSpace (sb, 1); + buf[0] = c; +} + +void MSG_WriteByte (sizebuf_t *sb, int c) +{ + byte *buf; + +#ifdef PARANOID + if (c < 0 || c > 255) + Sys_Error ("MSG_WriteByte: range error"); +#endif + + buf = SZ_GetSpace (sb, 1); + buf[0] = c; +} + +void MSG_WriteShort (sizebuf_t *sb, int c) +{ + byte *buf; + +#ifdef PARANOID + if (c < ((short)0x8000) || c > (short)0x7fff) + Sys_Error ("MSG_WriteShort: range error"); +#endif + + buf = SZ_GetSpace (sb, 2); + buf[0] = c&0xff; + buf[1] = c>>8; +} + +void MSG_WriteLong (sizebuf_t *sb, int c) +{ + byte *buf; + + buf = SZ_GetSpace (sb, 4); + buf[0] = c&0xff; + buf[1] = (c>>8)&0xff; + buf[2] = (c>>16)&0xff; + buf[3] = c>>24; +} + +void MSG_WriteFloat (sizebuf_t *sb, float f) +{ + union + { + float f; + int l; + } dat; + + + dat.f = f; + dat.l = LittleLong (dat.l); + + SZ_Write (sb, &dat.l, 4); +} + +void MSG_WriteString (sizebuf_t *sb, char *s) +{ + if (!s) + SZ_Write (sb, "", 1); + else + SZ_Write (sb, s, Q_strlen(s)+1); +} + +void MSG_WriteCoord (sizebuf_t *sb, float f) +{ + MSG_WriteShort (sb, (int)(f*8)); +} + +void MSG_WriteAngle (sizebuf_t *sb, float f) +{ + MSG_WriteByte (sb, ((int)f*256/360) & 255); +} + +// +// reading functions +// +int msg_readcount; +qboolean msg_badread; + +void MSG_BeginReading (void) +{ + msg_readcount = 0; + msg_badread = false; +} + +// returns -1 and sets msg_badread if no more characters are available +int MSG_ReadChar (void) +{ + int c; + + if (msg_readcount+1 > net_message.cursize) + { + msg_badread = true; + return -1; + } + + c = (signed char)net_message.data[msg_readcount]; + msg_readcount++; + + return c; +} + +int MSG_ReadByte (void) +{ + int c; + + if (msg_readcount+1 > net_message.cursize) + { + msg_badread = true; + return -1; + } + + c = (unsigned char)net_message.data[msg_readcount]; + msg_readcount++; + + return c; +} + +int MSG_ReadShort (void) +{ + int c; + + if (msg_readcount+2 > net_message.cursize) + { + msg_badread = true; + return -1; + } + + c = (short)(net_message.data[msg_readcount] + + (net_message.data[msg_readcount+1]<<8)); + + msg_readcount += 2; + + return c; +} + +int MSG_ReadLong (void) +{ + int c; + + if (msg_readcount+4 > net_message.cursize) + { + msg_badread = true; + return -1; + } + + c = net_message.data[msg_readcount] + + (net_message.data[msg_readcount+1]<<8) + + (net_message.data[msg_readcount+2]<<16) + + (net_message.data[msg_readcount+3]<<24); + + msg_readcount += 4; + + return c; +} + +float MSG_ReadFloat (void) +{ + union + { + byte b[4]; + float f; + int l; + } dat; + + dat.b[0] = net_message.data[msg_readcount]; + dat.b[1] = net_message.data[msg_readcount+1]; + dat.b[2] = net_message.data[msg_readcount+2]; + dat.b[3] = net_message.data[msg_readcount+3]; + msg_readcount += 4; + + dat.l = LittleLong (dat.l); + + return dat.f; +} + +char *MSG_ReadString (void) +{ + static char string[2048]; + int l,c; + + l = 0; + do + { + c = MSG_ReadChar (); + if (c == -1 || c == 0) + break; + string[l] = c; + l++; + } while (l < sizeof(string)-1); + + string[l] = 0; + + return string; +} + +float MSG_ReadCoord (void) +{ + return MSG_ReadShort() * (1.0/8); +} + +float MSG_ReadAngle (void) +{ + return MSG_ReadChar() * (360.0/256); +} + + + +//=========================================================================== + +void SZ_Alloc (sizebuf_t *buf, int startsize) +{ + if (startsize < 256) + startsize = 256; + buf->data = Hunk_AllocName (startsize, "sizebuf"); + buf->maxsize = startsize; + buf->cursize = 0; +} + + +void SZ_Free (sizebuf_t *buf) +{ +// Z_Free (buf->data); +// buf->data = NULL; +// buf->maxsize = 0; + buf->cursize = 0; +} + +void SZ_Clear (sizebuf_t *buf) +{ + buf->cursize = 0; +} + +void *SZ_GetSpace (sizebuf_t *buf, int length) +{ + void *data; + + if (buf->cursize + length > buf->maxsize) + { + + Con_Printf("buf->cursize: %d, length: %d, buf->maxsize: %d\n", buf->cursize, length, buf->maxsize); + + if (!buf->allowoverflow) + Sys_Error ("SZ_GetSpace: overflow without allowoverflow set"); + + if (length > buf->maxsize) + Sys_Error ("SZ_GetSpace: %i is > full buffer size", length); + + buf->overflowed = true; + Con_Printf ("SZ_GetSpace: overflow"); + SZ_Clear (buf); + } + + data = buf->data + buf->cursize; + buf->cursize += length; + + return data; +} + +void SZ_Write (sizebuf_t *buf, void *data, int length) +{ + Q_memcpy (SZ_GetSpace(buf,length),data,length); +} + +void SZ_Print (sizebuf_t *buf, char *data) +{ + int len; + + len = Q_strlen(data)+1; + +// byte * cast to keep VC++ happy + if (buf->data[buf->cursize-1]) + Q_memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0 + else + Q_memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0 +} + + +//============================================================================ + + +/* +============ +COM_SkipPath +============ +*/ +char *COM_SkipPath (char *pathname) +{ + char *last; + + last = pathname; + while (*pathname) + { + if (*pathname=='/') + last = pathname+1; + pathname++; + } + return last; +} + +/* +============ +COM_StripExtension +============ +*/ +void COM_StripExtension (char *in, char *out) +{ + while (*in && *in != '.') + *out++ = *in++; + *out = 0; +} + +/* +============ +COM_FileExtension +============ +*/ +char *COM_FileExtension (char *in) +{ + static char exten[8]; + int i; + + while (*in && *in != '.') + in++; + if (!*in) + return ""; + in++; + for (i=0 ; i<7 && *in ; i++,in++) + exten[i] = *in; + exten[i] = 0; + return exten; +} + +/* +============ +COM_FileBase +============ +*/ +void COM_FileBase (char *in, char *out) +{ + char *s, *s2; + + s = in + strlen(in) - 1; + + while (s != in && *s != '.') + s--; + + for (s2 = s ; s2 != in && *s2 && *s2 != '/' ; s2--) + ; + + if (s-s2 < 2) + strcpy (out,"?model?"); + else + { + s--; + strncpy (out,s2+1, s-s2); + out[s-s2] = 0; + } +} + + +/* +================== +COM_DefaultExtension +================== +*/ +void COM_DefaultExtension (char *path, char *extension) +{ + char *src; +// +// if path doesn't have a .EXT, append extension +// (extension should include the .) +// + src = path + strlen(path) - 1; + + while (*src != '/' && src != path) + { + if (*src == '.') + return; // it has an extension + src--; + } + + strcat (path, extension); +} + + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + int c; + int len; + + len = 0; + com_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + return NULL; // end of file; + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + +// parse single characters + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + { + com_token[len] = c; + len++; + com_token[len] = 0; + return data+1; + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + break; + } while (c>32); + + com_token[len] = 0; + return data; +} + + +/* +================ +COM_CheckParm + +Returns the position (1 to argc-1) in the program's argument list +where the given parameter apears, or 0 if not present +================ +*/ +int COM_CheckParm (char *parm) +{ + int i; + + for (i=1 ; inext) + { + if (s->pack) + { + Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); + } + else + Con_Printf ("%s\n", s->filename); + } +} + +/* +============ +COM_WriteFile + +The filename will be prefixed by the current game directory +============ +*/ +void COM_WriteFile (char *filename, void *data, int len) +{ + int handle; + char name[MAX_OSPATH]; + + sprintf (name, "%s/%s", com_gamedir, filename); + + handle = Sys_FileOpenWrite (name); + if (handle == -1) + { + Sys_Printf ("COM_WriteFile: failed on %s\n", name); + return; + } + + Sys_Printf ("COM_WriteFile: %s\n", name); + Sys_FileWrite (handle, data, len); + Sys_FileClose (handle); +} + + +/* +============ +COM_CreatePath + +Only used for CopyFile +============ +*/ +void COM_CreatePath (char *path) +{ + char *ofs; + + for (ofs = path+1 ; *ofs ; ofs++) + { + if (*ofs == '/') + { // create the directory + *ofs = 0; + Sys_mkdir (path); + *ofs = '/'; + } + } +} + + +/* +=========== +COM_CopyFile + +Copies a file over from the net to the local cache, creating any directories +needed. This is for the convenience of developers using ISDN from home. +=========== +*/ +void COM_CopyFile (char *netpath, char *cachepath) +{ + int in, out; + int remaining, count; + char buf[4096]; + + remaining = Sys_FileOpenRead (netpath, &in); + COM_CreatePath (cachepath); // create directories up to the cache file + out = Sys_FileOpenWrite (cachepath); + + while (remaining) + { + if (remaining < sizeof(buf)) + count = remaining; + else + count = sizeof(buf); + Sys_FileRead (in, buf, count); + Sys_FileWrite (out, buf, count); + remaining -= count; + } + + Sys_FileClose (in); + Sys_FileClose (out); +} + +/* +=========== +COM_FindFile + +Finds the file in the search path. +Sets com_filesize and one of handle or file +=========== +*/ +int COM_FindFile (char *filename, int *handle, FILE **file) +{ + searchpath_t *search; + char netpath[MAX_OSPATH]; + char cachepath[MAX_OSPATH]; + pack_t *pak; + int i; + int findtime, cachetime; + + if (file && handle) + Sys_Error ("COM_FindFile: both handle and file set"); + if (!file && !handle) + Sys_Error ("COM_FindFile: neither handle or file set"); + +// +// search through the path, one element at a time +// + search = com_searchpaths; + if (proghack) + { // gross hack to use quake 1 progs with quake 2 maps + if (!strcmp(filename, "progs.dat")) + search = search->next; + } + + for ( ; search ; search = search->next) + { + // is the element a pak file? + if (search->pack) + { + // look through all the pak file elements + pak = search->pack; + for (i=0 ; inumfiles ; i++) + if (!strcmp (pak->files[i].name, filename)) + { // found it! + Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename); + if (handle) + { + *handle = pak->handle; + Sys_FileSeek (pak->handle, pak->files[i].filepos); + } + else + { // open a new file on the pakfile + *file = fopen (pak->filename, "rb"); + if (*file) + fseek (*file, pak->files[i].filepos, SEEK_SET); + } + com_filesize = pak->files[i].filelen; + return com_filesize; + } + } + else + { + // check a file in the directory tree + sprintf (netpath, "%s/%s",search->filename, filename); + + findtime = Sys_FileTime (netpath); + if (findtime == -1) + continue; + + // see if the file needs to be updated in the cache + if (!com_cachedir[0]) + strcpy (cachepath, netpath); + else + { +#if defined(_WIN32) + if ((strlen(netpath) < 2) || (netpath[1] != ':')) + sprintf (cachepath,"%s%s", com_cachedir, netpath); + else + sprintf (cachepath,"%s%s", com_cachedir, netpath+2); +#else + sprintf (cachepath,"%s%s", com_cachedir, netpath); +#endif + + cachetime = Sys_FileTime (cachepath); + + if (cachetime < findtime) + COM_CopyFile (netpath, cachepath); + strcpy (netpath, cachepath); + } + + Sys_Printf ("FindFile: %s\n",netpath); + com_filesize = Sys_FileOpenRead (netpath, &i); + if (handle) + *handle = i; + else + { + Sys_FileClose (i); + *file = fopen (netpath, "rb"); + } + return com_filesize; + } + + } + + Sys_Printf ("FindFile: can't find %s\n", filename); + + if (handle) + *handle = -1; + else + *file = NULL; + com_filesize = -1; + return -1; +} + + +/* +=========== +COM_OpenFile + +filename never has a leading slash, but may contain directory walks +returns a handle and a length +it may actually be inside a pak file +=========== +*/ +int COM_OpenFile (char *filename, int *handle) +{ + return COM_FindFile (filename, handle, NULL); +} + +/* +=========== +COM_FOpenFile + +If the requested file is inside a packfile, a new FILE * will be opened +into the file. +=========== +*/ +int COM_FOpenFile (char *filename, FILE **file) +{ + return COM_FindFile (filename, NULL, file); +} + +/* +============ +COM_CloseFile + +If it is a pak file handle, don't really close it +============ +*/ +void COM_CloseFile (int h) +{ + searchpath_t *s; + + for (s = com_searchpaths ; s ; s=s->next) + if (s->pack && s->pack->handle == h) + return; + + Sys_FileClose (h); +} + + +/* +============ +COM_LoadFile + +Filename are reletive to the quake directory. +Allways appends a 0 byte. +============ +*/ +cache_user_t *loadcache; +byte *loadbuf; +int loadsize; +byte *COM_LoadFile (char *path, int usehunk) +{ + int h; + byte *buf; + char base[32]; + int len; + + buf = NULL; // quiet compiler warning + +// look for it in the filesystem or pack files + len = COM_OpenFile (path, &h); + if (h == -1) + return NULL; + +// extract the filename base name for hunk tag + COM_FileBase (path, base); + + if (usehunk == 1) + buf = Hunk_AllocName (len+1, base); + else if (usehunk == 2) + buf = Hunk_TempAlloc (len+1); + else if (usehunk == 0) + buf = Z_Malloc (len+1); + else if (usehunk == 3) + buf = Cache_Alloc (loadcache, len+1, base); + else if (usehunk == 4) + { + if (len+1 > loadsize) + buf = Hunk_TempAlloc (len+1); + else + buf = loadbuf; + } + else + Sys_Error ("COM_LoadFile: bad usehunk"); + + if (!buf) + Sys_Error ("COM_LoadFile: not enough space for %s", path); + + ((byte *)buf)[len] = 0; + + Sys_FileRead (h, buf, len); + COM_CloseFile (h); + + return buf; +} + +byte *COM_LoadHunkFile (char *path) +{ + return COM_LoadFile (path, 1); +} + +byte *COM_LoadTempFile (char *path) +{ + return COM_LoadFile (path, 2); +} + +void COM_LoadCacheFile (char *path, struct cache_user_s *cu) +{ + loadcache = cu; + COM_LoadFile (path, 3); +} + +// uses temp hunk if larger than bufsize +byte *COM_LoadStackFile (char *path, void *buffer, int bufsize) +{ + byte *buf; + + loadbuf = (byte *)buffer; + loadsize = bufsize; + buf = COM_LoadFile (path, 4); + + return buf; +} + +/* +================= +COM_LoadPackFile + +Takes an explicit (not game tree related) path to a pak file. + +Loads the header and directory, adding the files at the beginning +of the list so they override previous pack files. +================= +*/ +pack_t *COM_LoadPackFile (char *packfile) +{ + dpackheader_t header; + int i; + packfile_t *newfiles; + int numpackfiles; + pack_t *pack; + int packhandle; + dpackfile_t info[MAX_FILES_IN_PACK]; + unsigned short crc; + + if (Sys_FileOpenRead (packfile, &packhandle) == -1) + { +// Con_Printf ("Couldn't open %s\n", packfile); + return NULL; + } + Sys_FileRead (packhandle, (void *)&header, sizeof(header)); + if (header.id[0] != 'P' || header.id[1] != 'A' + || header.id[2] != 'C' || header.id[3] != 'K') + Sys_Error ("%s is not a packfile", packfile); + header.dirofs = LittleLong (header.dirofs); + header.dirlen = LittleLong (header.dirlen); + + numpackfiles = header.dirlen / sizeof(dpackfile_t); + + if (numpackfiles > MAX_FILES_IN_PACK) + Sys_Error ("%s has %i files", packfile, numpackfiles); + + if (numpackfiles != PAK0_COUNT) + com_modified = true; // not the original file + + newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile"); + + Sys_FileSeek (packhandle, header.dirofs); + Sys_FileRead (packhandle, (void *)info, header.dirlen); + +// crc the directory to check for modifications + CRC_Init (&crc); + for (i=0 ; ifilename, packfile); + pack->handle = packhandle; + pack->numfiles = numpackfiles; + pack->files = newfiles; + + Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); + return pack; +} + + +/* +================ +COM_AddGameDirectory + +Sets com_gamedir, adds the directory to the head of the path, +then loads and adds pak1.pak pak2.pak ... +================ +*/ +void COM_AddGameDirectory (char *dir) +{ + int i; + searchpath_t *search; + pack_t *pak; + char pakfile[MAX_OSPATH]; + + strcpy (com_gamedir, dir); + +// +// add the directory to the search path +// + search = Hunk_Alloc (sizeof(searchpath_t)); + strcpy (search->filename, dir); + search->next = com_searchpaths; + com_searchpaths = search; + +// +// add any pak files in the format pak0.pak pak1.pak, ... +// + for (i=0 ; ; i++) + { + sprintf (pakfile, "%s/pak%i.pak", dir, i); + pak = COM_LoadPackFile (pakfile); + if (!pak) + break; + search = Hunk_Alloc (sizeof(searchpath_t)); + search->pack = pak; + search->next = com_searchpaths; + com_searchpaths = search; + } + +// +// add the contents of the parms.txt file to the end of the command line +// + +} + +/* +================ +COM_InitFilesystem +================ +*/ +void COM_InitFilesystem (void) +{ + int i, j; + char basedir[MAX_OSPATH]; + searchpath_t *search; + +// +// -basedir +// Overrides the system supplied base directory (under GAMENAME) +// + i = COM_CheckParm ("-basedir"); + if (i && i < com_argc-1) + strcpy (basedir, com_argv[i+1]); + else + strcpy (basedir, host_parms.basedir); + + j = strlen (basedir); + + if (j > 0) + { + if ((basedir[j-1] == '\\') || (basedir[j-1] == '/')) + basedir[j-1] = 0; + } + +// +// -cachedir +// Overrides the system supplied cache directory (NULL or /qcache) +// -cachedir - will disable caching. +// + i = COM_CheckParm ("-cachedir"); + if (i && i < com_argc-1) + { + if (com_argv[i+1][0] == '-') + com_cachedir[0] = 0; + else + strcpy (com_cachedir, com_argv[i+1]); + } + else if (host_parms.cachedir) + strcpy (com_cachedir, host_parms.cachedir); + else + com_cachedir[0] = 0; + +// +// start up with GAMENAME by default (id1) +// + COM_AddGameDirectory (va("%s/"GAMENAME, basedir) ); + + if (COM_CheckParm ("-rogue")) + COM_AddGameDirectory (va("%s/rogue", basedir) ); + if (COM_CheckParm ("-hipnotic")) + COM_AddGameDirectory (va("%s/hipnotic", basedir) ); + +// +// -game +// Adds basedir/gamedir as an override game +// + i = COM_CheckParm ("-game"); + if (i && i < com_argc-1) + { + com_modified = true; + COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1])); + } + +// +// -path [] ... +// Fully specifies the exact serach path, overriding the generated one +// + i = COM_CheckParm ("-path"); + if (i) + { + com_modified = true; + com_searchpaths = NULL; + while (++i < com_argc) + { + if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-') + break; + + search = Hunk_Alloc (sizeof(searchpath_t)); + if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") ) + { + search->pack = COM_LoadPackFile (com_argv[i]); + if (!search->pack) + Sys_Error ("Couldn't load packfile: %s", com_argv[i]); + } + else + strcpy (search->filename, com_argv[i]); + search->next = com_searchpaths; + com_searchpaths = search; + } + } + + if (COM_CheckParm ("-proghack")) + proghack = true; +} + +//Diabolickal HLBSP +void Q_strncpyz (char *dest, char *src, size_t size) +{ + strncpy (dest, src, size - 1); + dest[size-1] = 0; +} +//Diabolickal End \ No newline at end of file diff --git a/source/ctr/common.h b/source/ctr/common.h new file mode 100644 index 0000000..d1a05f0 --- /dev/null +++ b/source/ctr/common.h @@ -0,0 +1,195 @@ +/* +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. + +*/ +// comndef.h -- general definitions + + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#define bound(a, b, c) ((a) >= (c) ? (a) : (b) < (a) ? (a) : (b) > (c) ? (c) : (b)) + +#if !defined BYTE_DEFINED +typedef unsigned char byte; +#define BYTE_DEFINED 1 +#endif + +#undef true +#undef false + +typedef enum {false, true} qboolean; + +//============================================================================ + +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); + +#define LittleLong(l) l +#define LittleShort(l) l +#define LittleFloat(l) 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); + +//============================================================================ + +void Q_memset (void *dest, int fill, int count); +void Q_memcpy (void *dest, void *src, int count); +int Q_memcmp (void *m1, void *m2, int count); +void Q_strcpy (char *dest, char *src); +void Q_strncpy (char *dest, char *src, int count); +int Q_strlen (char *str); +char *Q_strrchr (char *s, char c); +void Q_strcat (char *dest, char *src); +int Q_strcasecmp (char *s1, char *s2); +int Q_strncasecmp (char *s1, char *s2, int n); +int Q_atoi (char *str); +float Q_atof (char *str); +int q_snprintf (char *str, size_t size, const char *format, ...); +int q_vsnprintf(char *str, size_t size, const char *format, va_list args); + +//============================================================================ + +extern char com_token[1024]; +extern qboolean com_eof; + +char *COM_Parse (char *data); + + +extern int com_argc; +extern char **com_argv; + +int 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 (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 (char *filename, void *data, int len); +int COM_OpenFile (char *filename, int *hndl); +int COM_FOpenFile (char *filename, FILE **file); +void COM_CloseFile (int h); + +byte *COM_LoadStackFile (char *path, void *buffer, int bufsize); +byte *COM_LoadTempFile (char *path); +byte *COM_LoadHunkFile (char *path); +void COM_LoadCacheFile (char *path, struct cache_user_s *cu); + + +extern struct cvar_s registered; + +extern qboolean standard_quake, rogue, hipnotic; +void Q_strncpyz (char *dest, char *src, size_t size); //Diabolickal HLBSP diff --git a/source/ctr/dosisms.h b/source/ctr/dosisms.h new file mode 100644 index 0000000..7f12268 --- /dev/null +++ b/source/ctr/dosisms.h @@ -0,0 +1,100 @@ +/* +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. + +*/ + +// +// dosisms.h: I'd call it dos.h, but the name's taken +// + +#ifndef _DOSISMS_H_ +#define _DOSISMS_H_ + +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_ + diff --git a/source/ctr/gl/gl_decal.c b/source/ctr/gl/gl_decal.c new file mode 100644 index 0000000..0cd26f2 --- /dev/null +++ b/source/ctr/gl/gl_decal.c @@ -0,0 +1,156 @@ + +#include "../../quakedef.h" + +void R_SpawnDecal (vec3_t center, vec3_t normal, vec3_t tangent, int tex, int size, int isbsp) +{ +// naievil -- fixme +/* + int a; + float width, height, depth, d, one_over_w, one_over_h; + vec3_t binormal, test = {0.5, 0.5, 0.5}; + decal_t *dec; + + if (!qmb_initialized) + return; + + // allocate decal + if (!free_decals) + return; + + dec = free_decals; + free_decals = dec->next; + dec->next = active_decals; + active_decals = dec; + + VectorNormalize (test); + CrossProduct (normal, test, tangent); + + VectorCopy (center, dec->origin); + VectorCopy (tangent, dec->tangent); + VectorCopy (normal, dec->normal); + VectorNormalize (tangent); + VectorNormalize (normal); + CrossProduct (normal, tangent, binormal); + VectorNormalize (binormal); + + width = RandomMinMax (size * 0.5, size); + height = width; + depth = width * 0.5; + dec->radius = fmax(fmax(width, height), depth); + dec->starttime = cl.time; + dec->bspdecal = isbsp; + dec->die = (isbsp ? 0 : cl.time + r_decaltime.value); + dec->texture = tex; + + // Calculate boundary planes + d = DotProduct (center, tangent); + VectorCopy (tangent, leftPlane.normal); + leftPlane.dist = -(width * 0.5 - d); + VectorNegate (tangent, tangent); + VectorCopy (tangent, rightPlane.normal); + VectorNegate (tangent, tangent); + rightPlane.dist = -(width * 0.5 + d); + + d = DotProduct (center, binormal); + VectorCopy (binormal, bottomPlane.normal); + bottomPlane.dist = -(height * 0.5 - d); + VectorNegate (binormal, binormal); + VectorCopy (binormal, topPlane.normal); + VectorNegate (binormal, binormal); + topPlane.dist = -(height * 0.5 + d); + + d = DotProduct (center, normal); + VectorCopy (normal, backPlane.normal); + backPlane.dist = -(depth - d); + VectorNegate (normal, normal); + VectorCopy (normal, frontPlane.normal); + VectorNegate (normal, normal); + frontPlane.dist = -(depth + d); + + // Begin with empty mesh + dec->vertexCount = 0; + dec->triangleCount = 0; + + // Clip decal to bsp + DecalWalkBsp_R (dec, cl.worldmodel->nodes); + + // This happens when a decal is to far from any surface or the surface is to steeply sloped + if (dec->triangleCount == 0) + { // deallocate decal + active_decals = dec->next; + dec->next = free_decals; + free_decals = dec; + return; + } + + // Assign texture mapping coordinates + one_over_w = 1.0F / width; + one_over_h = 1.0F / height; + for (a = 0 ; a < dec->vertexCount ; a++) + { + float s, t; + vec3_t v; + + VectorSubtract (dec->vertexArray[a], center, v); + s = DotProduct (v, tangent) * one_over_w + 0.5F; + t = DotProduct (v, binormal) * one_over_h + 0.5F; + dec->texcoordArray[a][0] = s; + dec->texcoordArray[a][1] = t; + } +*/ +} + +//Revamped by blubs +void R_SpawnDecalStatic (vec3_t org, int tex, int size) +{ + /* + int i; + float frac, bestfrac; + vec3_t tangent, v, bestorg, normal, bestnormal, org2; + vec3_t tempVec; + + if (!qmb_initialized) + return; + + VectorClear (bestorg); + VectorClear (bestnormal); + VectorClear(tempVec); + + bestfrac = 10; + for (i = 0 ; i < 26 ; i++) + { + //Reference: i = 0: check straight up, i = 1: check straight down + //1 < i < 10: Check sideways in increments of 45 degrees + //9 < i < 18: Check angled 45 degrees down in increments of 45 degrees + //17 < i : Check angled 45 degrees up in increments of 45 degrees + org2[0] = (((((i - 2) % 8) < 2) || (((i - 2) % 8) == 7)) ? 1 : 0 ) + ((((i - 2) % 8) > 2 && ((i - 2) % 8) < 6) ? -1 : 0 ); + org2[1] = ((((i - 2) % 8) > 0 && ((i - 2) % 8) < 4) ? 1 : 0 ) + ((((i - 2) % 8) > 4 && ((i - 2) % 8) < 7) ? -1 : 0 ); + org2[2] = ((i == 0) ? 1 : 0) + ((i == 1) ? -1 : 0) + (((i > 9) && (i < 18)) ? 1 : 0) + ((i > 17) ? -1 : 0); + + VectorCopy(org,tempVec); + VectorMA(tempVec, -0.1,org2,tempVec); + + VectorMA (org, 20, org2, org2); + TraceLineN (tempVec, org2, v, normal); + + VectorSubtract(org2,tempVec,org2);//goal + VectorSubtract(v,tempVec,tempVec);//collision + + if(VectorLength(org2) == 0) + return; + + frac = VectorLength(tempVec) / VectorLength(org2); + + if(frac < 1 && frac < bestfrac) + { + bestfrac = frac; + VectorCopy(v,bestorg); + VectorCopy(normal, bestnormal); + CrossProduct(normal,bestnormal,tangent); + } + } + + if (bestfrac < 1) + R_SpawnDecal (bestorg, bestnormal, tangent, tex, size, 0); + */ +} \ No newline at end of file diff --git a/source/ctr/gl/gl_decal.h b/source/ctr/gl/gl_decal.h new file mode 100644 index 0000000..e711e44 --- /dev/null +++ b/source/ctr/gl/gl_decal.h @@ -0,0 +1,2 @@ +void R_SpawnDecal (vec3_t center, vec3_t normal, vec3_t tangent, int tex, int size, int isbsp); +void R_SpawnDecalStatic (vec3_t org, int tex, int size); \ No newline at end of file diff --git a/source/ctr/gl/gl_draw.c b/source/ctr/gl/gl_draw.c new file mode 100644 index 0000000..0a9484e --- /dev/null +++ b/source/ctr/gl/gl_draw.c @@ -0,0 +1,2338 @@ +/* +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. + +*/ + +// draw.c -- this is the only file outside the refresh that touches the +// vid buffer + +#include "../../quakedef.h" + +#define GL_COLOR_INDEX8_EXT 0x80E5 + +extern unsigned char d_15to8table[65536]; + +cvar_t gl_nobind = {"gl_nobind", "0"}; +cvar_t gl_max_size = {"gl_max_size", "1024"}; +cvar_t gl_picmip = {"gl_picmip", "0"}; + +byte *draw_chars; // 8*8 graphic characters +qpic_t *sniper_scope; + +qpic_t *draw_backtile; + +int translate_texture; +int char_texture; + +typedef struct +{ + int texnum; + float sl, tl, sh, th; +} glpic_t; + +byte conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)]; + +int gl_lightmap_format = 4; +int gl_solid_format = 3; +int gl_alpha_format = 4; + +int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; +int gl_filter_max = GL_LINEAR; + + +int texels; + +int GL_LoadPicTexture (qpic_t *pic); + +//Loading Fill by Crow_bar +float loading_cur_step; +char loading_name[32]; +float loading_num_step; +int loading_step; +float loading_cur_step_bk; + +typedef struct +{ + int texnum; + char identifier[64]; + int original_width; + int original_height; + int width, height; + qboolean mipmap; + qboolean islmp; + int checksum; + +// Diabolicka TGA +int bytesperpixel; +int lhcsum; +// Diabolickal end +} gltexture_t; + + +#define MAX_GLTEXTURES 1024 +gltexture_t gltextures[MAX_GLTEXTURES]; +int numgltextures; + + +void GL_Bind (int texnum) +{ + if (gl_nobind.value) + texnum = char_texture; + if (currenttexture == texnum) + return; + currenttexture = texnum; + glBindTexture(GL_TEXTURE_2D, texnum); +} + + +/* +============================================================================= + + scrap allocation + + Allocate all the little status bar obejcts into a single texture + to crutch up stupid hardware / drivers + +============================================================================= +*/ + +#define MAX_SCRAPS 2 +#define BLOCK_WIDTH 256 +#define BLOCK_HEIGHT 256 + +int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; +byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4]; +qboolean scrap_dirty; +int scrap_texnum; + +// returns a texture number and the position inside it +int Scrap_AllocBlock (int w, int h, int *x, int *y) +{ + int i, j; + int best, best2; + int texnum; + + for (texnum=0 ; texnum= best) + break; + if (scrap_allocated[texnum][i+j] > best2) + best2 = scrap_allocated[texnum][i+j]; + } + if (j == w) + { // this is a valid spot + *x = i; + *y = best = best2; + } + } + + if (best + h > BLOCK_HEIGHT) + continue; + + for (i=0 ; iname)) + return &pic->pic; + + if (menu_numcachepics == MAX_CACHED_PICS) + Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); + menu_numcachepics++; + strcpy (pic->name, str); + +// +// load the pic from disk +// + + int index = loadtextureimage (str, 0, 0, qfalse, qfalse); + if(index) + { + pic->pic.width = gltextures[index].original_width; + pic->pic.height = gltextures[index].original_height; + + gltextures[index].islmp = qfalse; + gl = (glpic_t *)pic->pic.data; + gl->texnum = index; + + return &pic->pic; + } + + dat = (qpic_t *)COM_LoadTempFile (str); + if (!dat) + { + strcat (str, ".lmp"); + dat = (qpic_t *)COM_LoadTempFile (str); + if (!dat) + { + Con_Printf ("Draw_CachePic: failed to load file %s\n", str); + return NULL; + } + } + SwapPic (dat); + + + pic->pic.width = dat->width; + pic->pic.height = dat->height; + + gl = (glpic_t *)pic->pic.data; + gl->texnum = GL_LoadPicTexture (dat); + + gltextures[gl->texnum].islmp = qtrue; + return &pic->pic; +} + +typedef struct +{ + char *name; + int minimize, maximize; +} glmode_t; + +glmode_t modes[] = { + {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, + {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} +}; + +/* +=============== +Draw_TextureMode_f +=============== +*/ +void Draw_TextureMode_f (void) +{ + int i; + gltexture_t *glt; + + if (Cmd_Argc() == 1) + { + for (i=0 ; i< 6 ; i++) + if (gl_filter_min == modes[i].minimize) + { + Con_Printf ("%s\n", modes[i].name); + return; + } + Con_Printf ("current filter is unknown???\n"); + return; + } + + for (i=0 ; i< 6 ; i++) + { + if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) ) + break; + } + if (i == 6) + { + Con_Printf ("bad filter name\n"); + return; + } + + gl_filter_min = modes[i].minimize; + gl_filter_max = modes[i].maximize; + + // change all the existing mipmap texture objects + for (i=0, glt=gltextures ; imipmap) + { + GL_Bind (glt->texnum); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + } +} + +// ! " # $ % & ' ( ) * _ , - . / 0 +// 1 2 3 4 5 6 7 8 9 : ; < = > ? @ +// A B C D E F G H I J K L M N O P +// Q R S T U V W X Y Z [ \ ] ^ _ ` +// a b c d e f g h i j k l m n o p +// q r s t u v w x y z { | } ~ +int font_kerningamount[96]; + +void InitKerningMap(void) +{ + // Initialize the kerning amount as 8px for each + // char in the event we cant load the file. + for(int i = 0; i < 96; i++) { + font_kerningamount[i] = 8; + } + + FILE *kerning_map = fopen(va("%s/gfx/kerning_map.txt", com_gamedir), "r"); + if (kerning_map == NULL) { + return; + } + + char buffer[1024]; + if (fgets(buffer, sizeof(buffer), kerning_map) != NULL) { + char *token = strtok(buffer, ","); + int i = 0; + while (token != NULL && i < 96) { + font_kerningamount[i++] = atoi(token); + token = strtok(NULL, ","); + } + } + + fclose(kerning_map); +} + +/* +=============== +Draw_Init +=============== +*/ +extern bool new3ds_flag; +void Draw_Init (void) +{ + int start; + + numgltextures = 0; + + Cvar_RegisterVariable (&gl_nobind); + Cvar_RegisterVariable (&gl_max_size); + Cvar_RegisterVariable (&gl_picmip); + + if (!new3ds_flag) { + //Cvar_SetValue("gl_picmip", 1); + Cvar_Set ("gl_max_size", "256"); + } + + Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f); + + // now turn them into textures + char_texture = loadtextureimage ("gfx/charset", 0, 0, false, false); + if (char_texture == 0)// did not find a matching TGA... + Sys_Error ("Could not load charset, make sure you have every folder and file installed properly\nDouble check that all of your files are in their correct places\nAnd that you have installed the game properly.\n"); + + start = Hunk_LowMark(); + + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // free loaded console + Hunk_FreeToLowMark(start); + + // save a texture slot for translated picture + translate_texture = texture_extension_number++; + + // save slots for scraps + scrap_texnum = texture_extension_number; + texture_extension_number += MAX_SCRAPS; + + // + // get the other pics we need + // + sniper_scope = Draw_CachePic ("gfx/hud/scope"); + + Clear_LoadingFill (); + + InitKerningMap(); +} + + + +/* +================ +Draw_Character + +Draws one 8*8 graphics character with 0 being transparent. +It can be clipped to the top of the screen to allow the console to be +smoothly scrolled off. +================ +*/ +void Draw_Character (int x, int y, int num) +{ + int row, col; + float frow, fcol, size; + + if (num == 32) + return; // space + + num &= 255; + + if (y <= -8) + return; // totally off screen + + row = num>>4; + col = num&15; + + frow = row*0.0625; + fcol = col*0.0625; + size = 0.0625; + + GL_Bind (char_texture); + + glEnable(GL_ALPHA_TEST); + glBegin (GL_QUADS); + glTexCoord2f (fcol, frow); + glVertex2f (x, y); + glTexCoord2f (fcol + size, frow); + glVertex2f (x+8, y); + glTexCoord2f (fcol + size, frow + size); + glVertex2f (x+8, y+8); + glTexCoord2f (fcol, frow + size); + glVertex2f (x, y+8); + glEnd (); + glDisable(GL_ALPHA_TEST); +} + +/* +================ +Draw_CharacterRGBA + +This is the same as Draw_Character, but with RGBA color codes. +- Cypress +================ +*/ +extern cvar_t scr_coloredtext; +void Draw_CharacterRGBA(int x, int y, int num, float r, float g, float b, float a, int scale) +{ + int row, col; + float frow, fcol, size; + + if (num == 32) + return; // space + + num &= 255; + + if (y <= -8) + return; // totally off screen + + row = num>>4; + col = num&15; + + frow = row*0.0625; + fcol = col*0.0625; + size = 0.0625*(float)scale; + + GL_Bind (char_texture); + + glEnable(GL_BLEND); + glColor4f(r/255, g/255, b/255, a/255); + glDisable (GL_ALPHA_TEST); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBegin (GL_QUADS); + glTexCoord2f (fcol, frow); + glVertex2f (x, y); + glTexCoord2f (fcol + (float)(size/(float)scale), frow); + glVertex2f (x+(8*(scale)), y); + glTexCoord2f (fcol + (float)(size/(float)scale), frow + (float)(size/(float)scale)); + glVertex2f (x+(8*(scale)), y+(8*(scale))); + glTexCoord2f (fcol, frow + (float)(size/(float)scale)); + glVertex2f (x, y+(8*(scale))); + glEnd (); + glColor4f(1,1,1,1); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnable(GL_ALPHA_TEST); + glDisable (GL_BLEND); +} + +/* +================ +Draw_String +================ +*/ +void Draw_String (int x, int y, char *str) +{ + Draw_ColoredString(x, y, str, 255, 255, 255, 255, 1); +} + +void Draw_ColoredString(int x, int y, char *str, float r, float g, float b, float a, int scale) +{ + while (*str) + { + Draw_CharacterRGBA (x, y, *str, r, g, b, a, scale); + + // Hooray for variable-spacing! + if (*str == ' ') + x += 4 * scale; + else if ((int)*str < 33 || (int)*str > 126) + x += 8 * scale; + else + x += (font_kerningamount[(int)(*str - 33)] + 1) * scale; + + str++; + } +} + +int getTextWidth(char *str, int scale) +{ + int width = 0; + + for (int i = 0; i < strlen(str); i++) { + // Hooray for variable-spacing! + if (str[i] == ' ') + width += 4 * scale; + else if ((int)str[i] < 33 || (int)str[i] > 126) + width += 8 * scale; + else + width += (font_kerningamount[(int)(str[i] - 33)] + 1) * scale; + } + + return width; +} + + +void Draw_ColoredStringCentered(int y, char *str, float r, float g, float b, float a, int scale) +{ + Draw_ColoredString((vid.width - getTextWidth(str, scale))/2, y, str, r, g, b, a, scale); +} + +/* +================ +Draw_DebugChar + +Draws a single character directly to the upper right corner of the screen. +This is for debugging lockups by drawing different chars in different parts +of the code. +================ +*/ +void Draw_DebugChar (char num) +{ +} + +/* +============= +Draw_AlphaPic +============= +*/ +void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha) +{ + Draw_ColorPic(x, y, pic, 255, 255, 255, alpha); +} + + +/* +============= +Draw_Pic +============= +*/ +void Draw_Pic (int x, int y, qpic_t *pic) +{ + Draw_ColorPic(x, y, pic, 255, 255, 255, 255); +} + +/* +============= +Draw_ColoredStretchPic +============= +*/ +void Draw_ColoredStretchPic (int x, int y, qpic_t *pic, int x_value, int y_value, int r, int g, int b, int a) +{ + glpic_t *gl; + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; + + glEnable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glColor4f(r/255.0,g/255.0,b/255.0,a/255.0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + GL_Bind (gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x+x_value, y); + glTexCoord2f (1, 1); + glVertex2f (x+x_value, y+y_value); + glTexCoord2f (0, 1); + glVertex2f (x, y+y_value); + glEnd (); + + glColor4f(1,1,1,1); +} + +/* +============= +Draw_StretchPic +============= +*/ +void Draw_StretchPic (int x, int y, qpic_t *pic, int x_value, int y_value) +{ + glpic_t *gl; + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; + + + glEnable(GL_ALPHA_TEST); + glColor4f(1,1,1,1); + + GL_Bind (gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x+x_value, y); + glTexCoord2f (1, 1); + glVertex2f (x+x_value, y+y_value); + glTexCoord2f (0, 1); + glVertex2f (x, y+y_value); + glEnd (); + + glColor4f(1,1,1,1); +} + +/* +============= +Draw_ColorPic +============= +*/ +void Draw_ColorPic (int x, int y, qpic_t *pic, float r, float g , float b, float a) +{ + glpic_t *gl; + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; + + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + glColor4f(r/255.0f,g/255.0f,b/255.0f,a/255.0f); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + GL_Bind (gl->texnum); + + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x+pic->width, y); + glTexCoord2f (1, 1); + glVertex2f (x+pic->width, y+pic->height); + glTexCoord2f (0, 1); + glVertex2f (x, y+pic->height); + glEnd (); + + glDisable(GL_BLEND); + //glDisable(GL_ALPHA_TEST); + glColor4f(1,1,1,1); +} + +/* +============= +Draw_TransPic +============= +*/ +void Draw_TransPic (int x, int y, qpic_t *pic) +{ + if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || + (unsigned)(y + pic->height) > vid.height) + { + Sys_Error ("Draw_TransPic: bad coordinates"); + } + + Draw_Pic (x, y, pic); +} + + +/* +============= +Draw_TransPicTranslate + +Only used for the player color selection menu +============= +*/ +void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation) +{ + int v, u; + unsigned trans[64*64], *dest; + byte *src; + int p; + + GL_Bind (translate_texture); + + dest = trans; + for (v=0 ; v<64 ; v++, dest += 64) + { + src = &menuplyr_pixels[ ((v*pic->height)>>6) *pic->width]; + for (u=0 ; u<64 ; u++) + { + p = src[(u*pic->width)>>6]; + if (p == 255) + dest[u] = p; + else + dest[u] = d_8to24table[translation[p]]; + } + } + + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glColor3f (1,1,1); + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x+pic->width, y); + glTexCoord2f (1, 1); + glVertex2f (x+pic->width, y+pic->height); + glTexCoord2f (0, 1); + glVertex2f (x, y+pic->height); + glEnd (); +} + + +/* +================ +Draw_ConsoleBackground + +================ +*/ +void Draw_ConsoleBackground (int lines) +{ + Draw_FillByColor(0, 0, vid.width, lines, 0, 0, 0, 255); +} + + +/* +============= +Draw_TileClear + +This repeats a 64*64 tile graphic to fill the screen around a sized down +refresh window. +============= +*/ +void Draw_TileClear (int x, int y, int w, int h) +{ + glColor3f (1,1,1); + GL_Bind (*(int *)draw_backtile->data); + glBegin (GL_QUADS); + glTexCoord2f (x/64.0, y/64.0); + glVertex2f (x, y); + glTexCoord2f ( (x+w)/64.0, y/64.0); + glVertex2f (x+w, y); + glTexCoord2f ( (x+w)/64.0, (y+h)/64.0); + glVertex2f (x+w, y+h); + glTexCoord2f ( x/64.0, (y+h)/64.0 ); + glVertex2f (x, y+h); + glEnd (); +} + +/* +================ +Draw_LoadingFill +By Crow_bar +================ +*/ +void Draw_LoadingFill(void) +{ + if(!loading_num_step) + return; + + int size = 8; + int max_step = 350; + int x = (vid.width / 2) - (max_step / 2); + int y = vid.height - (size/ 2) - 25; + int l; + char str[64]; + char* text; + + + if(loading_cur_step > loading_num_step) + loading_cur_step = loading_num_step; + + if (loading_cur_step < loading_cur_step_bk) + loading_cur_step = loading_cur_step_bk; + + if (loading_cur_step == loading_num_step && loading_cur_step_bk != loading_num_step) + loading_cur_step = loading_cur_step_bk; + + float loadsize = loading_cur_step * (max_step / loading_num_step); + Draw_FillByColor (x - 2, y - 2, max_step + 4, size + 4, 69, 69, 69, 255); + Draw_FillByColor (x, y, (int)loadsize, size, 0, 0, 0, 200); + + switch(loading_step) { + case 1: text = "Loading Models.."; break; + case 2: text = "Loading World.."; break; + case 3: text = "Running Test Frame.."; break; + case 4: text = "Loading Sounds.."; break; + default: text = "Initializing.."; break; + } + + Draw_ColoredStringCentered(y, text, 255, 255, 255, 255, 1); + + loading_cur_step_bk = loading_cur_step; +} + +void Clear_LoadingFill (void) +{ + //it is end loading + loading_cur_step = 0; + loading_cur_step_bk = 0; + loading_num_step = 0; + loading_step = -1; + memset(loading_name, 0, sizeof(loading_name)); +} + +/* +============= +Draw_FillByColor + +Fills a box of pixels with a single color +============= +*/ +void Draw_FillByColor (int x, int y, int w, int h, int r, int g, int b, int a) +{ + glDisable (GL_TEXTURE_2D); + glEnable (GL_BLEND); //johnfitz -- for alpha + glDisable (GL_ALPHA_TEST); //johnfitz -- for alpha + glColor4f ((float)(r/255.0f), (float)(g/255.0f), (float)(b/255.0f), (float)(a/255.0f)); + + glBegin (GL_QUADS); + + glVertex2f (x,y); + glVertex2f (x+w, y); + glVertex2f (x+w, y+h); + glVertex2f (x, y+h); + + glEnd (); + glColor4f (1,1,1,1); + glDisable (GL_BLEND); //johnfitz -- for alpha + glEnable(GL_ALPHA_TEST); //johnfitz -- for alpha + glEnable (GL_TEXTURE_2D); +} +//============================================================================= + +byte *StringToRGB (char *s) +{ + byte *col; + static byte rgb[4]; + + Cmd_TokenizeString (s); + if (Cmd_Argc() == 3) + { + rgb[0] = (byte)Q_atoi(Cmd_Argv(0)); + rgb[1] = (byte)Q_atoi(Cmd_Argv(1)); + rgb[2] = (byte)Q_atoi(Cmd_Argv(2)); + } + else + { + col = (byte *)&d_8to24table[(byte)Q_atoi(s)]; + rgb[0] = col[0]; + rgb[1] = col[1]; + rgb[2] = col[2]; + } + rgb[3] = 255; + + return rgb; +} + +extern cvar_t crosshair; +extern qboolean croshhairmoving; +//extern cvar_t cl_zoom; +extern qpic_t *hitmark; +double Hitmark_Time, crosshair_spread_time; +float cur_spread; +float crosshair_offset_step; + +int CrossHairWeapon (void) +{ + int i; + switch(cl.stats[STAT_ACTIVEWEAPON]) + { + case W_COLT: + case W_BIATCH: + case W_357: + case W_KILLU: + i = 22; + break; + case W_PTRS: + case W_PENETRATOR: + case W_KAR_SCOPE: + case W_HEADCRACKER: + case W_KAR: + case W_ARMAGEDDON: + case W_SPRING: + case W_PULVERIZER: + i = 65; + break; + case W_MP40: + case W_AFTERBURNER: + case W_STG: + case W_SPATZ: + case W_THOMPSON: + case W_GIBS: + case W_BAR: + case W_WIDOW: + case W_PPSH: + case W_REAPER: + case W_RAY: + case W_PORTER: + case W_TYPE: + case W_SAMURAI: + case W_FG: + case W_IMPELLER: + case W_MP5: + case W_KOLLIDER: + i = 10; + break; + case W_BROWNING: + case W_ACCELERATOR: + case W_MG: + case W_BARRACUDA: + i = 30; + break; + case W_SAWNOFF: + case W_SNUFF: + i = 50; + break; + case W_TRENCH: + case W_GUT: + case W_DB: + case W_BORE: + i = 35; + break; + case W_GEWEHR: + case W_COMPRESSOR: + case W_M1: + case W_M1000: + case W_M1A1: + case W_WIDDER: + i = 5; + break; + case W_PANZER: + case W_LONGINUS: + case W_TESLA: + i = 0; + break; + default: + i = 0; + break; + } + + i *= 0.68; + i += 6; + + if (cl.perks & 64) + i *= 0.75; + + return i; +} +int CrossHairMaxSpread (void) +{ + int i; + switch(cl.stats[STAT_ACTIVEWEAPON]) + { + case W_COLT: + case W_BIATCH: + case W_STG: + case W_SPATZ: + case W_MP40: + case W_AFTERBURNER: + case W_THOMPSON: + case W_GIBS: + case W_BAR: + case W_WIDOW: + case W_357: + case W_KILLU: + case W_BROWNING: + case W_ACCELERATOR: + case W_FG: + case W_IMPELLER: + case W_MP5: + case W_KOLLIDER: + case W_MG: + case W_BARRACUDA: + case W_PPSH: + case W_REAPER: + case W_RAY: + case W_PORTER: + case W_TYPE: + case W_SAMURAI: + i = 48; + break; + case W_PTRS: + case W_PENETRATOR: + case W_KAR_SCOPE: + case W_HEADCRACKER: + case W_KAR: + case W_ARMAGEDDON: + case W_SPRING: + case W_PULVERIZER: + i = 75; + break; + case W_SAWNOFF: + case W_SNUFF: + i = 50; + break; + case W_DB: + case W_BORE: + case W_TRENCH: + case W_GUT: + case W_GEWEHR: + case W_COMPRESSOR: + case W_M1: + case W_M1000: + case W_M1A1: + case W_WIDDER: + i = 35; + break; + case W_PANZER: + case W_LONGINUS: + case W_TESLA: + i = 0; + break; + default: + i = 0; + break; + } + + i *= 0.68; + i += 6; + + if (cl.perks & 64) + i *= 0.75; + + return i; +} + +/* +================ +Draw_Crosshair +================ +*/ + +extern float crosshair_opacity; +extern cvar_t cl_crosshair_debug; +extern qboolean crosshair_pulse_grenade; +void Draw_Crosshair (void) +{ + if (cl_crosshair_debug.value) { + Draw_FillByColor(vid.width/2, 0, 1, 240, 255, 0, 0, 255); + Draw_FillByColor(0, vid.height/2, 400, 1, 0, 255, 0, 255); + } + + if (cl.stats[STAT_HEALTH] <= 20) + return; + + if (cl.stats[STAT_ZOOM] == 2) + Draw_Pic (-39, -15, sniper_scope); + if (Hitmark_Time > sv.time) + Draw_Pic ((vid.width - hitmark->width)/2,(vid.height - hitmark->height)/2, hitmark); + + // Make sure to do this after hitmark drawing. + if (cl.stats[STAT_ZOOM] == 2 || cl.stats[STAT_ZOOM] == 1) + return; + + if (!crosshair_opacity) + crosshair_opacity = 255; + + float col; + + if (sv_player->v.facingenemy == 1) { + col = 0; + } else { + col = 255; + } + + // crosshair moving + if (crosshair_spread_time > sv.time && crosshair_spread_time) + { + cur_spread = cur_spread + 10; + crosshair_opacity = 128; + + if (cur_spread >= CrossHairMaxSpread()) + cur_spread = CrossHairMaxSpread(); + } + // crosshair not moving + else if (crosshair_spread_time < sv.time && crosshair_spread_time) + { + cur_spread = cur_spread - 4; + crosshair_opacity = 255; + + if (cur_spread <= 0) { + cur_spread = 0; + crosshair_spread_time = 0; + } + } + + int x_value, y_value; + int crosshair_offset; + + // Standard crosshair (+) + if (crosshair.value == 1) { + crosshair_offset = CrossHairWeapon() + cur_spread; + if (CrossHairMaxSpread() < crosshair_offset || croshhairmoving) + crosshair_offset = CrossHairMaxSpread(); + + if (sv_player->v.view_ofs[2] == 8) { + crosshair_offset *= 0.80; + } else if (sv_player->v.view_ofs[2] == -10) { + crosshair_offset *= 0.65; + } + + crosshair_offset_step += (crosshair_offset - crosshair_offset_step) * 0.5; + + x_value = (vid.width - 3)/2 - crosshair_offset_step; + y_value = (vid.height - 1)/2; + Draw_FillByColor(x_value, y_value, 3, 1, 255, (int)col, (int)col, (int)crosshair_opacity); + + x_value = (vid.width - 3)/2 + crosshair_offset_step; + y_value = (vid.height - 1)/2; + Draw_FillByColor(x_value, y_value, 3, 1, 255, (int)col, (int)col, (int)crosshair_opacity); + + x_value = (vid.width - 1)/2; + y_value = (vid.height - 3)/2 - crosshair_offset_step; + Draw_FillByColor(x_value, y_value, 1, 3, 255, (int)col, (int)col, (int)crosshair_opacity); + + x_value = (vid.width - 1)/2; + y_value = (vid.height - 3)/2 + crosshair_offset_step; + Draw_FillByColor(x_value, y_value, 1, 3, 255, (int)col, (int)col, (int)crosshair_opacity); + } + // Area of Effect (o) + else if (crosshair.value == 2) { + Draw_CharacterRGBA((vid.width)/2-4, (vid.height)/2, 'O', 255, (int)col, (int)col, (int)crosshair_opacity, 1); + } + // Dot crosshair (.) + else if (crosshair.value == 3) { + Draw_CharacterRGBA((vid.width - 8)/2, (vid.height - 8)/2, '.', 255, (int)col, (int)col, (int)crosshair_opacity, 1); + } + // Grenade crosshair + else if (crosshair.value == 4) { + if (crosshair_pulse_grenade) { + crosshair_offset_step = 0; + cur_spread = 2; + } + + crosshair_pulse_grenade = false; + + crosshair_offset = 12 + cur_spread; + crosshair_offset_step += (crosshair_offset - crosshair_offset_step) * 0.5; + + x_value = (vid.width - 3)/2 - crosshair_offset_step; + y_value = (vid.height - 1)/2; + Draw_FillByColor(x_value, y_value, 3, 1, 255, 255, 255, 255); + + x_value = (vid.width - 3)/2 + crosshair_offset_step; + y_value = (vid.height - 1)/2; + Draw_FillByColor(x_value, y_value, 3, 1, 255, 255, 255, 255); + + x_value = (vid.width - 1)/2; + y_value = (vid.height - 3)/2 - crosshair_offset_step; + Draw_FillByColor(x_value, y_value, 1, 3, 255, 255, 255, 255); + + x_value = (vid.width - 1)/2; + y_value = (vid.height - 3)/2 + crosshair_offset_step; + Draw_FillByColor(x_value, y_value, 1, 3, 255, 255, 255, 255); + } +} + + +/* +================ +Draw_FadeScreen + +================ +*/ +void Draw_FadeScreen (void) +{ + glEnable (GL_BLEND); + glDisable (GL_TEXTURE_2D); + glColor4f (0, 0, 0, 0.8); + glBegin (GL_QUADS); + + glVertex2f (0,0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + + glEnd (); + glColor4f (1,1,1,1); + glEnable (GL_TEXTURE_2D); + glDisable (GL_BLEND); + + Sbar_Changed(); +} + +//============================================================================= + +/* +================ +GL_Set2D + +Setup as if the screen was 320*200 +================ +*/ +void GL_Set2D (void) +{ + glViewport (glx, gly, glwidth, glheight); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity (); + glOrtho (0, vid.width, vid.height, 0, -99999, 99999); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity (); + + glDisable (GL_DEPTH_TEST); + glDisable (GL_CULL_FACE); + glDisable (GL_BLEND); + //glEnable (GL_ALPHA_TEST); + //glDisable (GL_ALPHA_TEST); + + glColor4f (1,1,1,1); +} + +//==================================================================== + +/* +================ +GL_FindTexture +================ +*/ +int GL_FindTexture (char *identifier) +{ + int i; + gltexture_t *glt; + + for (i=0, glt=gltextures ; iidentifier)) + return gltextures[i].texnum; + } + + return -1; +} + +/* +================ +GL_ResampleTexture +================ +*/ +void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) +{ + int i, j; + unsigned *inrow; + unsigned frac, fracstep; + + fracstep = inwidth*0x10000/outwidth; + for (i=0 ; i> 1; + for (j=0 ; j>16]; + frac += fracstep; + out[j+1] = inrow[frac>>16]; + frac += fracstep; + out[j+2] = inrow[frac>>16]; + frac += fracstep; + out[j+3] = inrow[frac>>16]; + frac += fracstep; + } + } +} + +/* +================ +GL_Resample8BitTexture -- JACK +================ +*/ +void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight) +{ + int i, j; + unsigned char *inrow; + unsigned frac, fracstep; + + fracstep = inwidth*0x10000/outwidth; + for (i=0 ; i> 1; + for (j=0 ; j>16]; + frac += fracstep; + out[j+1] = inrow[frac>>16]; + frac += fracstep; + out[j+2] = inrow[frac>>16]; + frac += fracstep; + out[j+3] = inrow[frac>>16]; + frac += fracstep; + } + } +} + + +/* +================ +GL_MipMap + +Operates in place, quartering the size of the texture +================ +*/ +void GL_MipMap (byte *in, int width, int height) +{ + int i, j; + byte *out; + + width <<=2; + height >>= 1; + out = in; + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2; + out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2; + out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2; + } + } +} + +/* +================ +GL_MipMap8Bit + +Mipping for 8 bit textures +================ +*/ +void GL_MipMap8Bit (byte *in, int width, int height) +{ + int i, j; + unsigned short r,g,b; + byte *out, *at1, *at2, *at3, *at4; + +// width <<=2; + height >>= 1; + out = in; + for (i=0 ; i>=5; + g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5; + b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5; + + out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)]; + } + } +} + +/* +=============== +GL_Upload32 +=============== +*/ +void GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap, qboolean alpha) +{ + int samples; +static unsigned scaled[1024*512]; // [512*256]; + int scaled_width, scaled_height; + + for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) + ; + for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) + ; + + scaled_width >>= (int)gl_picmip.value; + scaled_height >>= (int)gl_picmip.value; + + if (scaled_width > gl_max_size.value) + scaled_width = gl_max_size.value; + if (scaled_height > gl_max_size.value) + scaled_height = gl_max_size.value; + + if (scaled_width * scaled_height > sizeof(scaled)/4) + Sys_Error ("GL_LoadTexture: too big"); + + samples = alpha ? gl_alpha_format : gl_solid_format; + + texels += scaled_width * scaled_height; + + if (scaled_width == width && scaled_height == height) + { + if (!mipmap) + { + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + goto done; + } + memcpy (scaled, data, width*height*4); + } + else + GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); + + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + GL_MipMap ((byte *)scaled, scaled_width, scaled_height); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + miplevel++; + glTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); + } + } +done: ; + + if (mipmap) + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } +} + +void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap, qboolean alpha) +{ + int i, s; + qboolean noalpha; + unsigned char scaled[1024*512]; // [512*256]; + int scaled_width, scaled_height; + + s = width*height; + // if there are no transparent pixels, make it a 3 component + // texture even if it was specified as otherwise + if (alpha) + { + noalpha = true; + for (i=0 ; i>= (int)gl_picmip.value; + scaled_height >>= (int)gl_picmip.value; + + if (scaled_width > gl_max_size.value) + scaled_width = gl_max_size.value; + if (scaled_height > gl_max_size.value) + scaled_height = gl_max_size.value; + + if (scaled_width * scaled_height > sizeof(scaled)) + Sys_Error ("GL_LoadTexture: too big"); + + texels += scaled_width * scaled_height; + + if (scaled_width == width && scaled_height == height) + { + if (!mipmap) + { + glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data); + goto done; + } + memcpy (scaled, data, width*height); + } + else + GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height); + + glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + miplevel++; + glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); + } + } +done: ; + + + if (mipmap) + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } +} + +/* +=============== +GL_Upload8 +=============== +*/ +void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha) +{ +static unsigned trans[640*480]; // FIXME, temporary + int i, s; + qboolean noalpha; + int p; + + s = width*height; + // if there are no transparent pixels, make it a 3 component + // texture even if it was specified as otherwise + if (alpha) + { + noalpha = true; + for (i=0 ; iidentifier)) + { + if (lhcsum != glt->lhcsum || width != glt->width || height != glt->height) + { + Con_DPrintf("GL_LoadTexture: cache mismatch\n"); + Con_DPrintf("lhcsum: %d - %d\twidth: %d - %d\theight: %d - %d\n", lhcsum, glt->lhcsum, width, glt->width, height, glt->height); + goto GL_LoadTexture_setup; + } + return glt->texnum; + } + } + } + + // whoever at id or threewave must've been half asleep... + GL_LoadTexture_setup: + glt = &gltextures[numgltextures]; + numgltextures++; + glt->texnum = texture_extension_number; + texture_extension_number++; + strcpy (gltextures[glt->texnum].identifier, identifier); + + + // naievil -- why do we have this twice lol + gltextures[glt->texnum].checksum = lhcsum; + //gltextures[glt->texnum].lhcsum = lhcsum; + + gltextures[glt->texnum].width = width; + gltextures[glt->texnum].height = height; + gltextures[glt->texnum].original_width = width; + gltextures[glt->texnum].original_height = height; + gltextures[glt->texnum].mipmap = mipmap; + gltextures[glt->texnum].bytesperpixel = bytesperpixel; + + if (!isDedicated) + { + GL_Bind(glt->texnum); + if (bytesperpixel == 1) { + GL_Upload8 (data, width, height, mipmap, alpha); + } + else if (bytesperpixel == 4) { + GL_Upload32 ((unsigned*)data, width, height, mipmap, true); + } + else { + Sys_Error("GL_LoadTexture: unknown bytesperpixel\n"); + } + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + return glt->texnum; +} + +//Diabolickal TGA End + +/****************************************/ + +/* +================ +GL_LoadTexture32 +================ +*/ +extern byte vid_gamma_table[256]; + +int GL_LoadTexture32 (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha) +{ + int i; + gltexture_t *glt; + int image_size = width * height; + + // see if the texture is already present + if (identifier[0]) + { + for (i=0, glt=gltextures ; iidentifier)) + { + if (width != glt->width || height != glt->height) { + // naievil -- fixme: this means we have a memory leak somewhere, was sys_error + // OR that two different people used the same texture name + // which is actually possible + Con_Printf("GL_LoadTexture: cache mismatch for %s\n", identifier); + break; + } + return gltextures[i].texnum; + } + } + } + else { + glt = &gltextures[numgltextures]; + numgltextures++; + } + + strcpy (glt->identifier, identifier); + glt->texnum = texture_extension_number; + + gltextures[glt->texnum].width = width; + gltextures[glt->texnum].height = height; + gltextures[glt->texnum].original_width = width; + gltextures[glt->texnum].original_height = height; + gltextures[glt->texnum].mipmap = mipmap; + gltextures[glt->texnum].bytesperpixel = 4; + + GL_Bind(texture_extension_number ); + + for (i = 0; i < image_size; i++){ + data[4 * i] = gammatable[data[4 * i]]; + data[4 * i + 1] = gammatable[data[4 * i + 1]]; + data[4 * i + 2] = gammatable[data[4 * i + 2]]; + } + + GL_Upload32 ((unsigned *)data, width, height, mipmap, alpha); + + texture_extension_number++; + + return texture_extension_number-1; +} + +//Diabolickal End + +/* +================ +GL_LoadPicTexture +================ +*/ +int GL_LoadPicTexture (qpic_t *pic) +{ + return GL_LoadTexture ("", pic->width, pic->height, pic->data, false, true, 1); +} + +/****************************************/ + +static GLenum oldtarget = 0; + +float gVertexBuffer[VERTEXARRAYSIZE]; +float gColorBuffer[VERTEXARRAYSIZE]; +float gTexCoordBuffer[VERTEXARRAYSIZE]; + +void GL_SelectTexture (GLenum target) +{ + if (!gl_mtexable) + return; + qglSelectTextureSGIS(target); + if (target == oldtarget) + return; + cnttextures[oldtarget-TEXTURE0_SGIS] = currenttexture; + currenttexture = cnttextures[target-TEXTURE0_SGIS]; + oldtarget = target; +} + + + +//Diabolickal TGA Begin +int image_width; +int image_height; +#define IMAGE_MAX_DIMENSIONS 512 + +/* +================================================================= + PCX Loading +================================================================= +*/ + +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 data; // unbounded +} pcx_t; + +/* +============ +LoadPCX +============ +*/ +byte* LoadPCX (FILE *f, int matchwidth, int matchheight) +{ + pcx_t *pcx, pcxbuf; + byte palette[768]; + byte *pix, *image_rgba; + int x, y; + int dataByte, runLength; + int count; + +// +// parse the PCX file +// + fread (&pcxbuf, 1, sizeof(pcxbuf), f); + pcx = &pcxbuf; + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 + || pcx->xmax >= 514 + || pcx->ymax >= 514) + { + Con_Printf ("Bad pcx file\n"); + return NULL; + } + if (matchwidth && (pcx->xmax+1) != matchwidth) + return NULL; + if (matchheight && (pcx->ymax+1) != matchheight) + return NULL; + // seek to palette + fseek (f, -768, SEEK_END); + fread (palette, 1, 768, f); + fseek (f, sizeof(pcxbuf) - 4, SEEK_SET); + count = (pcx->xmax+1) * (pcx->ymax+1); + image_rgba = (byte*)malloc( count * 4); + + for (y=0 ; y<=pcx->ymax ; y++) + { + pix = image_rgba + 4*y*(pcx->xmax+1); + for (x=0 ; x<=pcx->xmax ; ) // muff - fixed - was referencing ymax + { + dataByte = fgetc(f); + if((dataByte & 0xC0) == 0xC0) + { + runLength = dataByte & 0x3F; + dataByte = fgetc(f); + } + else + runLength = 1; + + while(runLength-- > 0) + { + pix[0] = palette[dataByte*3]; + pix[1] = palette[dataByte*3+1]; + pix[2] = palette[dataByte*3+2]; + pix[3] = 255; + pix += 4; + x++; + } + } + } + image_width = pcx->xmax+1; + image_height = pcx->ymax+1; + + fclose(f); + return image_rgba; +} + + +/* +========================================================= + + Targa + +========================================================= +*/ + +#define TGA_MAXCOLORS 16384 + +/* Definitions for image types. */ +#define TGA_Null 0 /* no image data */ +#define TGA_Map 1 /* Uncompressed, color-mapped images. */ +#define TGA_RGB 2 /* Uncompressed, RGB images. */ +#define TGA_Mono 3 /* Uncompressed, black and white images. */ +#define TGA_RLEMap 9 /* Runlength encoded color-mapped images. */ +#define TGA_RLERGB 10 /* Runlength encoded RGB images. */ +#define TGA_RLEMono 11 /* Compressed, black and white images. */ +#define TGA_CompMap 32 /* Compressed color-mapped data, using Huffman, Delta, and runlength encoding. */ +#define TGA_CompMap4 33 /* Compressed color-mapped data, using Huffman, Delta, and runlength encoding. 4-pass quadtree-type process. */ + +/* Definitions for interleave flag. */ +#define TGA_IL_None 0 /* non-interleaved. */ +#define TGA_IL_Two 1 /* two-way (even/odd) interleaving */ +#define TGA_IL_Four 2 /* four way interleaving */ +#define TGA_IL_Reserved 3 /* reserved */ + +/* Definitions for origin flag */ +#define TGA_O_UPPER 0 /* Origin in lower left-hand corner. */ +#define TGA_O_LOWER 1 /* Origin in upper left-hand corner. */ + +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; + +int fgetLittleShort (FILE *f) +{ + byte b1, b2; + + b1 = fgetc(f); + b2 = fgetc(f); + + return (short)(b1 + b2*256); +} + +int fgetLittleLong (FILE *f) +{ + byte b1, b2, b3, b4; + + b1 = fgetc(f); + b2 = fgetc(f); + b3 = fgetc(f); + b4 = fgetc(f); + + return b1 + (b2<<8) + (b3<<16) + (b4<<24); +} + +/* +============= +LoadTGA +============= +*/ +byte *LoadTGA (FILE *fin, int matchwidth, int matchheight) +{ + int w, h, x, y, realrow, truerow, baserow, i, temp1, temp2, pixel_size, map_idx; + int RLE_count, RLE_flag, size, interleave, origin; + qboolean mapped, rlencoded; + byte *data, *dst, r, g, b, a, j, k, l, *ColorMap; + TargaHeader header; + + header.id_length = fgetc (fin); + header.colormap_type = fgetc (fin); + header.image_type = fgetc (fin); + header.colormap_index = fgetLittleShort (fin); + header.colormap_length = fgetLittleShort (fin); + header.colormap_size = fgetc (fin); + header.x_origin = fgetLittleShort (fin); + header.y_origin = fgetLittleShort (fin); + header.width = fgetLittleShort (fin); + header.height = fgetLittleShort (fin); + header.pixel_size = fgetc (fin); + header.attributes = fgetc (fin); + + if (header.width > IMAGE_MAX_DIMENSIONS || header.height > IMAGE_MAX_DIMENSIONS) + { + Con_DPrintf ("TGA image %s exceeds maximum supported dimensions\n", fin); + fclose (fin); + return NULL; + } + + if ((matchwidth && header.width != matchwidth) || (matchheight && header.height != matchheight)) + { + fclose (fin); + return NULL; + } + + if (header.id_length != 0) + fseek (fin, header.id_length, SEEK_CUR); + + /* validate TGA type */ + switch (header.image_type) + { + case TGA_Map: + case TGA_RGB: + case TGA_Mono: + case TGA_RLEMap: + case TGA_RLERGB: + case TGA_RLEMono: + break; + + default: + Con_DPrintf ("Unsupported TGA image %s: Only type 1 (map), 2 (RGB), 3 (mono), 9 (RLEmap), 10 (RLERGB), 11 (RLEmono) TGA images supported\n"); + fclose (fin); + return NULL; + } + + /* validate color depth */ + switch (header.pixel_size) + { + case 8: + case 15: + case 16: + case 24: + case 32: + break; + + default: + Con_DPrintf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit images (with colormaps) supported\n"); + fclose (fin); + return NULL; + } + + r = g = b = a = l = 0; + + /* if required, read the color map information. */ + ColorMap = NULL; + mapped = (header.image_type == TGA_Map || header.image_type == TGA_RLEMap) && header.colormap_type == 1; + if (mapped) + { + /* validate colormap size */ + switch (header.colormap_size) + { + case 8: + case 15: + case 16: + case 32: + case 24: + break; + + default: + Con_DPrintf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit colormaps supported\n"); + fclose (fin); + return NULL; + } + + temp1 = header.colormap_index; + temp2 = header.colormap_length; + if ((temp1 + temp2 + 1) >= TGA_MAXCOLORS) + { + fclose (fin); + return NULL; + } + ColorMap = (byte*)(malloc (TGA_MAXCOLORS * 4)); + map_idx = 0; + for (i = temp1 ; i < temp1 + temp2 ; ++i, map_idx += 4) + { + /* read appropriate number of bytes, break into rgb & put in map. */ + switch (header.colormap_size) + { + case 8: /* grey scale, read and triplicate. */ + r = g = b = getc (fin); + a = 255; + break; + + case 15: /* 5 bits each of red green and blue. */ + /* watch byte order. */ + j = getc (fin); + k = getc (fin); + l = ((unsigned int)k << 8) + j; + r = (byte)(((k & 0x7C) >> 2) << 3); + g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3); + b = (byte)((j & 0x1F) << 3); + a = 255; + break; + + case 16: /* 5 bits each of red green and blue, 1 alpha bit. */ + /* watch byte order. */ + j = getc (fin); + k = getc (fin); + l = ((unsigned int)k << 8) + j; + r = (byte)(((k & 0x7C) >> 2) << 3); + g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3); + b = (byte)((j & 0x1F) << 3); + a = (k & 0x80) ? 255 : 0; + break; + + case 24: /* 8 bits each of blue, green and red. */ + b = getc (fin); + g = getc (fin); + r = getc (fin); + a = 255; + l = 0; + break; + + case 32: /* 8 bits each of blue, green, red and alpha. */ + b = getc (fin); + g = getc (fin); + r = getc (fin); + a = getc (fin); + l = 0; + break; + } + ColorMap[map_idx+0] = r; + ColorMap[map_idx+1] = g; + ColorMap[map_idx+2] = b; + ColorMap[map_idx+3] = a; + } + } + + /* check run-length encoding. */ + rlencoded = (header.image_type == TGA_RLEMap || header.image_type == TGA_RLERGB || header.image_type == TGA_RLEMono); + RLE_count = RLE_flag = 0; + + image_width = w = header.width; + image_height = h = header.height; + + size = w * h * 4; + data = (byte*)(malloc (size)); + + /* read the Targa file body and convert to portable format. */ + pixel_size = header.pixel_size; + origin = (header.attributes & 0x20) >> 5; + interleave = (header.attributes & 0xC0) >> 6; + truerow = baserow = 0; + for (y=0 ; y> 2) << 3); + g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3); + b = (byte)((j & 0x1F) << 3); + a = 255; + break; + + case 16: /* 5 bits each of red green and blue, 1 alpha bit. */ + /* watch byte order. */ + j = getc (fin); + k = getc (fin); + l = ((unsigned int)k << 8) + j; + r = (byte)(((k & 0x7C) >> 2) << 3); + g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3); + b = (byte)((j & 0x1F) << 3); + a = (k & 0x80) ? 255 : 0; + break; + + case 24: /* 8 bits each of blue, green and red. */ + b = getc (fin); + g = getc (fin); + r = getc (fin); + a = 255; + l = 0; + break; + + case 32: /* 8 bits each of blue, green, red and alpha. */ + b = getc (fin); + g = getc (fin); + r = getc (fin); + a = getc (fin); + l = 0; + break; + + default: + Con_DPrintf ("Malformed TGA image: Illegal pixel_size '%d'\n", pixel_size); + fclose (fin); + free (data); + if (mapped) + free (ColorMap); + return NULL; + } + +PixEncode: + if (mapped) + { + map_idx = l * 4; + *dst++ = ColorMap[map_idx+0]; + *dst++ = ColorMap[map_idx+1]; + *dst++ = ColorMap[map_idx+2]; + *dst++ = ColorMap[map_idx+3]; + } + else + { + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = a; + } + } + + if (interleave == TGA_IL_Four) + truerow += 4; + else if (interleave == TGA_IL_Two) + truerow += 2; + else + truerow++; + if (truerow >= h) + truerow = ++baserow; + } + + if (mapped) + free (ColorMap); + + fclose (fin); + + return data; +} + +/*small function to read files with stb_image - single-file image loader library. +** downloaded from: https://raw.githubusercontent.com/nothings/stb/master/stb_image.h +** only use jpeg+png formats, because tbh there's not much need for the others. +** */ +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_JPEG +#define STBI_ONLY_PNG +#include "../../stb_image.h" +byte* LoadSTBI(FILE *f, int width, int height) +{ + int bpp; + int inwidth, inheight; + byte* image = stbi_load_from_file(f, &inwidth, &inheight, &bpp, 4); + // wtf? + image_width = inwidth; + image_height = inheight; + fclose(f); + return image; +} + +byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight) + +{ + FILE *f; + char basename[128], name[132]; + byte *c; + + if (complain == qfalse) + COM_StripExtension(filename, basename); // strip the extension to allow TGA + else + strcpy(basename, filename); + + c = (byte*)basename; + while (*c) + { + if (*c == '*') + *c = '+'; + c++; + } + + //Try PCX + sprintf (name, "%s.pcx", basename); + COM_FOpenFile (name, &f); + if (f) + return LoadPCX (f, matchwidth, matchheight); + //Try TGA + sprintf (name, "%s.tga", basename); + COM_FOpenFile (name, &f); + if (f) + return LoadTGA (f, matchwidth, matchheight); + //Try PNG + sprintf (name, "%s.png", basename); + COM_FOpenFile (name, &f); + if (f) + return LoadSTBI (f, matchwidth, matchheight); + //Try JPEG + sprintf (name, "%s.jpeg", basename); + COM_FOpenFile (name, &f); + if (f) + return LoadSTBI (f, matchwidth, matchheight); + sprintf (name, "%s.jpg", basename); + COM_FOpenFile (name, &f); + if (f) + return LoadSTBI (f, matchwidth, matchheight); + + //if (complain) + // Con_Printf ("Couldn't load %s.tga or %s.pcx \n", filename); + + return NULL; +} + +int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap) +{ + int texnum; + byte *data; + if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight))) { + Con_DPrintf("Cannot load image %s\n", filename); + return 0; + } + texnum = GL_LoadTexture (filename, image_width, image_height, data, mipmap, qtrue, 4); + free(data); + return texnum; +} +// Tomaz || TGA End diff --git a/source/ctr/gl/gl_fog.c b/source/ctr/gl/gl_fog.c new file mode 100644 index 0000000..c967681 --- /dev/null +++ b/source/ctr/gl/gl_fog.c @@ -0,0 +1,517 @@ +/* + +Fogging system based on FitzQuake's implementation +Now with Quakespasm bits thrown into it! + +*/ + +#include "../../quakedef.h" + +//============================================================================== +// +// GLOBAL FOG +// +//============================================================================== + +#define DEFAULT_DENSITY 1.0 +#define DEFAULT_GRAY 0.3 + +float density = 1.0; +float fog_density_gl; + +float fog_start; +float fog_end; +float fog_red; +float fog_green; +float fog_blue; + +float old_density; +float old_start; +float old_end; +float old_red; +float old_green; +float old_blue; + +float fade_time; //duration of fade +float fade_done; //time when fade will be done + +/* +============= +Fog_Update + +update internal variables +============= +*/ +void Fog_Update (float start, float end, float red, float green, float blue, float time) +{ + //save previous settings for fade + if (time > 0) + { + //check for a fade in progress + if (fade_done > cl.time) + { + float f; + + f = (fade_done - cl.time) / fade_time; + old_start = f * old_start + (1.0 - f) * fog_start; + old_end = f * old_end + (1.0 - f) * fog_end; + old_red = f * old_red + (1.0 - f) * fog_red; + old_green = f * old_green + (1.0 - f) * fog_green; + old_blue = f * old_blue + (1.0 - f) * fog_blue; + old_density = f * old_density + (1.0 - f) * fog_density_gl; + } + else + { + old_start = fog_start; + old_end = fog_end; + old_red = fog_red; + old_green = fog_green; + old_blue = fog_blue; + old_density = fog_density_gl; + } + } + + fog_start = start; + fog_end = end; + fog_red = red; + fog_green = green; + fog_blue = blue; + fade_time = time; + fade_done = cl.time + time; + fog_density_gl = ((fog_start / fog_end))/3.5; +} + +/* +============= +Fog_ParseServerMessage + +handle an SVC_FOG message from server +============= +*/ +void Fog_ParseServerMessage (void) +{ + float start, end, red, green, blue, time; + + start = MSG_ReadByte() / 255.0; + end = MSG_ReadByte() / 255.0; + red = MSG_ReadByte() / 255.0; + green = MSG_ReadByte() / 255.0; + blue = MSG_ReadByte() / 255.0; + time = MSG_ReadShort() / 100.0; + + Fog_Update (start, end, red, green, blue, time); +} + +/* +============= +Fog_FogCommand_f + +handle the 'fog' console command +============= +*/ +void Fog_FogCommand_f (void) +{ + switch (Cmd_Argc()) + { + default: + case 1: + Con_Printf("usage:\n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf("current values:\n"); + Con_Printf(" \"start\" is \"%f\"\n", fog_start); + Con_Printf(" \"end\" is \"%f\"\n", fog_end); + Con_Printf(" \"red\" is \"%f\"\n", fog_red); + Con_Printf(" \"green\" is \"%f\"\n", fog_green); + Con_Printf(" \"blue\" is \"%f\"\n", fog_blue); + Con_Printf(" \"fade\" is \"%f\"\n", fade_time); + break; + case 2: //TEST + Fog_Update(fog_start, + fog_end, + fog_red, + fog_green, + fog_blue, + atof(Cmd_Argv(1))); + break; + case 3: + Fog_Update(atof(Cmd_Argv(1)), + atof(Cmd_Argv(2)), + fog_red, + fog_green, + fog_blue, + 0.0); + break; + case 4: + Fog_Update(fog_start, + fog_end, + CLAMP(0.0, atof(Cmd_Argv(1)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(2)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(3)), 100.0), + 0.0); + break; + case 5: //TEST + Fog_Update(fog_start, + fog_end, + CLAMP(0.0, atof(Cmd_Argv(1)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(2)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(3)), 100.0), + atof(Cmd_Argv(4))); + break; + case 6: + Fog_Update(atof(Cmd_Argv(1)), + atof(Cmd_Argv(2)), + CLAMP(0.0, atof(Cmd_Argv(3)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(4)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(5)), 100.0), + 0.0); + break; + case 7: + Fog_Update(atof(Cmd_Argv(1)), + atof(Cmd_Argv(2)), + CLAMP(0.0, atof(Cmd_Argv(3)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(4)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(5)), 100.0), + atof(Cmd_Argv(6))); + break; + } +} + +/* +============= +Fog_ParseWorldspawn + +called at map load +============= +*/ +void Fog_ParseWorldspawn (void) +{ + char key[128], value[4096]; + const char *data; + + fog_density_gl = DEFAULT_DENSITY; + //initially no fog + fog_start = 0; + old_start = 0; + + fog_end = 4000; + old_end = 4000; + + fog_red = 0.0; + old_red = 0.0; + + fog_green = 0.0; + old_green = 0.0; + + fog_blue = 0.0; + old_blue = 0.0; + + fade_time = 0.0; + fade_done = 0.0; + + data = COM_Parse(cl.worldmodel->entities); + if (!data) + return; // error + if (com_token[0] != '{') + return; // error + while (1) + { + data = COM_Parse(data); + if (!data) + return; // error + if (com_token[0] == '}') + break; // end of worldspawn + if (com_token[0] == '_') + strcpy(key, com_token + 1); + else + strcpy(key, com_token); + while (key[strlen(key)-1] == ' ') // remove trailing spaces + key[strlen(key)-1] = 0; + data = COM_Parse(data); + if (!data) + return; // error + strcpy(value, com_token); + + if (!strcmp("fog", key)) + { + sscanf(value, "%f %f %f %f %f", &fog_start, &fog_end, &fog_red, &fog_green, &fog_blue); + } + + fog_density_gl = ((fog_start / fog_end))/3.5; + } +} + +/* +============= +Fog_GetColor + +calculates fog color for this frame, taking into account fade times +============= +*/ +float *Fog_GetColor (void) +{ + static float c[4]; // = {0.1f, 0.1f, 0.1f, 1.0f} + + float f; + int i; + + if (fade_done > cl.time) + { + f = (fade_done - cl.time) / fade_time; + c[0] = f * old_red + (1.0 - f) * fog_red; + c[1] = f * old_green + (1.0 - f) * fog_green; + c[2] = f * old_blue + (1.0 - f) * fog_blue; + c[3] = 1.0; + } + else + { + c[0] = fog_red; + c[1] = fog_green; + c[2] = fog_blue; + c[3] = 1.0; + } + + //find closest 24-bit RGB value, so solid-colored sky can match the fog perfectly + for (i=0;i<3;i++) + c[i] = (float)(rint(c[i] * 255)) / 255.0f; + + for (i = 0; i < 3; i++) + c[i] /= 64.0; + + return c; +} + +/* +============= +Fog_GetDensity + +returns current density of fog + +============= +*/ +float Fog_GetDensity (void) +{ + float f; + + if (fade_done > cl.time) + { + f = (fade_done - cl.time) / fade_time; + return f * old_density + (1.0 - f) * fog_density_gl; + } + else + return fog_density_gl; +} + +/* +============= +Fog_SetupFrame + +called at the beginning of each frame +============= +*/ +void Fog_SetupFrame (void) +{ + glFogfv(GL_FOG_COLOR, Fog_GetColor()); + glFogf(GL_FOG_DENSITY, 0.2f); + glFogf(GL_FOG_START, fog_start); + glFogf(GL_FOG_END, fog_end); +} + +/* +============= +Fog_SetColorForSkyS +Start called before drawing flat-colored sky +Crow_bar* +============= +*/ +void Fog_SetColorForSkyS (void) +{ + if (fog_end > 0.0f && r_skyfog.value) + { + float a = fog_end * 0.00025f; + float r = fog_red * 0.01f + (a * 0.25f); + float g = fog_green * 0.01f + (a * 0.25f); + float b = fog_blue * 0.01f + (a * 0.25f); + + if (a > 1.0f) + a = 1.0f; + if (r > 1.0f) + r = 1.0f; + if (g > 1.0f) + g = 1.0f; + if (b > 1.0f) + b = 1.0f; + + glColor4f(r, g, b, a); + } +} + +/* +============= +Fog_GetStart +returns current start of fog +============= +*/ +float Fog_GetStart (void) +{ + float f; + + if (fade_done > cl.time) + { + f = (fade_done - cl.time) / fade_time; + return f * old_start + (1.0 - f) * fog_start; + } + else + return fog_start; +} + +/* +============= +Fog_GetEnd +returns current end of fog +============= +*/ +float Fog_GetEnd (void) +{ + float f; + + if (fade_done > cl.time) + { + f = (fade_done - cl.time) / fade_time; + return f * old_start + (1.0 - f) * fog_end; + } + else + return fog_end; +} + +/* +============= +Fog_SetColorForSky +End called before drawing flat-colored sky +Crow_bar* +============= +*/ +void Fog_SetColorForSkyE (void) +{ + if (fog_end > 0.0f && r_skyfog.value) + { + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } +} + +/* +============= +Fog_EnableGFog + +called before drawing stuff that should be fogged +============= +*/ +void Fog_EnableGFog (void) +{ + if (!Fog_GetStart() == 0 || !Fog_GetEnd() <= 0) { + glEnable(GL_FOG); + } +} + +/* +============= +Fog_DisableGFog + +called after drawing stuff that should be fogged +============= +*/ +void Fog_DisableGFog (void) +{ + if (!Fog_GetStart() == 0 || !Fog_GetEnd() <= 0) + glDisable(GL_FOG); +} + +/* +============= +Fog_SetColorForSky + +called before drawing flat-colored sky +============= +*/ +/* +void Fog_SetColorForSky (void) +{ + float c[3]; + float f, d; + + if (fade_done > cl.time) + { + f = (fade_done - cl.time) / fade_time; + d = f * old_density + (1.0 - f) * fog_density; + c[0] = f * old_red + (1.0 - f) * fog_red; + c[1] = f * old_green + (1.0 - f) * fog_green; + c[2] = f * old_blue + (1.0 - f) * fog_blue; + } + else + { + d = fog_density; + c[0] = fog_red; + c[1] = fog_green; + c[2] = fog_blue; + } + + if (d > 0) + glColor3fv (c); +} +*/ +//============================================================================== +// +// VOLUMETRIC FOG +// +//============================================================================== +/* + +void Fog_DrawVFog (void){} +void Fog_MarkModels (void){} +*/ +//============================================================================== +// +// INIT +// +//============================================================================== + +/* +============= +Fog_NewMap + +called whenever a map is loaded +============= +*/ + +void Fog_NewMap (void) +{ + Fog_ParseWorldspawn (); //for global fog +} + +/* +============= +Fog_Init + +called when quake initializes +============= +*/ +void Fog_Init (void) +{ + Cmd_AddCommand ("fog",Fog_FogCommand_f); + + //set up global fog + fog_start = 300; + fog_end = 4000; + fog_red = 0.5; + fog_green = 0.5; + fog_blue = 0.5; + fade_time = 1; + fog_density_gl = DEFAULT_DENSITY; + fade_time = 1; + + glFogi(GL_FOG_MODE, GL_LINEAR); +} \ No newline at end of file diff --git a/source/ctr/gl/gl_mesh.c b/source/ctr/gl/gl_mesh.c new file mode 100644 index 0000000..e5044f9 --- /dev/null +++ b/source/ctr/gl/gl_mesh.c @@ -0,0 +1,318 @@ +/* +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. + +*/ +// gl_mesh.c: triangle model functions + +#include "../../quakedef.h" + +/* +================================================================= + +ALIAS MODEL DISPLAY LIST GENERATION + +================================================================= +*/ + +model_t *aliasmodel; +aliashdr_t *paliashdr; + +qboolean used[8192]; + +// the command list holds counts and s/t values that are valid for +// every frame +int commands[8192]; +int numcommands; + +// all frames will have their vertexes rearranged and expanded +// so they are in the order expected by the command list +int vertexorder[8192]; +int numorder; + +int allverts, alltris; + +int stripverts[128]; +int striptris[128]; +int stripcount; + +/* +================ +StripLength +================ +*/ +int StripLength (int starttri, int startv) +{ + int m1, m2; + int j; + mtriangle_t *last, *check; + int k; + + used[starttri] = 2; + + last = &triangles[starttri]; + + stripverts[0] = last->vertindex[(startv)%3]; + stripverts[1] = last->vertindex[(startv+1)%3]; + stripverts[2] = last->vertindex[(startv+2)%3]; + + striptris[0] = starttri; + stripcount = 1; + + m1 = last->vertindex[(startv+2)%3]; + m2 = last->vertindex[(startv+1)%3]; + + // look for a matching triangle +nexttri: + for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) + { + if (check->facesfront != last->facesfront) + continue; + for (k=0 ; k<3 ; k++) + { + if (check->vertindex[k] != m1) + continue; + if (check->vertindex[ (k+1)%3 ] != m2) + continue; + + // this is the next part of the fan + + // if we can't use this triangle, this tristrip is done + if (used[j]) + goto done; + + // the new edge + if (stripcount & 1) + m2 = check->vertindex[ (k+2)%3 ]; + else + m1 = check->vertindex[ (k+2)%3 ]; + + stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ]; + striptris[stripcount] = j; + stripcount++; + + used[j] = 2; + goto nexttri; + } + } +done: + + // clear the temp used flags + for (j=starttri+1 ; jnumtris ; j++) + if (used[j] == 2) + used[j] = 0; + + return stripcount; +} + +/* +=========== +FanLength +=========== +*/ +int FanLength (int starttri, int startv) +{ + int m1, m2; + int j; + mtriangle_t *last, *check; + int k; + + used[starttri] = 2; + + last = &triangles[starttri]; + + stripverts[0] = last->vertindex[(startv)%3]; + stripverts[1] = last->vertindex[(startv+1)%3]; + stripverts[2] = last->vertindex[(startv+2)%3]; + + striptris[0] = starttri; + stripcount = 1; + + m1 = last->vertindex[(startv+0)%3]; + m2 = last->vertindex[(startv+2)%3]; + + + // look for a matching triangle +nexttri: + for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) + { + if (check->facesfront != last->facesfront) + continue; + for (k=0 ; k<3 ; k++) + { + if (check->vertindex[k] != m1) + continue; + if (check->vertindex[ (k+1)%3 ] != m2) + continue; + + // this is the next part of the fan + + // if we can't use this triangle, this tristrip is done + if (used[j]) + goto done; + + // the new edge + m2 = check->vertindex[ (k+2)%3 ]; + + stripverts[stripcount+2] = m2; + striptris[stripcount] = j; + stripcount++; + + used[j] = 2; + goto nexttri; + } + } +done: + + // clear the temp used flags + for (j=starttri+1 ; jnumtris ; j++) + if (used[j] == 2) + used[j] = 0; + + return stripcount; +} + + +/* +================ +BuildTris + +Generate a list of trifans or strips +for the model, which holds for all frames +================ +*/ +void BuildTris (void) +{ + int i, j, k; + int startv; + mtriangle_t *last, *check; + int m1, m2; + int striplength; + trivertx_t *v; + mtriangle_t *tv; + float s, t; + int index; + int len, bestlen, besttype; + int bestverts[1024]; + int besttris[1024]; + int type; + + // + // build tristrips + // + numorder = 0; + numcommands = 0; + memset (used, 0, sizeof(used)); + for (i=0 ; inumtris ; i++) + { + // pick an unused triangle and start the trifan + if (used[i]) + continue; + + bestlen = 0; + for (type = 0 ; type < 2 ; type++) +// type = 1; + { + for (startv =0 ; startv < 3 ; startv++) + { + if (type == 1) + len = StripLength (i, startv); + else + len = FanLength (i, startv); + if (len > bestlen) + { + besttype = type; + bestlen = len; + for (j=0 ; jskinwidth / 2; // on back side + s = (s + 0.5) / pheader->skinwidth; + t = (t + 0.5) / pheader->skinheight; + + *(float *)&commands[numcommands++] = s; + *(float *)&commands[numcommands++] = t; + } + } + + commands[numcommands++] = 0; // end of list marker + + Con_DPrintf ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands); + + allverts += numorder; + alltris += pheader->numtris; +} + + +/* +================ +GL_MakeAliasModelDisplayLists +================ +*/ +void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr) +{ + int i, j; + maliasgroup_t *paliasgroup; + int *cmds; + trivertx_t *verts; + char cache[MAX_QPATH], fullpath[MAX_OSPATH], *c; + FILE *f; + int len; + byte *data; + + aliasmodel = m; + paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); + + BuildTris (); // trifans or lists + + paliashdr->poseverts = numorder; + + cmds = Hunk_Alloc (numcommands * 4); + paliashdr->commands = (byte *)cmds - (byte *)paliashdr; + memcpy (cmds, commands, numcommands * 4); + + verts = Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts + * sizeof(trivertx_t) ); + paliashdr->posedata = (byte *)verts - (byte *)paliashdr; + for (i=0 ; inumposes ; i++) + for (j=0 ; jcache); + if (r) + return r; + + Mod_LoadModel (mod, true); + + if (!mod->cache.data) + Sys_Error ("cache fail (%s)", mod->name); + return mod->cache.data; +} + +/* +=============== +Mod_PointInLeaf +=============== +*/ +mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) +{ + mnode_t *node; + float d; + mplane_t *plane; + + if (!model || !model->nodes) + Sys_Error ("Mod_PointInLeaf: bad model"); + + node = model->nodes; + while (1) + { + if (node->contents < 0) + return (mleaf_t *)node; + plane = node->plane; + d = DotProduct (p,plane->normal) - plane->dist; + if (d > 0) + node = node->children[0]; + else + node = node->children[1]; + } + + return NULL; // never reached +} + + +/* +=================== +Mod_DecompressVis +=================== +*/ +byte *Mod_DecompressVis (byte *in, model_t *model) +{ + static byte decompressed[MAX_MAP_LEAFS/8]; + int c; + byte *out; + int row; + + row = (model->numleafs+7)>>3; + out = decompressed; + +#if 0 + memcpy (out, in, row); +#else + if (!in) + { // no vis info, so make all visible + while (row) + { + *out++ = 0xff; + row--; + } + return decompressed; + } + + do + { + if (*in) + { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + while (c) + { + *out++ = 0; + c--; + } + } while (out - decompressed < row); +#endif + + return decompressed; +} + +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) +{ + if (leaf == model->leafs) + return mod_novis; + return Mod_DecompressVis (leaf->compressed_vis, model); +} + +/* +=================== +Mod_ClearAll +=================== +*/ +void Mod_ClearAll (void) +{ + int i; + model_t *mod; + + for (i=0 , mod=mod_known ; itype != mod_alias) + mod->needload = true; +} + +/* +================== +Mod_FindName + +================== +*/ +model_t *Mod_FindName (char *name) +{ + int i; + model_t *mod; + + if (!name[0]) + Sys_Error ("Mod_ForName: NULL name"); + +// +// search the currently loaded models +// + for (i=0 , mod=mod_known ; iname, name) ) + break; + + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); + strcpy (mod->name, name); + mod->needload = true; + mod_numknown++; + } + + return mod; +} + +/* +================== +Mod_TouchModel + +================== +*/ +void Mod_TouchModel (char *name) +{ + model_t *mod; + + mod = Mod_FindName (name); + + if (!mod->needload) + { + if (mod->type == mod_alias) + Cache_Check (&mod->cache); + } +} + +/* +================== +Mod_LoadModel + +Loads a model into the cache +================== +*/ +model_t *Mod_LoadModel (model_t *mod, qboolean crash) +{ + void *d; + unsigned *buf; + byte stackbuf[1024]; // avoid dirtying the cache heap + + if (!mod->needload) + { + if (mod->type == mod_alias) + { + d = Cache_Check (&mod->cache); + if (d) + return mod; + } + else + return mod; // not cached at all + } + +// +// because the world is so huge, load it one piece at a time +// + if (!crash) + { + + } + +// +// load the file +// + buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + if (!buf) + { + // Reload with another .mdl + buf = (unsigned *)COM_LoadStackFile("models/missing_model.mdl", stackbuf, sizeof(stackbuf)); + if (buf) + { + Con_Printf ("Missing model %s substituted\n", mod->name); + } + return NULL; + } + +// +// allocate a new model +// + COM_FileBase (mod->name, loadname); + + loadmodel = mod; + +// +// fill it in +// + +// call the apropriate loader + mod->needload = false; + + switch (LittleLong(*(unsigned *)buf)) + { + case IDPOLYHEADER: + Mod_LoadAliasModel (mod, buf); + break; + + case IDSPRITEHEADER: + Mod_LoadSpriteModel (mod, buf); + break; + + default: + Mod_LoadBrushModel (mod, buf); + break; + } + + return mod; +} + +/* +================== +Mod_ForName + +Loads in a model for the given name +================== +*/ +model_t *Mod_ForName (char *name, qboolean crash) +{ + model_t *mod; + + mod = Mod_FindName (name); + + return Mod_LoadModel (mod, crash); +} + + +/* +=============================================================================== + + BRUSHMODEL LOADING + +=============================================================================== +*/ + +byte *mod_base; + + +/* +================= +Mod_LoadTextures +================= +*/ +void Mod_LoadTextures (lump_t *l) +{ + int i, j, pixels, num, max, altmax; + miptex_t *mt; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + dmiptexlump_t *m; + + //Diabolickal TGA Begin + char texname[64]; + //Diabolckal TGA End + + byte *data; + + if (!l->filelen) + { + loadmodel->textures = NULL; + return; + } + m = (dmiptexlump_t *)(mod_base + l->fileofs); + + m->nummiptex = LittleLong (m->nummiptex); + + loadmodel->numtextures = m->nummiptex; + loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); + + loading_num_step = loading_num_step + m->nummiptex; + + for (i=0 ; inummiptex ; i++) + { + m->dataofs[i] = LittleLong(m->dataofs[i]); + if (m->dataofs[i] == -1) + continue; + mt = (miptex_t *)((byte *)m + m->dataofs[i]); + mt->width = LittleLong (mt->width); + mt->height = LittleLong (mt->height); + for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); + + if ( (mt->width & 15) || (mt->height & 15) ) + Sys_Error ("Texture %s is not 16 aligned", mt->name); + pixels = mt->width*mt->height/64*85; + tx = Hunk_AllocName (sizeof(texture_t), loadname ); + loadmodel->textures[i] = tx; + + memcpy (tx->name, mt->name, sizeof(tx->name)); + tx->width = mt->width; + tx->height = mt->height; + for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); + + + if (loadmodel->bspversion != HL_BSPVERSION && !strncmp(mt->name,"sky",3)) + { + R_InitSky (mt); + } + else + { + if (loadmodel->bspversion == HL_BSPVERSION) + { + if (1)//((data = WAD3_LoadTexture(mt))) + { + texture_mode = GL_LINEAR_MIPMAP_NEAREST; + sprintf (texname, "textures/%s", mt->name); + tx->gl_texturenum = loadtextureimage (texname, 0, 0, false, true); //Diabolickal TGA textures + if (tx->gl_texturenum == 0)// did not find a matching TGA... + { + data = WAD3_LoadTexture(mt); + bool choosealpha = mt->name[0] == '{' ? true : false; // naievil -- need to choose alpha mode for certain textures + tx->gl_texturenum = GL_LoadTexture32 (mt->name, tx->width, tx->height, (byte *)data, true, choosealpha); + } + texture_mode = GL_LINEAR; + } + } + else + { + texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; + tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false, 1); + texture_mode = GL_LINEAR; + } + } + strcpy(loading_name, mt->name); + loading_cur_step++; + SCR_UpdateScreen(); + } + +// +// sequence the animations +// + for (i=0 ; inummiptex ; i++) + { + tx = loadmodel->textures[i]; + if (!tx || tx->name[0] != '+') + continue; + if (tx->anim_next) + continue; // allready sequenced + + // find the number of frames in the animation + memset (anims, 0, sizeof(anims)); + memset (altanims, 0, sizeof(altanims)); + + max = tx->name[1]; + altmax = 0; + if (max >= 'a' && max <= 'z') + max -= 'a' - 'A'; + if (max >= '0' && max <= '9') + { + max -= '0'; + altmax = 0; + anims[max] = tx; + max++; + } + else if (max >= 'A' && max <= 'J') + { + altmax = max - 'A'; + max = 0; + altanims[altmax] = tx; + altmax++; + } + else + Sys_Error ("Bad animating texture %s", tx->name); + + for (j=i+1 ; jnummiptex ; j++) + { + tx2 = loadmodel->textures[j]; + if (!tx2 || tx2->name[0] != '+') + continue; + if (strcmp (tx2->name+2, tx->name+2)) + continue; + + num = tx2->name[1]; + if (num >= 'a' && num <= 'z') + num -= 'a' - 'A'; + if (num >= '0' && num <= '9') + { + num -= '0'; + anims[num] = tx2; + if (num+1 > max) + max = num + 1; + } + else if (num >= 'A' && num <= 'J') + { + num = num - 'A'; + altanims[num] = tx2; + if (num+1 > altmax) + altmax = num+1; + } + else + Sys_Error ("Bad animating texture %s", tx->name); + } + +#define ANIM_CYCLE 2 + // link them all together + for (j=0 ; jname); + tx2->anim_total = max * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = anims[ (j+1)%max ]; + if (altmax) + tx2->alternate_anims = altanims[0]; + } + for (j=0 ; jname); + tx2->anim_total = altmax * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = altanims[ (j+1)%altmax ]; + if (max) + tx2->alternate_anims = anims[0]; + } + } +} + +/* +================= +Mod_LoadLighting +================= +*/ +void Mod_LoadLighting (lump_t *l) +{ + // LordHavoc: .lit support begin + int i; + byte *in, *out, *data; + byte d; + char litfilename[1024]; + loadmodel->lightdata = NULL; + + // Diabolickal HLBSP + if (loadmodel->bspversion == HL_BSPVERSION) + { + if (!l->filelen) + { + return; + } + loadmodel->lightdata = (Hunk_AllocName ( l->filelen, loadname)); + memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); + return; + } + + // LordHavoc: check for a .lit file + strcpy(litfilename, loadmodel->name); + COM_StripExtension(litfilename, litfilename); + strcat(litfilename, ".lit"); + data = (byte*) COM_LoadHunkFile (litfilename); + if (data) + { + if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T') + { + i = LittleLong(((int *)data)[1]); + if (i == 1) + { + Con_DPrintf("%s loaded", litfilename); + loadmodel->lightdata = data + 8; + return; + } + else + Con_Printf("Unknown .lit file version (%d)\n", i); + } + else + Con_Printf("Corrupt .lit file (old version?), ignoring\n"); + } + + // LordHavoc: no .lit found, expand the white lighting data to color + if (!l->filelen) + return; + loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, litfilename); + in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write + out = loadmodel->lightdata; + memcpy (in, mod_base + l->fileofs, l->filelen); + for (i = 0;i < l->filelen;i++) + { + d = *in++; + *out++ = d; + *out++ = d; + *out++ = d; + } + // LordHavoc: .lit support end + +} + + +/* +================= +Mod_LoadVisibility +================= +*/ +void Mod_LoadVisibility (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->visdata = NULL; + return; + } + loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadEntities +================= +*/ +void Mod_LoadEntities (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->entities = NULL; + return; + } + loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadVertexes +================= +*/ +void Mod_LoadVertexes (lump_t *l) +{ + dvertex_t *in; + mvertex_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->vertexes = out; + loadmodel->numvertexes = count; + + for ( i=0 ; iposition[0] = LittleFloat (in->point[0]); + out->position[1] = LittleFloat (in->point[1]); + out->position[2] = LittleFloat (in->point[2]); + } +} + +/* +================= +Mod_LoadSubmodels +================= +*/ +void Mod_LoadSubmodels (lump_t *l) +{ + dmodel_t *in; + dmodel_t *out; + int i, j, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->submodels = out; + loadmodel->numsubmodels = count; + + for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; + out->maxs[j] = LittleFloat (in->maxs[j]) + 1; + out->origin[j] = LittleFloat (in->origin[j]); + } + for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); + out->visleafs = LittleLong (in->visleafs); + out->firstface = LittleLong (in->firstface); + out->numfaces = LittleLong (in->numfaces); + } +} + +/* +================= +Mod_LoadEdges +================= +*/ +void Mod_LoadEdges (lump_t *l) +{ + dedge_t *in; + medge_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); + + loadmodel->edges = out; + loadmodel->numedges = count; + + for ( i=0 ; iv[0] = (unsigned short)LittleShort(in->v[0]); + out->v[1] = (unsigned short)LittleShort(in->v[1]); + } +} + +/* +================= +Mod_LoadTexinfo +================= +*/ +void Mod_LoadTexinfo (lump_t *l) +{ + texinfo_t *in; + mtexinfo_t *out; + int i, j, count; + int miptex; + float len1, len2; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); + len1 = Length (out->vecs[0]); + len2 = Length (out->vecs[1]); + len1 = (len1 + len2)/2; + if (len1 < 0.32) + out->mipadjust = 4; + else if (len1 < 0.49) + out->mipadjust = 3; + else if (len1 < 0.99) + out->mipadjust = 2; + else + out->mipadjust = 1; +#if 0 + if (len1 + len2 < 0.001) + out->mipadjust = 1; // don't crash + else + out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); +#endif + + miptex = LittleLong (in->miptex); + out->flags = LittleLong (in->flags); + + if (!loadmodel->textures) + { + out->texture = r_notexture_mip; // checkerboard texture + out->flags = 0; + } + else + { + if (miptex >= loadmodel->numtextures) + Sys_Error ("miptex >= loadmodel->numtextures"); + out->texture = loadmodel->textures[miptex]; + if (!out->texture) + { + out->texture = r_notexture_mip; // texture not found + out->flags = 0; + } + } + } +} + +/* +================ +CalcSurfaceExtents + +Fills in s->texturemins[] and s->extents[] +================ +*/ +void CalcSurfaceExtents (msurface_t *s) +{ + float mins[2], maxs[2], val; + int i,j, e; + mvertex_t *v; + mtexinfo_t *tex; + int bmins[2], bmaxs[2]; + + mins[0] = mins[1] = 999999; + maxs[0] = maxs[1] = -99999; + + tex = s->texinfo; + + for (i=0 ; inumedges ; i++) + { + e = loadmodel->surfedges[s->firstedge+i]; + if (e >= 0) + v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + else + v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + + for (j=0 ; j<2 ; j++) + { + val = v->position[0] * tex->vecs[j][0] + + v->position[1] * tex->vecs[j][1] + + v->position[2] * tex->vecs[j][2] + + tex->vecs[j][3]; + if (val < mins[j]) + mins[j] = val; + if (val > maxs[j]) + maxs[j] = val; + } + } + + for (i=0 ; i<2 ; i++) + { + bmins[i] = (int)floorf(mins[i]/16); + bmaxs[i] = (int)ceilf(maxs[i]/16); + + s->texturemins[i] = bmins[i] * 16; + s->extents[i] = (bmaxs[i] - bmins[i]) * 16; + if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 /* 256 */ ) + Sys_Error ("Bad surface extents"); + } +} + + +/* +================= +Mod_LoadFaces +================= +*/ +void Mod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = (dface_t *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = (msurface_t*)Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->numedges); + out->flags = 0; + + planenum = LittleShort(in->planenum); + side = LittleShort(in->side); + if (side) + out->flags |= SURF_PLANEBACK; + + out->plane = loadmodel->planes + planenum; + + out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); + + CalcSurfaceExtents (out); + + // lighting info + + for (i=0 ; istyles[i] = in->styles[i]; + if (loadmodel->bspversion == HL_BSPVERSION) //Diabolickal HLBSP + i = LittleLong(in->lightofs/3); + else + i = LittleLong(in->lightofs); + if (i == -1) + out->samples = NULL; + else + out->samples = loadmodel->lightdata + (i * 3); // LordHavoc + + // set the drawing flags flag + + if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky + { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); +#ifndef QUAKE2 + GL_SubdivideSurface (out); // cut up polygon for warps +#endif + continue; + } + + if (!strncmp(out->texinfo->texture->name,"nodraw",6) || !strncmp(out->texinfo->texture->name,"NODRAW",6)) { + out->flags |= TEXFLAG_NODRAW; + continue; + } + + if (strstr(out->texinfo->texture->name,"light")) { + out->flags |= TEXFLAG_LIGHT; + continue; + } + + if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent + { + out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + GL_SubdivideSurface (out); // cut up polygon for warps + continue; + } + + } +} + + +/* +================= +Mod_SetParent +================= +*/ +void Mod_SetParent (mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents < 0) + return; + Mod_SetParent (node->children[0], node); + Mod_SetParent (node->children[1], node); +} + +/* +================= +Mod_LoadNodes +================= +*/ +void Mod_LoadNodes (lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->nodes = out; + loadmodel->numnodes = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->planenum); + out->plane = loadmodel->planes + p; + + out->firstsurface = LittleShort (in->firstface); + out->numsurfaces = LittleShort (in->numfaces); + + for (j=0 ; j<2 ; j++) + { + p = LittleShort (in->children[j]); + if (p >= 0) + out->children[j] = loadmodel->nodes + p; + else + out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); + } + } + + Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +} + +/* +================= +Mod_LoadLeafs +================= +*/ +void Mod_LoadLeafs (lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count, p; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->contents); + out->contents = p; + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstmarksurface); + out->nummarksurfaces = LittleShort(in->nummarksurfaces); + + p = LittleLong(in->visofs); + if (p == -1) + out->compressed_vis = NULL; + else + out->compressed_vis = loadmodel->visdata + p; + out->efrags = NULL; + + for (j=0 ; j<4 ; j++) + out->ambient_sound_level[j] = in->ambient_level[j]; + + // gl underwater warp + if (out->contents != CONTENTS_EMPTY) + { + for (j=0 ; jnummarksurfaces ; j++) + out->firstmarksurface[j]->flags |= SURF_UNDERWATER; + } + } +} + +/* +================= +Mod_LoadClipnodes +================= +*/ +void Mod_LoadClipnodes (lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->clipnodes = out; + loadmodel->numclipnodes = count; + + if (loadmodel->bspversion == HL_BSPVERSION) //Diabolickal HLBSP + { + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -36; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 36; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -32; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 32; + + hull = &loadmodel->hulls[3]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -18; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 18; + } else { + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 32; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 64; + } + + for (i=0 ; iplanenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[1] = LittleShort(in->children[1]); + } +} + +/* +================= +Mod_MakeHull0 + +Deplicate the drawing hull structure as a clipping hull +================= +*/ +void Mod_MakeHull0 (void) +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &loadmodel->hulls[0]; + + in = loadmodel->nodes; + count = loadmodel->numnodes; + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + + for (i=0 ; iplanenum = in->plane - loadmodel->planes; + for (j=0 ; j<2 ; j++) + { + child = in->children[j]; + if (child->contents < 0) + out->children[j] = child->contents; + else + out->children[j] = child - loadmodel->nodes; + } + } +} + +/* +================= +Mod_LoadMarksurfaces +================= +*/ +void Mod_LoadMarksurfaces (lump_t *l) +{ + int i, j, count; + short *in; + msurface_t **out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i= loadmodel->numsurfaces) + Sys_Error ("Mod_ParseMarksurfaces: bad surface number"); + out[i] = loadmodel->surfaces + j; + } +} + +/* +================= +Mod_LoadSurfedges +================= +*/ +void Mod_LoadSurfedges (lump_t *l) +{ + int i, count; + int *in, *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*2*sizeof(*out), loadname); + + loadmodel->planes = out; + loadmodel->numplanes = count; + + for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); + if (out->normal[j] < 0) + bits |= 1<dist = LittleFloat (in->dist); + out->type = LittleLong (in->type); + out->signbits = bits; + } +} + +/* +================= +RadiusFromBounds +================= +*/ +float RadiusFromBounds (vec3_t mins, vec3_t maxs) +{ + int i; + vec3_t corner; + + for (i=0 ; i<3 ; i++) + { + corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); + } + + return Length (corner); +} + +/* +================= +Mod_LoadBrushModel +================= +*/ +void Mod_LoadBrushModel (model_t *mod, void *buffer) +{ + int i, j; + dheader_t *header; + dmodel_t *bm; + + loadmodel->type = mod_brush; + + header = (dheader_t *)buffer; + + i = LittleLong (header->version); + mod->bspversion = i; + if (i != BSPVERSION && i != HL_BSPVERSION) + Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION); + +// swap all the lumps + mod_base = (byte *)header; + + for (i=0 ; ilumps[LUMP_VERTEXES]); + + loading_cur_step++; + strcpy(loading_name, "Edges"); + SCR_UpdateScreen (); + + Mod_LoadEdges (&header->lumps[LUMP_EDGES]); + + loading_cur_step++; + strcpy(loading_name, "Surfedges"); + SCR_UpdateScreen (); + + Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); + + loading_cur_step++; + strcpy(loading_name, "Entities"); + SCR_UpdateScreen (); + + Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); + + loading_cur_step++; + strcpy(loading_name, "Textures"); + SCR_UpdateScreen (); + + Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); + Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); + + loading_cur_step++; + SCR_UpdateScreen (); + + Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); + + loading_cur_step++; + strcpy(loading_name, "Texinfo"); + SCR_UpdateScreen (); + + Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); + + loading_cur_step++; + strcpy(loading_name, "Faces"); + SCR_UpdateScreen (); + + Mod_LoadFaces (&header->lumps[LUMP_FACES]); + + loading_cur_step++; + strcpy(loading_name, "Marksurfaces"); + SCR_UpdateScreen (); + + Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); + + loading_cur_step++; + strcpy(loading_name, "Visibility"); + SCR_UpdateScreen (); + + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + + loading_cur_step++; + strcpy(loading_name, "Leafs"); + SCR_UpdateScreen (); + + Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + + loading_cur_step++; + strcpy(loading_name, "Nodes"); + SCR_UpdateScreen (); + + Mod_LoadNodes (&header->lumps[LUMP_NODES]); + + loading_cur_step++; + strcpy(loading_name, "Clipnodes"); + SCR_UpdateScreen (); + + Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); + + loading_cur_step++; + strcpy(loading_name, "Submodels"); + SCR_UpdateScreen (); + + Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + + loading_cur_step++; + strcpy(loading_name, "Hull"); + SCR_UpdateScreen (); + + Mod_MakeHull0 (); + loading_cur_step++; + + loading_step = 2; + + strcpy(loading_name, "Screen"); + loading_cur_step++; + SCR_UpdateScreen (); + + mod->numframes = 2; // regular and alternate animation + +// +// set up the submodels (FIXME: this is confusing) +// + for (i=0 ; inumsubmodels ; i++) + { + bm = &mod->submodels[i]; + + mod->hulls[0].firstclipnode = bm->headnode[0]; + for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; + mod->hulls[j].lastclipnode = mod->numclipnodes-1; + } + + mod->firstmodelsurface = bm->firstface; + mod->nummodelsurfaces = bm->numfaces; + + VectorCopy (bm->maxs, mod->maxs); + VectorCopy (bm->mins, mod->mins); + + mod->radius = RadiusFromBounds (mod->mins, mod->maxs); + + mod->numleafs = bm->visleafs; + + if (i < mod->numsubmodels-1) + { // duplicate the basic information + char name[10]; + + sprintf (name, "*%i", i+1); + loadmodel = Mod_FindName (name); + *loadmodel = *mod; + strcpy (loadmodel->name, name); + mod = loadmodel; + } + } +} + +/* +============================================================================== + +ALIAS MODELS + +============================================================================== +*/ + +#pragma GCC push_options +#pragma GCC optimize ("O0") +aliashdr_t *pheader; + +stvert_t stverts[MAXALIASVERTS]; +mtriangle_t triangles[MAXALIASTRIS]; + +// a pose is a single set of vertexes. a frame may be +// an animating sequence of poses +trivertx_t *poseverts[MAXALIASFRAMES]; +int posenum; + +byte **player_8bit_texels_tbl; +byte *player_8bit_texels; + +/* +================= +Mod_LoadAliasFrame +================= +*/ +void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame) +{ + trivertx_t *pframe, *pinframe; + int i, j; + daliasframe_t *pdaliasframe; + + pdaliasframe = (daliasframe_t *)pin; + + strcpy (frame->name, pdaliasframe->name); + frame->firstpose = posenum; + frame->numposes = 1; + + for (i=0 ; i<3 ; i++) + { + // these are byte values, so we don't have to worry about + // endianness + frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i]; + frame->bboxmin.v[i] = pdaliasframe->bboxmax.v[i]; + } + + pinframe = (trivertx_t *)(pdaliasframe + 1); + + poseverts[posenum] = pinframe; + posenum++; + + pinframe += pheader->numverts; + + return (void *)pinframe; +} + + +/* +================= +Mod_LoadAliasGroup +================= +*/ +void *Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame) +{ + daliasgroup_t *pingroup; + int i, numframes; + daliasinterval_t *pin_intervals; + void *ptemp; + + pingroup = (daliasgroup_t *)pin; + + numframes = LittleLong (pingroup->numframes); + + frame->firstpose = posenum; + frame->numposes = numframes; + + for (i=0 ; i<3 ; i++) + { + // these are byte values, so we don't have to worry about endianness + frame->bboxmin.v[i] = pingroup->bboxmin.v[i]; + frame->bboxmin.v[i] = pingroup->bboxmax.v[i]; + } + + pin_intervals = (daliasinterval_t *)(pingroup + 1); + + frame->interval = LittleFloat (pin_intervals->interval); + + pin_intervals += numframes; + + ptemp = (void *)pin_intervals; + + for (i=0 ; inumverts; + } + + return ptemp; +} + +//========================================================= + +/* +================= +Mod_FloodFillSkin + +Fill background pixels so mipmapping doesn't have haloes - Ed +================= +*/ + +typedef struct +{ + short x, y; +} floodfill_t; + +extern unsigned d_8to24table[]; + +// must be a power of 2 +#define FLOODFILL_FIFO_SIZE 0x1000 +#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) + +#define FLOODFILL_STEP( off, dx, dy ) \ +{ \ + if (pos[off] == fillcolor) \ + { \ + pos[off] = 255; \ + fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ + } \ + else if (pos[off] != 255) fdc = pos[off]; \ +} + +void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight ) +{ + byte fillcolor = *skin; // assume this is the pixel to fill + floodfill_t fifo[FLOODFILL_FIFO_SIZE]; + int inpt = 0, outpt = 0; + int filledcolor = -1; + int i; + + if (filledcolor == -1) + { + filledcolor = 0; + // attempt to find opaque black + for (i = 0; i < 256; ++i) + if (d_8to24table[i] == (255 << 0)) // alpha 1.0 + { + filledcolor = i; + break; + } + } + + // can't fill to filled color or to transparent color (used as visited marker) + if ((fillcolor == filledcolor) || (fillcolor == 255)) + { + //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); + return; + } + + fifo[inpt].x = 0, fifo[inpt].y = 0; + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; + + while (outpt != inpt) + { + int x = fifo[outpt].x, y = fifo[outpt].y; + int fdc = filledcolor; + byte *pos = &skin[x + skinwidth * y]; + + outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; + + if (x > 0) FLOODFILL_STEP( -1, -1, 0 ); + if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 ); + if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 ); + if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 ); + skin[x + skinwidth * y] = fdc; + } +} + +/* +=============== +Mod_LoadAllSkins +=============== +*/ +void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype) +{ + int i, j, k; + char name[32], model[64], model2[64]; + int s; + byte *copy; + byte *skin; + byte *texels; + daliasskingroup_t *pinskingroup; + int groupskins; + daliasskininterval_t *pinskinintervals; + + skin = (byte *)(pskintype + 1); + + if (numskins < 1 || numskins > MAX_SKINS) + Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); + + s = pheader->skinwidth * pheader->skinheight; + + // + // General texture override stuff. + // + + // Mustang & Sally // v_biatch + if (strcmp(loadmodel->name, "models/weapons/m1911/v_biatch_left.mdl") == 0 || + strcmp(loadmodel->name, "models/weapons/m1911/v_biatch_right.mdl") == 0) { + pheader->gl_texturenum[0][0] = + pheader->gl_texturenum[0][1] = + pheader->gl_texturenum[0][2] = + pheader->gl_texturenum[0][3] = loadtextureimage("models/weapons/m1911/v_biatch.mdl_0", 0, 0, qtrue, qfalse); + + pheader->gl_texturenum[1][0] = + pheader->gl_texturenum[1][1] = + pheader->gl_texturenum[1][2] = + pheader->gl_texturenum[1][3] = loadtextureimage("models/weapons/m1911/v_biatch.mdl_0", 0, 0, qtrue, qfalse); + + pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + s); + return (void *)pskintype; + } + + for (i=0 ; itype == ALIAS_SKIN_SINGLE) { + Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight ); + COM_StripExtension(loadmodel->name, model); + + texels = Hunk_AllocName(s, loadname); + pheader->texels[i] = texels - (byte *)pheader; + memcpy (texels, (byte *)(pskintype + 1), s); + + // HACK HACK HACK + sprintf(model2, "%s.mdl_%i", model, i); + pheader->gl_texturenum[i][0] = + pheader->gl_texturenum[i][1] = + pheader->gl_texturenum[i][2] = + pheader->gl_texturenum[i][3] = loadtextureimage(model2, 0, 0, qtrue, qfalse); + + if (pheader->gl_texturenum[i][0] == 0) // did not find a matching TGA... + { + sprintf(name, "%s_%i", loadmodel->name, i); + pheader->gl_texturenum[i][0] = + pheader->gl_texturenum[i][1] = + pheader->gl_texturenum[i][2] = + pheader->gl_texturenum[i][3] = GL_LoadTexture (name, pheader->skinwidth, + pheader->skinheight, (byte *)(pskintype + 1), true, false, 1); + } + + pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + s); + } else { + // animating skin group. yuck. + pskintype++; + pinskingroup = (daliasskingroup_t *)pskintype; + groupskins = LittleLong (pinskingroup->numskins); + pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); + + pskintype = (void *)(pinskinintervals + groupskins); + + for (j=0 ; jskinwidth, pheader->skinheight ); + if (j == 0) { + texels = Hunk_AllocName(s, loadname); + pheader->texels[i] = texels - (byte *)pheader; + memcpy (texels, (byte *)(pskintype), s); + } + sprintf (name, "%s_%i_%i", loadmodel->name, i,j); + pheader->gl_texturenum[i][j&3] = + GL_LoadTexture (name, pheader->skinwidth, + pheader->skinheight, (byte *)(pskintype), true, false, 1); + pskintype = (daliasskintype_t *)((byte *)(pskintype) + s); + } + k = j; + for (/* */; j < 4; j++) + pheader->gl_texturenum[i][j&3] = + pheader->gl_texturenum[i][j - k]; + } + } + + return (void *)pskintype; +} + +//========================================================================= + +/* +================= +Mod_LoadAliasModel +================= +*/ +void Mod_LoadAliasModel (model_t *mod, void *buffer) +{ + int i, j; + mdl_t *pinmodel; + stvert_t *pinstverts; + dtriangle_t *pintriangles; + int version, numframes, numskins; + int size; + daliasframetype_t *pframetype; + daliasskintype_t *pskintype; + int start, end, total; + +// some models are special + // NOTE: comparing not only with player.mdl, but with all models + // begin with "player" coz we need to support DME models as well! + if (!strncmp(mod->name, "progs/player", 12)) + mod->modhint = MOD_PLAYER; + else if (!strcmp(mod->name, "progs/eyes.mdl")) + mod->modhint = MOD_EYES; + else if (!strcmp(mod->name, "progs/flame0.mdl") || + !strcmp(mod->name, "progs/flame.mdl") || + !strcmp(mod->name, "progs/flame2.mdl")) + mod->modhint = MOD_FLAME; + else if (!strcmp(mod->name, "progs/bolt.mdl") || + !strcmp(mod->name, "models/misc/bolt2.mdl") || + !strcmp(mod->name, "progs/bolt3.mdl")) + mod->modhint = MOD_THUNDERBOLT; + else if (!strcmp(mod->name, "progs/VModels/v_Colt.mdl") || //JUKKI Add nzp weapons here please plox + !strcmp(mod->name, "progs/VModels/v_kar.mdl") || + !strcmp(mod->name, "progs/VModels/v_thomp.mdl")) + mod->modhint = MOD_WEAPON; + else if (!strcmp(mod->name, "progs/lavaball.mdl")) + mod->modhint = MOD_LAVABALL; + else if (!strcmp(mod->name, "progs/spike.mdl") || + !strcmp(mod->name, "progs/s_spike.mdl")) + mod->modhint = MOD_SPIKE; + else if (!strcmp(mod->name, "progs/shambler.mdl")) + mod->modhint = MOD_SHAMBLER; + else + mod->modhint = MOD_NORMAL; + + start = Hunk_LowMark (); + + pinmodel = (mdl_t *)buffer; + + version = LittleLong (pinmodel->version); + if (version != ALIAS_VERSION) + Sys_Error ("%s has wrong version number (%i should be %i)", + mod->name, version, ALIAS_VERSION); + +// +// allocate space for a working header, plus all the data except the frames, +// skin and group info +// + size = sizeof (aliashdr_t) + + (LittleLong (pinmodel->numframes) - 1) * + sizeof (pheader->frames[0]); + pheader = Hunk_AllocName (size, loadname); + + mod->flags = LittleLong (pinmodel->flags); + +// +// endian-adjust and copy the data, starting with the alias model header +// + pheader->boundingradius = LittleFloat (pinmodel->boundingradius); + pheader->numskins = LittleLong (pinmodel->numskins); + pheader->skinwidth = LittleLong (pinmodel->skinwidth); + pheader->skinheight = LittleLong (pinmodel->skinheight); + + if (pheader->skinheight > MAX_LBM_HEIGHT) + Sys_Error ("model %s has a skin taller than %d", mod->name, + MAX_LBM_HEIGHT); + + pheader->numverts = LittleLong (pinmodel->numverts); + + if (pheader->numverts <= 0) + Sys_Error ("model %s has no vertices", mod->name); + + //Con_Printf("numverts: %d\n", pheader->numverts); + if (pheader->numverts > MAXALIASVERTS) + Sys_Error ("model %s has too many vertices", mod->name); + + pheader->numtris = LittleLong (pinmodel->numtris); + + if (pheader->numtris <= 0) + Sys_Error ("model %s has no triangles", mod->name); + + pheader->numframes = LittleLong (pinmodel->numframes); + numframes = pheader->numframes; + if (numframes < 1) + Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); + + pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; + mod->synctype = LittleLong (pinmodel->synctype); + mod->numframes = pheader->numframes; + + for (i=0 ; i<3 ; i++) + { + pheader->scale[i] = LittleFloat (pinmodel->scale[i]); + pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); + pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); + } + + +// +// load the skins +// + pskintype = (daliasskintype_t *)&pinmodel[1]; + pskintype = Mod_LoadAllSkins (pheader->numskins, pskintype); + +// +// load base s and t vertices +// + pinstverts = (stvert_t *)pskintype; + + for (i=0 ; inumverts ; i++) + { + stverts[i].onseam = LittleLong (pinstverts[i].onseam); + stverts[i].s = LittleLong (pinstverts[i].s); + stverts[i].t = LittleLong (pinstverts[i].t); + } + +// +// load triangle lists +// + pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts]; + + for (i=0 ; inumtris ; i++) + { + triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); + + for (j=0 ; j<3 ; j++) + { + triangles[i].vertindex[j] = + LittleLong (pintriangles[i].vertindex[j]); + } + } + +// +// load the frames +// + posenum = 0; + pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; + + for (i=0 ; itype); + + if (frametype == ALIAS_SINGLE) + { + pframetype = (daliasframetype_t *) + Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]); + } + else + { + pframetype = (daliasframetype_t *) + Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]); + } + } + + pheader->numposes = posenum; + + mod->type = mod_alias; + +// FIXME: do this right + mod->mins[0] = mod->mins[1] = mod->mins[2] = -32; + mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 32; + + // + // build the draw lists + // + GL_MakeAliasModelDisplayLists (mod, pheader); + +// +// move the complete, relocatable alias model to the cache +// + end = Hunk_LowMark (); + total = end - start; + + Cache_Alloc (&mod->cache, total, loadname); + if (!mod->cache.data) + return; + memcpy (mod->cache.data, pheader, total); + + Hunk_FreeToLowMark (start); +} + +//============================================================================= + +/* +================= +Mod_LoadSpriteFrame +================= +*/ +void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum) +{ + dspriteframe_t *pinframe; + mspriteframe_t *pspriteframe; + int i, width, height, size, origin[2]; + unsigned short *ppixout; + byte *ppixin; + char name[64], sprite[64], sprite2[64]; + + pinframe = (dspriteframe_t *)pin; + + width = LittleLong (pinframe->width); + height = LittleLong (pinframe->height); + size = width * height; + + pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t),loadname); + + Q_memset (pspriteframe, 0, sizeof (mspriteframe_t)); + + *ppframe = pspriteframe; + + pspriteframe->width = width; + pspriteframe->height = height; + origin[0] = LittleLong (pinframe->origin[0]); + origin[1] = LittleLong (pinframe->origin[1]); + + pspriteframe->up = origin[1]; + pspriteframe->down = origin[1] - height; + pspriteframe->left = origin[0]; + pspriteframe->right = width + origin[0]; + + // HACK HACK HACK + sprintf (name, "%s.spr_%i", loadmodel->name, framenum); + + COM_StripExtension(loadmodel->name, sprite); + sprintf(sprite2, "%s.spr_%i", sprite, framenum); + pspriteframe->gl_texturenum = loadtextureimage(sprite2, 0, 0, qtrue, qfalse); + + if (pspriteframe->gl_texturenum == 0) // did not find a matching TGA... + { + pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), true, true, 1); + } + + return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); +} + + +/* +================= +Mod_LoadSpriteGroup +================= +*/ +void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum) +{ + dspritegroup_t *pingroup; + mspritegroup_t *pspritegroup; + int i, numframes; + dspriteinterval_t *pin_intervals; + float *poutintervals; + void *ptemp; + + pingroup = (dspritegroup_t *)pin; + + numframes = LittleLong (pingroup->numframes); + + pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) + + (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname); + + pspritegroup->numframes = numframes; + + *ppframe = (mspriteframe_t *)pspritegroup; + + pin_intervals = (dspriteinterval_t *)(pingroup + 1); + + poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); + + pspritegroup->intervals = poutintervals; + + for (i=0 ; iinterval); + if (*poutintervals <= 0.0) + Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); + + poutintervals++; + pin_intervals++; + } + + ptemp = (void *)pin_intervals; + + for (i=0 ; iframes[i], framenum * 100 + i); + } + + return ptemp; +} + + +/* +================= +Mod_LoadSpriteModel +================= +*/ +void Mod_LoadSpriteModel (model_t *mod, void *buffer) +{ + int i; + int version; + dsprite_t *pin; + msprite_t *psprite; + int numframes; + int size; + dspriteframetype_t *pframetype; + + pin = (dsprite_t *)buffer; + + version = LittleLong (pin->version); + if (version != SPRITE_VERSION) + Sys_Error ("%s has wrong version number " + "(%i should be %i)", mod->name, version, SPRITE_VERSION); + + numframes = LittleLong (pin->numframes); + + size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); + + psprite = Hunk_AllocName (size, loadname); + + mod->cache.data = psprite; + + psprite->type = LittleLong (pin->type); + psprite->maxwidth = LittleLong (pin->width); + psprite->maxheight = LittleLong (pin->height); + psprite->beamlength = LittleFloat (pin->beamlength); + mod->synctype = LittleLong (pin->synctype); + psprite->numframes = numframes; + + mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; + mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; + mod->mins[2] = -psprite->maxheight/2; + mod->maxs[2] = psprite->maxheight/2; + +// +// load the frames +// + if (numframes < 1) + Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); + + mod->numframes = numframes; + + pframetype = (dspriteframetype_t *)(pin + 1); + + for (i=0 ; itype); + psprite->frames[i].type = frametype; + + if (frametype == SPR_SINGLE) + { + pframetype = (dspriteframetype_t *) + Mod_LoadSpriteFrame (pframetype + 1, + &psprite->frames[i].frameptr, i); + } + else + { + pframetype = (dspriteframetype_t *) + Mod_LoadSpriteGroup (pframetype + 1, + &psprite->frames[i].frameptr, i); + } + } + + mod->type = mod_sprite; +} +#pragma GCC pop_options +//============================================================================= + +/* +================ +Mod_Print +================ +*/ +void Mod_Print (void) +{ + int i; + model_t *mod; + + Con_Printf ("Cached models:\n"); + for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) + { + Con_Printf ("%8p : %s\n",mod->cache.data, mod->name); + } +} + + diff --git a/source/ctr/gl/gl_model.h b/source/ctr/gl/gl_model.h new file mode 100644 index 0000000..0a2028b --- /dev/null +++ b/source/ctr/gl/gl_model.h @@ -0,0 +1,468 @@ +/* +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__ +#define __MODEL__ + +#include "../modelgen.h" +#include "../../spritegn.h" + +/* + +d*_t structures are on-disk representations +m*_t structures are in-memory + +*/ + +// entity effects + +#define EF_BLUELIGHT 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_REDLIGHT 8 +#define EF_ORANGELIGHT 16 +#define EF_GREENLIGHT 32 +#define EF_PINKLIGHT 64 // formerly EF_LIGHT +#define EF_NODRAW 128 +#define EF_LIMELIGHT 256 // formerly EF_BRIGHTFIELD +#define EF_FULLBRIGHT 512 +#define EF_CYANLIGHT 1024 // formerly EF_DARKLIGHT +#define EF_YELLOWLIGHT 2048 // formerly EF_DARKFIELD +#define EF_PURPLELIGHT 4096 +#define EF_RAYRED 8196 // red trail for porter x2 +#define EF_RAYGREEN 16384 // green trail for ray gun + + +/* +============================================================================== + +BRUSH MODELS + +============================================================================== +*/ + + +// +// in memory representation +// +typedef struct +{ + vec3_t position; +} mvertex_t; + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + + +// plane_t structure +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 TEXFLAG_NODRAW 256 +#define TEXFLAG_LIGHT 512 + +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) +// vec3_t midpoint;//MHQuake // naievil -- fixme: this guy is causing some kind of rendering issue +} 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 int dlightbits[(MAX_DLIGHTS + 31) >> 5]; + // int is 32 bits, need an array for MAX_DLIGHTS > 32 + + 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; + +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; + +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 256 +#define MAXALIASTRIS 2048 +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 + + +// some models are special +typedef enum +{ + MOD_NORMAL, + MOD_PLAYER, + MOD_EYES, + MOD_FLAME, + MOD_THUNDERBOLT, + MOD_WEAPON, + MOD_LAVABALL, + MOD_SPIKE, + MOD_SHAMBLER, + MOD_SPR, + MOD_SPR32, +// MOD_GKEY, +// MOD_SKEY, +} modhint_t; + +typedef struct model_s +{ + char name[MAX_QPATH]; + qboolean needload; // bmodels and sprites don't cache normally + + modhint_t modhint; + + 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 + + int bspversion; //Diabolickal HLBSP + +} 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); + +mleaf_t *Mod_PointInLeaf (float *p, model_t *model); +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model); + +int GL_LoadTexture32 (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha); //Diabolickal HLBSP +void BuildGammaTable (float g); +int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap); //Diabolickal TGA + +#endif // __MODEL__ diff --git a/source/ctr/gl/gl_qmb.c b/source/ctr/gl/gl_qmb.c new file mode 100644 index 0000000..c7d04e3 --- /dev/null +++ b/source/ctr/gl/gl_qmb.c @@ -0,0 +1,3040 @@ +/* +Copyright (C) 2002-2003, Dr Labman, A. Nourai +Copyright (C) 2009, Crow_bar psp port + +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. + +*/ +// gl_rpart.c + +#include "../../quakedef.h" + +//#define DEFAULT_NUM_PARTICLES 8192 +#define ABSOLUTE_MIN_PARTICLES 64 +#define ABSOLUTE_MAX_PARTICLES 6144 + +extern int decal_blood1, decal_blood2, decal_blood3, decal_q3blood, decal_burn, decal_mark, decal_glow; + +typedef enum +{ + p_spark, + p_rayspark, + p_raysmoke, + p_smoke, + p_fire, + p_fire2, + p_bubble, + p_gunblast, + p_chunk, + p_shockwave, + p_inferno_flame, + p_inferno_trail, + p_sparkray, + p_staticbubble, + p_trailpart, + p_dpsmoke, + p_dpfire, + p_teleflare, + p_blood1, + p_blood2, + p_blood3, + p_bloodcloud, + p_flame, + p_lavatrail, + p_bubble2, + p_rain, + p_streak, + p_streaktrail, + p_streakwave, + p_lightningbeam, + p_glow, + p_alphatrail,//R00k + p_torch_flame, + p_flare, + p_dot, + p_muzzleflash, + p_muzzleflash2, + p_muzzleflash3, + p_q3flame, + num_particletypes +} part_type_t; + +typedef enum +{ + pm_static, + pm_normal, + pm_bounce, + pm_die, + pm_nophysics, + pm_float, + pm_rain, + pm_streak, + pm_streakwave, +} part_move_t; + +typedef enum +{ + ptex_none, + ptex_smoke, + ptex_generic, + ptex_dpsmoke, + ptex_blood1, + ptex_blood2, + ptex_blood3, + ptex_lightning, + ptex_flame, + ptex_muzzleflash, + ptex_muzzleflash2, + ptex_muzzleflash3, + ptex_bloodcloud, + ptex_q3flame, + num_particletextures +} part_tex_t; + +typedef enum +{ + pd_spark, + pd_sparkray, + pd_billboard, + pd_billboard_vel, + pd_hide, + pd_beam, + pd_q3flame, + pd_q3gunshot, + pd_q3teleport +} part_draw_t; + +typedef struct particle_type_s +{ + particle_t *start; + part_type_t id; + part_draw_t drawtype; + int SrcBlend; + int DstBlend; + part_tex_t texture; + float startalpha; + float grav; + float accel; + part_move_t move; + float custom; +} particle_type_t; + +#define MAX_PTEX_COMPONENTS 8 + +typedef struct particle_texture_s +{ + int texnum; + int components; + float coords[MAX_PTEX_COMPONENTS][4]; +} particle_texture_t; + +static float sint[7] = {0.000000, 0.781832, 0.974928, 0.433884, -0.433884, -0.974928, -0.781832}; +static float cost[7] = {1.000000, 0.623490, -0.222521, -0.900969, -0.900969, -0.222521, 0.623490}; + +static particle_t *particles, *free_particles, active_particles; +static particle_type_t particle_types[num_particletypes];//R00k +static int particle_type_index[num_particletypes]; +static particle_texture_t particle_textures[num_particletextures]; + +int lightning_texture;//Vult +float varray_vertex[16];//Vult +void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width);//Vult + +vec3_t NULLVEC = {0,0,0};//r00k + +static int r_numparticles; +static vec3_t zerodir = {22, 22, 22}; +static int particle_count = 0; +static float particle_time; +qboolean qmb_initialized = qfalse; +int particle_mode = 0; // 0: classic (default), 1: QMB, 2: mixed + +qboolean OnChange_gl_particle_count (cvar_t *var, char *string) +{ + float f; + + f = bound(ABSOLUTE_MIN_PARTICLES, (atoi(string)), ABSOLUTE_MAX_PARTICLES); + Cvar_SetValue("r_particle_count", f); + + QMB_ClearParticles (); // also re-allocc particles + + return qtrue; +} + +extern cvar_t cl_gun_offset; +cvar_t r_particle_count = {"r_particle_count", "1024", qtrue}; +cvar_t r_bounceparticles = {"r_bounceparticles", "1",qtrue}; +cvar_t r_decal_blood = {"r_decal_blood", "1",qtrue}; +cvar_t r_decal_bullets = {"r_decal_bullets","1",qtrue}; +cvar_t r_decal_sparks = {"r_decal_sparks","1",qtrue}; +cvar_t r_decal_explosions = {"r_decal_explosions","1",qtrue}; + +int decals_enabled; + +void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width); + +extern cvar_t sv_gravity; + +static byte *ColorForParticle (part_type_t type) +{ + static col_t color; + int lambda; + + switch (type) + { + case p_spark: + color[0] = 224 + (rand() & 31); + color[1] = 100 + (rand() & 31); + color[2] = 50; + break; + + case p_torch_flame: + case p_glow: + color[0] = color[1] = color[2] = 255; + break; + + case p_smoke: + color[0] = color[1] = color[2] = 128; + color[3] = 64; + break; + + case p_q3flame: + color[0] = color[1] = color[2] = 255; + break; + + case p_fire: + color[0] = 255; + color[1] = 122; + color[2] = 62; + break; + case p_fire2: + color[0] = 80; + color[1] = 80; + color[2] = 80; + color[3] = 64; + break; + + case p_bubble: + case p_bubble2: + case p_staticbubble: + color[0] = color[1] = color[2] = 192 + (rand() & 63); + break; + + case p_teleflare: + color[0] = color[1] = color[2] = 128 + (rand() & 127); + break; + + case p_gunblast: + color[0]= 224 + (rand() & 31); + color[1] = 170 + (rand() & 31); + color[2] = 0; + break; + + case p_chunk: + color[0] = color[1] = color[2] = (32 + (rand() & 127)); + break; + + case p_shockwave: + color[0] = color[1] = color[2] = 64 + (rand() & 31); + break; + + case p_inferno_flame: + case p_inferno_trail: + color[0] = 255; + color[1] = 77; + color[2] = 13; + break; + + case p_sparkray: + color[0] = 255; + color[1] = 102; + color[2] = 25; + break; + + case p_dpsmoke: + color[0] = color[1] = color[2] = 48 + (((rand() & 0xFF) * 48) >> 8); + break; + + case p_dpfire: + lambda = rand() & 0xFF; + color[0] = 160 + ((lambda * 48) >> 8); + color[1] = 16 + ((lambda * 148) >> 8); + color[2] = 16 + ((lambda * 16) >> 8); + break; + + case p_blood1: + case p_blood2: + color[0] = 30; + color[1] = 5; + color[2] = 5; + color[3] = 255; + break; + + case p_blood3: + color[0] = (50 + (rand() & 31)); + color[1] = color[2] = 0; + color[3] = 200; + break; + + case p_flame: + color[0] = 255; + color[1] = 100; + color[2] = 25; + color[3] = 128; + break; + + case p_lavatrail: + color[0] = 255; + color[1] = 102; + color[2] = 25; + color[3] = 255; + break; + + default: + //assert (!"ColorForParticle: unexpected type"); + break; + } + + return color; +} + + +#define ADD_PARTICLE_TEXTURE(_ptex, _texnum, _texindex, _components, _s1, _t1, _s2, _t2)\ +do { \ + particle_textures[_ptex].texnum = _texnum; \ + particle_textures[_ptex].components = _components; \ + particle_textures[_ptex].coords[_texindex][0] = (_s1 + 1) / max_s; \ + particle_textures[_ptex].coords[_texindex][1] = (_t1 + 1) / max_t; \ + particle_textures[_ptex].coords[_texindex][2] = (_s2 - 1) / max_s; \ + particle_textures[_ptex].coords[_texindex][3] = (_t2 - 1) / max_t; \ +} while(0) + +#define ADD_PARTICLE_TYPE(_id, _drawtype, _SrcBlend, _DstBlend, _texture, _startalpha, _grav, _accel, _move, _custom)\ +do {\ + particle_types[count].id = (_id);\ + particle_types[count].drawtype = (_drawtype);\ + particle_types[count].SrcBlend = (_SrcBlend);\ + particle_types[count].DstBlend = (_DstBlend);\ + particle_types[count].texture = (_texture);\ + particle_types[count].startalpha = (_startalpha);\ + particle_types[count].grav = 9.8 * (_grav);\ + particle_types[count].accel = (_accel);\ + particle_types[count].move = (_move);\ + particle_types[count].custom = (_custom);\ + particle_type_index[_id] = count;\ + count++;\ +} while(0) + +void QMB_AllocParticles (void) +{ + extern cvar_t r_particle_count; + + r_numparticles = bound(ABSOLUTE_MIN_PARTICLES, r_particle_count.value, ABSOLUTE_MAX_PARTICLES); + + //if (particles) + // Con_Printf("QMB_AllocParticles: internal error >particles<\n"); + + if (r_numparticles < 1) { + Con_Printf("QMB_AllocParticles: internal error >num particles<\n"); + } + + // can't alloc on Hunk, using native memory + particles = (particle_t *) malloc (r_numparticles * sizeof(particle_t)); +} + +void QMB_InitParticles (void) +{ + int i, j, ti, count = 0, particleimage; + float max_s, max_t; //For ADD_PARTICLE_TEXTURE + + particle_mode = pm_classic; + + Cvar_RegisterVariable (&r_bounceparticles); + Cvar_RegisterVariable (&r_flametype); + Cvar_RegisterVariable (&r_decal_blood); + Cvar_RegisterVariable (&r_decal_bullets); + Cvar_RegisterVariable (&r_decal_sparks); + Cvar_RegisterVariable (&r_decal_explosions); + Cvar_RegisterVariable (&r_particle_count); + + loading_num_step = loading_num_step + 24; + + if (!(particleimage = loadtextureimage("textures/particles/particlefont", 0, 0, qfalse, qtrue))) + { + //Clear_LoadingFill (); + return; + } + + loading_cur_step++; + strcpy(loading_name, "Particles"); + SCR_UpdateScreen (); + + max_s = max_t = 128.0; + + // LAST 4 PARAMS = START X, START Y, END X, END Y + ADD_PARTICLE_TEXTURE(ptex_none, 0, 0, 1, 0, 0, 0, 0); + ADD_PARTICLE_TEXTURE(ptex_blood1, particleimage, 0, 1, 0, 0, 64, 64); + ADD_PARTICLE_TEXTURE(ptex_blood2, particleimage, 0, 1, 64, 0, 128, 64); + ADD_PARTICLE_TEXTURE(ptex_generic, particleimage, 0, 1, 0, 64, 32, 96); + ADD_PARTICLE_TEXTURE(ptex_smoke, particleimage, 0, 1, 32, 64, 96, 128); + ADD_PARTICLE_TEXTURE(ptex_blood3, particleimage, 0, 1, 0, 96, 32, 128); + + for (i=0 ; i<8 ; i++) + ADD_PARTICLE_TEXTURE(ptex_dpsmoke, particleimage, i, 8, i * 32, 64, (i + 1) * 32, 96); + + loading_cur_step++; + SCR_UpdateScreen (); + + max_s = max_t = 128.0; + + if (!(particleimage = loadtextureimage("textures/particles/flame", 0, 0, qfalse, qtrue))) + { + //Clear_LoadingFill (); + return; + } + + ADD_PARTICLE_TEXTURE(ptex_q3flame, particleimage, 0, 1, 0, 0, 64, 64); + loading_cur_step++; + SCR_UpdateScreen (); + + max_s = max_t = 64.0; + + if (!(particleimage = loadtextureimage("textures/particles/inferno", 0, 0, qfalse, qtrue))) + { + //Clear_LoadingFill (); + return; + } + max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_flame, particleimage, 0, 1, 0, 0, 256, 256); + + loading_cur_step++; + SCR_UpdateScreen (); + + if (!(particleimage = loadtextureimage("textures/particles/zing1", 0, 0, qfalse, qtrue))) + { + //Clear_LoadingFill (); + return; + } + + max_s = 256.0; max_t = 128.0; + ADD_PARTICLE_TEXTURE(ptex_lightning, particleimage, 0, 1, 0, 0, 256, 128);//R00k changed + + loading_cur_step++; + SCR_UpdateScreen (); + max_s = max_t = 128.0; + + if (!(particleimage = loadtextureimage("textures/mzfl/mzfl0", 0, 0, qfalse, qtrue))) + { + //Clear_LoadingFill (); + return; + } + //max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_muzzleflash, particleimage, 0, 1, 0, 0, 128, 128); + + loading_cur_step++; + SCR_UpdateScreen (); + + if (!(particleimage = loadtextureimage("textures/mzfl/mzfl1", 0, 0, qfalse, qtrue))) + { + //Clear_LoadingFill (); + return; + } + //max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_muzzleflash2, particleimage, 0, 1, 0, 0, 128, 128); + + loading_cur_step++; + SCR_UpdateScreen (); + if (!(particleimage = loadtextureimage("textures/mzfl/mzfl2", 0, 0, qfalse, qtrue))) + { + //Clear_LoadingFill (); + return; + } + //max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_muzzleflash3, particleimage, 0, 1, 0, 0, 128, 128); + + loading_cur_step++; + SCR_UpdateScreen (); + + max_s = max_t = 64.0; + if (!(particleimage = loadtextureimage("textures/particles/bloodcloud", 0, 0, qfalse, qtrue))) + { + //Clear_LoadingFill (); + return; + } + //max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_bloodcloud, particleimage, 0, 1, 0, 0, 64, 64); + + loading_cur_step++; + SCR_UpdateScreen (); + + QMB_AllocParticles (); + + ADD_PARTICLE_TYPE(p_spark, pd_spark, GL_SRC_ALPHA, GL_ONE, ptex_none, 255, -8, 0, pm_normal, 1.3); + ADD_PARTICLE_TYPE(p_gunblast, pd_spark, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_none, 255, 0, 0, pm_normal, 1.3); + ADD_PARTICLE_TYPE(p_sparkray, pd_sparkray, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_none, 255, -0, 0, pm_nophysics, 0); + ADD_PARTICLE_TYPE(p_fire, pd_billboard, GL_SRC_ALPHA, GL_ONE, ptex_smoke, 204, 0, -2.95, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen (); + + ADD_PARTICLE_TYPE(p_fire2, pd_billboard, GL_SRC_ALPHA, GL_ONE, ptex_smoke, 204, 0, -2.95, pm_die, 0); + ADD_PARTICLE_TYPE(p_chunk, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, -16, 0, pm_bounce, 1.475); + ADD_PARTICLE_TYPE(p_shockwave, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, 0, -4.85, pm_nophysics, 0); + ADD_PARTICLE_TYPE(p_inferno_flame, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 153, 0, 0, pm_static, 0); + + loading_cur_step++; + SCR_UpdateScreen (); + + ADD_PARTICLE_TYPE(p_inferno_trail, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 204, 0, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_trailpart, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 230, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_smoke, pd_billboard, GL_SRC_ALPHA, GL_ONE, ptex_smoke, 140, 3, 0, pm_normal, 0); + ADD_PARTICLE_TYPE(p_raysmoke, pd_billboard, GL_SRC_ALPHA, GL_ONE, ptex_smoke, 140, 3, 0, pm_normal, 0); + ADD_PARTICLE_TYPE(p_dpfire, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_dpsmoke, 144, 0, 0, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen (); + + ADD_PARTICLE_TYPE(p_dpsmoke, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_dpsmoke, 85, 3, 0, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_dot, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_blood1, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR, ptex_blood1, 255, -20, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_blood2, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_blood3, 255, -45, 0, pm_normal, 0.018);//disisgonnabethegibchunks + ADD_PARTICLE_TYPE(p_blood3, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_blood3, 255, -30, 0, pm_normal, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_flame, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 200, 10, 0, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_lavatrail, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_dpsmoke, 255, 3, 0, pm_normal, 0);//R00k + ADD_PARTICLE_TYPE(p_glow, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 204, 0, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_alphatrail, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 100, 0, 0, pm_static, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_torch_flame, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_flame, 255, 12, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_streak, pd_hide, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_none, 255, -64, 0, pm_streak, 1.5); + ADD_PARTICLE_TYPE(p_streakwave, pd_hide, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_none, 255, 0, 0, pm_streakwave, 0); + ADD_PARTICLE_TYPE(p_streaktrail, pd_beam, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_none, 255, 0, 0, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_lightningbeam, pd_beam, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_lightning, 255, 0, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_muzzleflash, pd_billboard, GL_SRC_ALPHA, GL_ONE, ptex_muzzleflash, 255, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_muzzleflash2, pd_billboard, GL_SRC_ALPHA, GL_ONE, ptex_muzzleflash2, 255, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_muzzleflash3, pd_billboard, GL_SRC_ALPHA, GL_ONE, ptex_muzzleflash3, 255, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_rain, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, -16, 0, pm_rain, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + //shpuldeditedthis(GI_ONE_MINUS_DST_ALPHA->GL_ONE_MINUS_SRC_ALPHA) (edited one right after this comment) + ADD_PARTICLE_TYPE(p_bloodcloud, pd_billboard, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, ptex_bloodcloud, 255, -2, 0, pm_normal, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + //old: ADD_PARTICLE_TYPE(p_q3flame, pd_q3flame, GL_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, ptex_q3flame, 204, 0, 0, pm_static, -1); + ADD_PARTICLE_TYPE(p_q3flame, pd_billboard, GL_SRC_ALPHA, GL_ONE, ptex_q3flame, 180, 0.66, 0, pm_nophysics, 0); + + loading_cur_step++; + strcpy(loading_name, "particles"); + SCR_UpdateScreen (); + + Clear_LoadingFill (); + + qmb_initialized = qtrue; +} + +#define INIT_NEW_PARTICLE(_pt, _p, _color, _size, _time) \ + _p = free_particles; \ + free_particles = _p->next; \ + _p->next = _pt->start; \ + _pt->start = _p; \ + _p->size = _size; \ + _p->hit = 0; \ + _p->start = cl.time; \ + _p->die = _p->start + _time; \ + _p->growth = 0; \ + _p->rotspeed = 0; \ + _p->texindex = (rand() % particle_textures[_pt->texture].components); \ + _p->bounces = 0; \ + VectorCopy(_color, _p->color); + +__inline static void AddParticle (part_type_t type, vec3_t org, int count, float size, float time, col_t col, vec3_t dir) +{ + byte *color; + int i, j, k; + float tempSize; //stage; + particle_t *p; + particle_type_t *pt; + static unsigned long q3blood_texindex = 0; + + if (!qmb_initialized) + Sys_Error ("QMB particle added without initialization"); + + //assert (size > 0 && time > 0); + + if (type < 0 || type >= num_particletypes) { + Sys_Error ("AddParticle: Invalid type (%d)", type); + } + + pt = &particle_types[particle_type_index[type]]; + + for (i=0 ; i < count && free_particles ; i++) + { + + color = col ? col : ColorForParticle (type); + + INIT_NEW_PARTICLE(pt, p, color, size, time); + + switch (type) + { + case p_spark: + p->size = 1.175; + VectorCopy (org, p->org); + tempSize = size * 2; + p->vel[0] = (rand() % (int)tempSize) - ((int)tempSize / 2); + p->vel[1] = (rand() % (int)tempSize) - ((int)tempSize / 2); + p->vel[2] = (rand() % (int)tempSize) - ((int)tempSize / 2); + break; + case p_rayspark: + p->size = 1.175; + VectorCopy (org, p->org); + tempSize = size * 2; + p->vel[0] = (rand() % (int)tempSize) - ((int)tempSize/6); + p->vel[1] = (rand() % (int)tempSize) - ((int)tempSize/6); + p->vel[2] = /*(rand() % (int)tempSize) - (*/(int)tempSize; + break; + case p_raysmoke: + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + ((rand() & 31) - 16) / 2.0; + + p->vel[0] = ((rand() % 10)+2); + p->vel[1] = ((rand() % 10)+2); + p->vel[2] = ((rand() % 10)+2)*5; + p->growth = 7.5; + break; + case p_smoke: + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + ((rand() & 31) - 16) / 2.0; + for (j=0 ; j<3 ; j++) + p->vel[j] = ((rand() % 10) - 5) / 20.0; + p->growth = 4.5; + break; + case p_fire: + VectorCopy (org, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 159) - 80; + p->org[0] = org[0] + ((rand() & 63) - 32); + p->org[1] = org[1] + ((rand() & 63) - 32); + p->org[2] = org[2] + ((rand() & 63) - 10); + break; + + case p_fire2: + VectorCopy (org, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 199) - 100; + p->org[0] = org[0] + ((rand() & 99) - 50); + p->org[1] = org[1] + ((rand() & 99) - 50); + p->org[2] = org[2] + ((rand() & 99) - 18); + p->growth = 12; + break; + + case p_bubble: + p->start += (rand() & 15) / 36.0; + p->org[0] = org[0] + ((rand() & 31) - 16); + p->org[1] = org[1] + ((rand() & 31) - 16); + p->org[2] = org[2] + ((rand() & 63) - 32); + VectorClear (p->vel); + break; + + case p_streak: + case p_streakwave: + case p_shockwave: + VectorCopy (org, p->org); + VectorCopy (dir, p->vel); + break; + + case p_gunblast: + p->size = 1; + VectorCopy (org, p->org); + p->vel[0] = (rand() & 159) - 80; + p->vel[1] = (rand() & 159) - 80; + p->vel[2] = (rand() & 159) - 80; + break; + + case p_chunk: + VectorCopy (org, p->org); + p->vel[0] = (rand() % 40) - 20; + p->vel[1] = (rand() % 40) - 20; + p->vel[2] = (rand() % 40) - 5; + break; + + case p_rain: + VectorCopy(org, p->org); + p->vel[0] = (rand() % 180) - 90; + p->vel[1] = (rand() % 180) - 90; + p->vel[2] = (rand() % -100 - 1200); + break; + + case p_inferno_trail: + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + (rand() & 15) - 8; + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 3) - 2; + p->growth = -1.5; + break; + + case p_inferno_flame: + VectorCopy (org, p->org); + VectorClear (p->vel); + p->growth = -30; + break; + + case p_sparkray: + VectorCopy (org, p->endorg); + VectorCopy (dir, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 127) - 64; + p->growth = -16; + break; + + case p_bloodcloud: //shpuld + VectorCopy (org, p->org); + p->vel[0] = (rand() & 39) - 20; + p->vel[1] = (rand() & 39) - 20; + p->vel[2] = (rand() & 39) - 20; + p->growth = 24; + break; + + case p_staticbubble: + VectorCopy (org, p->org); + VectorClear (p->vel); + break; + + case p_muzzleflash: + case p_muzzleflash2: + case p_muzzleflash3: + + VectorCopy (org, p->org); + p->rotspeed = (rand() & 45) - 90; + //p->size = size * (rand() % 6) / 4;//r00k + p->size = size * (0.75 +((0.05 * (rand() % 20)) * 0.5));//blubs: resultant size range: [size * 0.75, size * 1.25) + break; + + case p_teleflare: + case p_flare: + VectorCopy (org, p->org); + VectorCopy (dir, p->vel); + p->growth = 1.75; + break; + + case p_blood1: + p->size = size * (rand() % 2) + 0.50;//r00k + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + (rand() & 15) - 8; + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 63) - 32; + break; + + case p_blood2: //shpuld + VectorCopy (org, p->org); + p->vel[0] = (rand() & 200) - 100; + p->vel[1] = (rand() & 200) - 100; + p->vel[2] = (rand() & 250) - 70; + //p->growth = 24; + break; + + case p_blood3: + p->size = size * (rand() % 20) / 5.0; + VectorCopy (org, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() % 40) - 20; + break; + + case p_flame: + VectorCopy (org, p->org); + p->growth = -p->size / 2; + VectorClear (p->vel); + for (j=0 ; j<2 ; j++) + p->vel[j] = (rand() % 6) - 3; + break; + + case p_q3flame: //shpuld + VectorCopy (org, p->org); + p->vel[0] = (rand() & 3) - 2; + p->vel[1] = (rand() & 3) - 2; + p->vel[2] = (rand() & 2); + p->growth = 6; + break; + + case p_torch_flame: + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + (rand() & 3) - 2; + p->vel[0] = rand() % 15 - 8; + p->vel[1] = rand() % 15 - 8; + p->vel[2] = rand() % 15; + p->rotspeed = (rand() & 31) + 32; + break; + + case p_dot: + case p_glow: + VectorCopy (org, p->org); + VectorCopy (dir, p->vel); + p->growth = -1.5; + break; + + case p_streaktrail: + case p_lightningbeam: + VectorCopy(org, p->org); + VectorCopy(dir, p->endorg); + VectorClear(p->vel); + p->growth = -p->size/time; + p->bounces = color[3]; + break; + + default: + //assert (!"AddParticle: unexpected type"); + break; + } + } +} + +__inline static void AddParticleTrail (part_type_t type, vec3_t start, vec3_t end, float size, float time, col_t col) +{ + byte *color; + int i, j, num_particles; + float count, length; + vec3_t point, delta; + particle_t *p; + particle_type_t *pt; + static float rotangle = 0; + count = 0; + + if (!qmb_initialized) + Sys_Error ("QMB particle added without initialization"); + + //assert (size > 0 && time > 0); + + if (type < 0 || type >= num_particletypes) + Sys_Error ("AddParticle: Invalid type (%d)", type); + + pt = &particle_types[particle_type_index[type]]; + + VectorCopy(start, point); + VectorSubtract(end, start, delta); + if (!(length = VectorLength(delta))) + return; + + switch (type) + { + case p_alphatrail: + case p_trailpart: + case p_lavatrail: + count = length / 1.1; + break; + + case p_blood3: + count = length / 8; + break; + + case p_bubble: + case p_bubble2: + count = length / 5.0; + break; + + case p_smoke: + count = length / 3.8; + break; + + case p_dpsmoke: + count = length / 2.5; + break; + + case p_dpfire: + count = length / 2.8; + break; + + default: + //assert (!"AddParticleTrail: unexpected type"); + break; + } + + if (!(num_particles = (int)count)) + num_particles = 1; + + VectorScale(delta, 1.0 / num_particles, delta); + + for (i=0 ; i < num_particles && free_particles ; i++) + { + color = col ? col : ColorForParticle (type); + INIT_NEW_PARTICLE(pt, p, color, size, time); + + switch (type) + { + case p_alphatrail: + case p_trailpart: + VectorCopy (point, p->org); + VectorClear (p->vel); + p->growth = -size / time; + break; + + case p_blood3: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 15) - 8) / 8.0; + for (j=0 ; j<3 ; j++) + p->vel[j] = ((rand() & 15) - 8) / 2.0; + p->size = size * (rand() % 20) / 10.0; + p->growth = 6; + break; + + case p_bubble2: + VectorCopy(point, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() % 10) - 5; + break; + + //R00k added + case p_bubble: + VectorCopy (point, p->org); + + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 15) - 8) / 8.0; + + for (j=0 ; j<3 ; j++) + p->vel[j] = ((rand() & 15) - 8) / 2.0; + + p->size = size * (rand() % 20) / 10.0; + p->growth = 1; + break; + + case p_smoke: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 7) - 4) / 8.0; + p->vel[0] = p->vel[1] = 0; + p->vel[2] = rand() & 3; + p->growth = 4.5; + p->rotspeed = (rand() & 63) + 96; + break; + + case p_dpsmoke: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() % 10) - 5; + p->growth = 3; + p->rotspeed = (rand() & 63) + 96; + break; + + case p_dpfire: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() % 40) - 20; + break; + + case p_lavatrail: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 7) - 4); + p->vel[0] = p->vel[1] = 0; + p->vel[2] = rand() & 3; + break; + + default: + //assert (!"AddParticleTrail: unexpected type"); + break; + } + + VectorAdd(point, delta, point); + } +} + +void QMB_ClearParticles (void) +{ + int i; + + if (!qmb_initialized) + return; + + free (particles); // free + QMB_AllocParticles (); // and alloc again + particle_count = 0; + memset (particles, 0, r_numparticles * sizeof(particle_t)); + free_particles = &particles[0]; + + for (i=0 ; i+1 < r_numparticles ; i++) + particles[i].next = &particles[i + 1]; + particles[r_numparticles-1].next = NULL; + + for (i=0 ; i < num_particletypes ; i++) + particle_types[i].start = NULL; +} + +inline static void QMB_UpdateParticles(void) +{ + int i, c; + float grav, bounce, frametime, distance[3]; + vec3_t oldorg, stop, normal; + particle_type_t *pt; + particle_t *p, *kill; + + if (!qmb_initialized) + return; + + particle_count = 0; + frametime = fabs(cl.ctime - cl.oldtime); + grav = sv_gravity.value / 800.0; + + for (i=0 ; istart) + { + p = pt->start; + while (p && p->next) + { + kill = p->next; + if (kill->die <= particle_time) + { + p->next = kill->next; + kill->next = free_particles; + free_particles = kill; + } + else + { + p = p->next; + } + } + if (pt->start->die <= particle_time) + { + kill = pt->start; + pt->start = kill->next; + kill->next = free_particles; + free_particles = kill; + } + } + + for (p = pt->start ; (p); p = p->next) + { + if (particle_time < p->start) + continue; + + particle_count++; + + p->size += p->growth * frametime; + + if (p->size <= 0) + { + p->die = 0; + continue; + } + VectorCopy (p->org, oldorg); + VectorSubtract(r_refdef.vieworg,oldorg, distance); + + if (VectorLength(distance) >= r_farclip.value) + p->die = 0; + + switch (pt->id) + { + case p_streaktrail://R00k + case p_lightningbeam: + p->color[3] = p->bounces * ((p->die - particle_time) / (p->die - p->start)); + break; + + //shpuld + case p_q3flame: + p->color[3] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start)); + p->color[0] = p->color[1] = p->color[2] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start)); + break; + + default: + p->color[3] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start)); + break; + } + + p->rotangle += p->rotspeed * frametime; + + if (p->hit) + continue; + + p->vel[2] += pt->grav * grav * frametime; + + VectorScale (p->vel, 1 + pt->accel * frametime, p->vel); + + switch (pt->move) + { + case pm_static: + break; + + case pm_normal: + VectorCopy (p->org, oldorg); + VectorMA (p->org, frametime, p->vel, p->org); + + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + p->hit = 1; + + if ((pt->id == p_blood3)&&(r_decal_blood.value) && (decals_enabled) && (particle_mode)) + { + TraceLineN(oldorg, p->org, stop, normal); + + if ((stop != p->org)&&(VectorLength(stop)!=0)) + { + vec3_t tangent; + VectorCopy(stop, p->org); + VectorCopy(normal, p->vel); + CrossProduct(normal,p->vel,tangent); + #if 0 // naievil -- fixme + R_SpawnDecal(p->org, normal, tangent, decal_blood3, 12, 0); + #endif + } + p->die = 0; + } + VectorCopy (oldorg, p->org); + VectorClear (p->vel); + } + + break; + + case pm_float: + VectorMA (p->org, frametime, p->vel, p->org); + p->org[2] += p->size + 1; + if (!ISUNDERWATER(TruePointContents(p->org))) + p->die = 0; + p->org[2] -= p->size + 1; + break; + + case pm_nophysics: + VectorMA (p->org, frametime, p->vel, p->org); + break; + + case pm_die: + VectorCopy (p->org, oldorg); + VectorMA (p->org, frametime, p->vel, p->org); + + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + if ((decals_enabled) && (particle_mode)) + { + TraceLineN(oldorg, p->org, stop, normal); + + if ((stop != p->org)&&(VectorLength(stop)!=0)) + { + vec3_t tangent; + + VectorCopy(stop, p->org); + VectorCopy(normal, p->vel); + CrossProduct(normal,p->vel,tangent); +/* + if ((pt->id == p_blood1)&&(r_decal_blood.value)) + { + R_SpawnDecal(p->org, normal, tangent, decal_blood1, 12, 0); + } + else + { + if ((pt->id == p_blood2)&&(r_decal_blood.value)) + { + R_SpawnDecal(p->org, normal, tangent, decal_blood2, 12, 0); + } + } +*/ + #if 0// naievil -- fixme + if ((pt->id == p_fire || pt->id == p_dpfire) && r_decal_explosions.value) + R_SpawnDecal (p->org, normal, tangent, decal_burn, 32, 0); + else if (pt->id == p_blood1 && r_decal_blood.value) + R_SpawnDecal (p->org, normal, tangent, decal_blood1, 12, 0); + else if (pt->id == p_blood2 && r_decal_blood.value) + R_SpawnDecal (p->org, normal, tangent, decal_blood2, 12, 0); + else if (pt->id == p_q3blood_trail && r_decal_blood.value) + R_SpawnDecal (p->org, normal, tangent, decal_q3blood, 48, 0); + #endif + + } + } + VectorCopy (oldorg, p->org); + VectorClear (p->vel);//R00k added Needed? + p->die = 0; + } + + break; + + case pm_bounce: + if (!r_bounceparticles.value || p->bounces) + { + VectorMA(p->org, frametime, p->vel, p->org); + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + p->die = 0; + } + } + else + { + VectorCopy (p->org, oldorg); + VectorMA (p->org, frametime, p->vel, p->org); + + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + if (TraceLineN(oldorg, p->org, stop, normal)) + { + VectorCopy (stop, p->org); + bounce = -pt->custom * DotProduct(p->vel, normal); + VectorMA(p->vel, bounce, normal, p->vel); + p->bounces++; + } + } + + } + break; + + case pm_streak: + VectorCopy(p->org, oldorg); + VectorMA(p->org, frametime, p->vel, p->org); + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + if (TraceLineN(oldorg, p->org, stop, normal)) + { + VectorCopy(stop, p->org); + bounce = -pt->custom * DotProduct(p->vel, normal); + VectorMA(p->vel, bounce, normal, p->vel); + } + } + + AddParticle (p_streaktrail, oldorg, 1, p->size, 0.2, p->color, p->org); + + if (!VectorLength(p->vel)) + p->die = 0; + break; + + case pm_rain: + VectorCopy(p->org, oldorg); + VectorMA(p->org, frametime, p->vel, p->org); + + VectorSubtract(r_refdef.vieworg,oldorg, distance); + + if (VectorLength(distance) < r_farclip.value) + { + if ((rand()%10+1 > 6)) + AddParticle (p_streaktrail, oldorg, 1, ((rand() % 1) + 0.5), 0.2, p->color, p->org); + + c = TruePointContents(p->org); + + if ((CONTENTS_SOLID == c) || (ISUNDERWATER(c))) + { + VectorClear (p->vel); + p->die = 0; + } + } + break; + + case pm_streakwave: + VectorCopy(p->org, oldorg); + VectorMA(p->org, frametime, p->vel, p->org); + AddParticle (p_streaktrail, oldorg, 1, p->size, 0.5, p->color, p->org); + p->vel[0] = 19 * p->vel[0] / 20; + p->vel[1] = 19 * p->vel[1] / 20; + p->vel[2] = 19 * p->vel[2] / 20; + break; + + default: + //assert (!"QMB_UpdateParticles: unexpected pt->move"); + break; + } + } + } +} + +//from darkplaces engine - finds which corner of a particle goes where, so I don't have to :D +void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width) +{ + vec3_t right1, right2, diff, normal; + + VectorSubtract (org2, org1, normal); + VectorNormalize (normal); + + //width = width / 2; + // calculate 'right' vector for start + VectorSubtract (r_origin, org1, diff); + VectorNormalize (diff); + CrossProduct (normal, diff, right1); + + // calculate 'right' vector for end + VectorSubtract (r_origin, org2, diff); + VectorNormalize (diff); + CrossProduct (normal, diff, right2); + + vert[ 0] = org1[0] + width * right1[0]; + vert[ 1] = org1[1] + width * right1[1]; + vert[ 2] = org1[2] + width * right1[2]; + vert[ 4] = org1[0] - width * right1[0]; + vert[ 5] = org1[1] - width * right1[1]; + vert[ 6] = org1[2] - width * right1[2]; + vert[ 8] = org2[0] - width * right2[0]; + vert[ 9] = org2[1] - width * right2[1]; + vert[10] = org2[2] - width * right2[2]; + vert[12] = org2[0] + width * right2[0]; + vert[13] = org2[1] + width * right2[1]; + vert[14] = org2[2] + width * right2[2]; +} + +// naievil -- hacky particle drawing...NOT OPTIMIZED -- from NX +void DRAW_PARTICLE_BILLBOARD(particle_texture_t *ptex, particle_t *p, vec3_t *coord) { + float scale; + vec3_t up, right, p_downleft, p_upleft, p_downright, p_upright; + GLubyte color[4], *c; + + VectorScale (vup, 1.5, up); + VectorScale (vright, 1.5, right); + + glEnable (GL_BLEND); + glDepthMask (GL_FALSE); + glBegin (GL_QUADS); + + scale = p->size; + color[0] = p->color[0]; + color[1] = p->color[1]; + color[2] = p->color[2]; + color[3] = p->color[3]; + glColor4ubv(color); + + float subTexLeft = ptex->coords[p->texindex][0]; + float subTexTop = ptex->coords[p->texindex][1]; + float subTexRight = ptex->coords[p->texindex][2]; + float subTexBottom = ptex->coords[p->texindex][3]; + + glTexCoord2f(subTexLeft, subTexTop); + VectorMA(p->org, -scale * 0.5, up, p_downleft); + VectorMA(p_downleft, -scale * 0.5, right, p_downleft); + glVertex3fv (p_downleft); + + glTexCoord2f(subTexRight, subTexTop); + VectorMA (p_downleft, scale, up, p_upleft); + glVertex3fv (p_upleft); + + glTexCoord2f(subTexRight, subTexBottom); + VectorMA (p_upleft, scale, right, p_upright); + glVertex3fv (p_upright); + + glTexCoord2f(subTexLeft, subTexBottom); + VectorMA (p_downleft, scale, right, p_downright); + glVertex3fv (p_downright); + + glEnd (); + + + glDepthMask (GL_TRUE); + glDisable (GL_BLEND); + glColor3f(1,1,1); +} + +void QMB_DrawParticles (void) +{ + int j, i; + vec3_t up, right, billboard[4], velcoord[4], neworg; + particle_t *p; + particle_type_t *pt; + particle_texture_t *ptex; + + float varray_vertex[16]; + vec3_t distance; + + if (!qmb_initialized) + return; + + particle_time = cl.time; + + if (!cl.paused) + QMB_UpdateParticles (); + + VectorAdd (vup, vright, billboard[2]); + VectorSubtract (vright, vup, billboard[3]); + VectorNegate (billboard[2], billboard[0]); + VectorNegate (billboard[3], billboard[1]); + + //glDepthMask (GL_TRUE); + glEnable (GL_BLEND); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glShadeModel (GL_SMOOTH); + + for (i = 0 ; i < num_particletypes ; i++) + { + pt = &particle_types[i]; + + if (!pt->start) { + //Con_Printf("Particle type %d (want %d) does not have start\n", pt->drawtype, pd_billboard); + continue; + } + + glBlendFunc (pt->SrcBlend, pt->DstBlend); + + switch (pt->drawtype) + { + /*case pd_hide: + break; + case pd_beam: + ptex = &particle_textures[pt->texture]; + GL_Bind (ptex->texnum); + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + VectorSubtract(r_refdef.vieworg, p->org, distance); + if (VectorLength(distance) > r_farclip.value) + continue; + // Allocate the vertices. + struct vertex + { + float u, v; + float x, y, z; + }; + + struct vertex* const out = (struct vertex*)(malloc(sizeof(struct vertex) * 4)); + + glColor4f(p->color[0]/255, p->color[1]/255, p->color[2]/255, p->color[3]/255); + R_CalcBeamVerts (varray_vertex, p->org, p->endorg, p->size / 3.0); + + out[0].u = 1; + out[0].v = 0; + + out[0].x = varray_vertex[0]; + out[0].y = varray_vertex[1]; + out[0].z = varray_vertex[2]; + + out[1].u = 1; + out[1].v = 1; + + out[1].x = varray_vertex[4]; + out[1].y = varray_vertex[5]; + out[1].z = varray_vertex[6]; + + out[2].u = 0; + out[2].v = 1; + + out[2].x = varray_vertex[8]; + out[2].y = varray_vertex[9]; + out[2].z = varray_vertex[10]; + + out[3].u = 0; + out[3].v = 0; + + out[3].x = varray_vertex[12]; + out[3].y = varray_vertex[13]; + out[3].z = varray_vertex[14]; + + glBegin (GL_TRIANGLE_FAN); + glVertex4fv (out); + glEnd (); + glColor4f(1,1,1,1); //return to normal color + } + break; + case pd_spark: + glDisable (GL_TEXTURE_2D); + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + VectorSubtract(r_refdef.vieworg, p->org, distance); + if (VectorLength(distance) > r_farclip.value) + continue; + + struct vertex + { + vec3_t xyz; + }; + + struct vertex* const out = (struct vertex*)(malloc(sizeof(struct vertex) * 9)); + + glColor4f(p->color[0]/255, p->color[1]/255, p->color[2]/255, p->color[3]/255); + + for (int gh=0 ; gh<3 ; gh++) + out[0].xyz[gh] = p->org[gh]; + + glColor4f((p->color[0] >> 1)/255, (p->color[1] >> 1)/255, (p->color[2] >> 1)/255, (p->color[3] >> 1)/255); + + int vt = 1; + + for (j=7; j>=0 ; j--) + { + + for (int k=0 ; k<3 ; k++) + out[vt].xyz[k] = p->org[k] - p->vel[k] / 8 + vright[k] * cost[1%7] * p->size + vup[k] * sint[j%7] * p->size; + vt = vt + 1; + } + + glBegin (GL_TRIANGLE_FAN); + glVertex4fv (out); + glEnd (); + glColor4f(1,1,1,1); //return to normal color + } + glEnable (GL_TEXTURE_2D); + break; + case pd_sparkray: + glDisable (GL_TEXTURE_2D); + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + VectorSubtract(r_refdef.vieworg, p->org, distance); + if (VectorLength(distance) > r_farclip.value) + continue; + + if (!TraceLineN(p->endorg, p->org, neworg, NULLVEC)) + VectorCopy(p->org, neworg); + + //R00k added -start- + //glEnable (GL_BLEND); + //p->color[3] = bound(0, 0.3, 1) * 255; + //R00k added -end- + //glColor4ubv (p->color); + + struct vertex + { + vec3_t xyz; + }; + + struct vertex* const out = (struct vertex*)(malloc(sizeof(struct vertex) * 9)); + + glColor4f(p->color[0]/255, p->color[1]/255, p->color[2]/255, p->color[3]/255); + + for (int gh=0 ; gh<3 ; gh++) + out[0].xyz[gh] = p->endorg[gh]; + + + glColor4f((p->color[0] >> 1)/255, (p->color[1] >> 1)/255, (p->color[2] >> 1)/255, (p->color[3] >> 1)/255); + + int vt = 1; + + for (j=7 ; j>=0 ; j--) + { + for (int k=0 ; k<3 ; k++) + out[vt].xyz[k] = neworg[k] + vright[k] * cost[j%7] * p->size + vup[k] * sint[j%7] * p->size; + + vt = vt + 1; + } + glBegin (GL_TRIANGLE_FAN); + glVertex4fv (out); + glEnd (); + glColor4f(1,1,1,1); //return to normal color + + } + glEnable (GL_TEXTURE_2D); + break;*/ + case pd_billboard: + ptex = &particle_textures[pt->texture]; + GL_Bind (ptex->texnum); + + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + for (j = 0 ; j < cl.maxclients ; j++) + { + if (pt->custom != -1 && VectorSupCompare(p->org, cl_entities[1+j].origin, 40)) + { + p->die = 0; + continue; + } + } + + if(pt->texture == ptex_muzzleflash || pt->texture == ptex_muzzleflash2 || pt->texture == ptex_muzzleflash3) + glDepthRange (0, 0.3); + + DRAW_PARTICLE_BILLBOARD(ptex, p, billboard); + + if(pt->texture == ptex_muzzleflash || pt->texture == ptex_muzzleflash2 || pt->texture == ptex_muzzleflash3) + glDepthRange(0, 1); + } + break; + + case pd_billboard_vel: + ptex = &particle_textures[pt->texture]; + GL_Bind (ptex->texnum); + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + VectorCopy (p->vel, up); + CrossProduct (vpn, up, right); + VectorNormalizeFast (right); + VectorScale (up, pt->custom, up); + + VectorAdd (up, right, velcoord[2]); + VectorSubtract (right, up, velcoord[3]); + VectorNegate (velcoord[2], velcoord[0]); + VectorNegate (velcoord[3], velcoord[1]); + DRAW_PARTICLE_BILLBOARD(ptex, p, velcoord); + } + break; + + /*case pd_q3flame: + ptex = &particle_textures[pt->texture]; + GL_Bind (ptex->texnum); + for (p = pt->start ; p ; p = p->next) + { + float varray_vertex[16]; + float xhalf = p->size / 2.0, yhalf = p->size; + // vec3_t org, v, end, normal; + + if (particle_time < p->start || particle_time >= p->die) + continue; + + glDisable (GL_CULL_FACE); + + for (j=0 ; j<2 ; j++) + { + glPushMatrix (); + + glTranslatef(p->org[0], p->org[1], p->org[2]); + + //glRotatef (!j ? 45 : -45, 0, 0, 1); + + // naievil -- I don't know the equivalent of this + //sceGumRotateZ(!j ? 45 : -45 * (M_PI / 180.0f)); + + glColor4f(p->color[0]/255, p->color[1]/255, p->color[2]/255, p->color[3]/255); + + // sigh. The best would be if the flames were always orthogonal to their surfaces + // but I'm afraid it's impossible to get that work (w/o progs modification of course) + varray_vertex[0] = 0; + varray_vertex[1] = xhalf; + varray_vertex[2] = -yhalf; + varray_vertex[4] = 0; + varray_vertex[5] = xhalf; + varray_vertex[6] = yhalf; + varray_vertex[8] = 0; + varray_vertex[9] = -xhalf; + varray_vertex[10] = yhalf; + varray_vertex[12] = 0; + varray_vertex[13] = -xhalf; + varray_vertex[14] = -yhalf; + + struct vertex + { + float u, v; + float x, y, z; + }; + + struct vertex* const out = (struct vertex*)(malloc(sizeof(struct vertex) * 4)); + + out[0].u = ptex->coords[p->texindex][0]; + out[0].v = ptex->coords[p->texindex][3]; + out[0].x = varray_vertex[0]; + out[0].y = varray_vertex[1]; + out[0].z = varray_vertex[2]; + + + out[1].u = ptex->coords[p->texindex][0]; + out[1].v = ptex->coords[p->texindex][1]; + out[1].x = varray_vertex[4]; + out[1].y = varray_vertex[5]; + out[1].z = varray_vertex[6]; + + + out[2].u = ptex->coords[p->texindex][2]; + out[2].v = ptex->coords[p->texindex][1]; + out[2].x = varray_vertex[8]; + out[2].y = varray_vertex[9]; + out[2].z = varray_vertex[10]; + + + out[3].u = ptex->coords[p->texindex][2]; + out[3].v = ptex->coords[p->texindex][3]; + out[3].x = varray_vertex[12]; + out[3].y = varray_vertex[13]; + out[3].z = varray_vertex[14]; + + glBegin (GL_TRIANGLE_FAN); + glVertex4fv (out); + glEnd (); + glPopMatrix (); + } + glEnable (GL_CULL_FACE); + glColor4f(1,1,1,1); //return to normal color + } + break; + + case pd_q3gunshot: + for (p = pt->start ; p ; p = p->next) + QMB_Q3Gunshot (p->org, (int)p->texindex, (float)p->color[3] / 255.0); + break; + + case pd_q3teleport: + for (p = pt->start ; p ; p = p->next) + QMB_Q3Teleport (p->org, (float)p->color[3] / 255.0); + break;*/ + default: + //assert (!"QMB_DrawParticles: unexpected drawtype"); + break; + } + } + + //glDepthMask (GL_FALSE); + glDisable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glShadeModel (GL_SMOOTH); +} + +void QMB_Shockwave_Splash(vec3_t org, int radius) +{ + float theta; + vec3_t angle; + + angle[2] = 0; + + for (theta = 0; theta < 6.283185307179586476925286766559; theta += 0.069813170079773183076947630739545) + { + angle[0] = cos(theta) * radius; + angle[1] = sin(theta) * radius; + AddParticle(p_shockwave, org, 1, 2, 0.625f, NULL, angle); + } +} + +extern sfx_t *cl_sfx_thunder; + +//R00k: revamped to coincide with classic particle style... + +void QMB_ParticleExplosion (vec3_t org) +{ + if (r_explosiontype.value == 2)//no explosion what so ever + return; + + if (ISUNDERWATER(TruePointContents(org))) + { + AddParticle (p_bubble, org, 6, 3.0, 2.5, NULL, zerodir); + AddParticle (p_bubble, org, 4, 2.35, 2.5, NULL, zerodir); + + AddParticle (p_fire, org, 16, 120, 1, NULL, zerodir); + if (r_explosiontype.value != 1) + { + AddParticle (p_spark, org, 50, 250, 0.925f, NULL, zerodir); + AddParticle (p_spark, org, 25, 150, 0.925f, NULL, zerodir); + } + } + else + + { + /* original + if (r_explosiontype.value != 3) + { + if (r_flametype.value < 1)//R00k + { + AddParticle (p_fire2, org, 16, 18, 1, NULL, zerodir); + } + else + { + AddParticle (p_fire, org, 16, 18, 1, NULL, zerodir); + } + } + + if ((r_explosiontype.value == 0) || (r_explosiontype.value == 1) || (r_explosiontype.value == 3)) + { + AddParticle (p_spark, org, 50, 250, 0.925f, NULL, zerodir); + AddParticle (p_spark, org, 25, 150, 0.925f, NULL, zerodir); + } + */ + + //shpuld + AddParticle (p_fire, org, 10, 40, 0.5, NULL, zerodir); + AddParticle (p_fire2, org, 14, 36, 1.8, NULL, zerodir); + } +} + +void d8to24col (col_t colourv, int colour) +{ + byte *colourByte; + + colourByte = (byte *)&d_8to24table[colour]; + colourv[0] = colourByte[0]; + colourv[1] = colourByte[1]; + colourv[2] = colourByte[2]; +} + +__inline static void AddColoredParticle (part_type_t type, vec3_t org, int count, float size, float time, int colorStart, int colorLength, vec3_t dir) +{ + col_t color; + int i, j, colorMod = 0; + float tempSize; + particle_t *p; + particle_type_t *pt; + + + if (!qmb_initialized) + Sys_Error ("QMB particle added without initialization"); + + //assert (size > 0 && time > 0); + + if (type < 0 || type >= num_particletypes) + Sys_Error ("AddColoredParticle: Invalid type (%d)", type); + + pt = &particle_types[particle_type_index[type]]; + + for (i=0 ; i < count && free_particles ; i++) + { + d8to24col (color, colorStart + (colorMod % colorLength)); + colorMod++; + INIT_NEW_PARTICLE(pt, p, color, size, time); + + switch (type) + { + case p_spark: + p->size = 1.175; + VectorCopy (org, p->org); + tempSize = size * 2; + p->vel[0] = (rand() % (int)tempSize) - ((int)tempSize / 4); + p->vel[1] = (rand() % (int)tempSize) - ((int)tempSize / 4); + p->vel[2] = (rand() % (int)tempSize) - ((int)tempSize / 6); + break; + + case p_fire: + VectorCopy (org, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = ((rand() % 160) - 80) * (size / 25.0); + break; + + default: + //assert (!"AddColoredParticle: unexpected type"); + break; + } + } +} + +void QMB_ColorMappedExplosion (vec3_t org, int colorStart, int colorLength) +{ + + if (ISUNDERWATER(TruePointContents(org))) + { + //AddColoredParticle (p_fire, org, 16, 18, 1, colorStart, colorLength, zerodir); + AddParticle (p_bubble, org, 6, 3.0, 2.5, NULL, zerodir); + AddParticle (p_bubble, org, 4, 2.35, 2.5, NULL, zerodir); + if (r_explosiontype.value != 2) + { + AddColoredParticle (p_spark, org, 50, 100, 0.5, colorStart, colorLength, zerodir); + AddColoredParticle (p_spark, org, 25, 60, 0.5, colorStart, colorLength, zerodir); + } + } + else + { + if (r_flametype.value < 1)//R00k + { + AddColoredParticle (p_fire2, org, 16, 18, 1, colorStart, colorLength, zerodir); + } + else + { + AddColoredParticle (p_fire, org, 16, 18, 1, colorStart, colorLength, zerodir); + } + + if (r_explosiontype.value < 2) + { + AddColoredParticle (p_spark, org, 50, 250, 0.625f, colorStart, colorLength, zerodir); + if (r_explosiontype.value < 1) + AddColoredParticle (p_spark, org, 25, 150, 0.625f, colorStart, colorLength, zerodir); + } + } +} + +/* Original + +void QMB_Blood_Splat(part_type_t type, vec3_t org) //R00k :) +{ + int j; + col_t color; + vec3_t neworg, angle; + + VectorClear (angle); + + color[0]=100; + color[1]=0; + color[2]=0; + color[3]=255; + + AddParticle(p_bloodcloud, org, 1, 6, 0.5, color, zerodir); + + for (j=0 ; j<4 ; j++) + { + AngleVectors (angle, NULLVEC, NULLVEC, neworg); + VectorMA (org, 70, neworg, neworg); + + AddParticle (type, org, 5, 1, 2, color, neworg); + angle[1] += 360 / 4; + } +}*/ + +void QMB_Blood_Splat(part_type_t type, vec3_t org) //Shpuldified +{ + int j; + col_t color; + vec3_t neworg, angle; + + VectorClear (angle); + + color[0]=100; + color[1]=0; + color[2]=0; + color[3]=255; + + if(type == p_blood1) + { + AddParticle(p_bloodcloud, org, 3, 5, 0.3, color, zerodir); + } + + else if(type == p_blood2) + { + AddParticle(p_bloodcloud, org, 3, 7, 0.3, color, zerodir); + color[0] = 40; + AddParticle(p_blood2, org, 16, 3, 1.0, color, zerodir); + } + + else //p_blood3, trail? + { + AddParticle(p_bloodcloud, org, 3, 5, 0.6, color, zerodir); + for (j=0 ; j<4 ; j++) + { + AngleVectors (angle, NULLVEC, NULLVEC, neworg); + VectorMA (org, 70, neworg, neworg); + + AddParticle (type, org, 5, 1, 2, color, neworg); + angle[1] += 360 / 4; + } + } +} + +void QMB_RunParticleEffect (vec3_t org, vec3_t dir, int col, int count) +{ + col_t color; + vec3_t neworg, newdir; + int i, j, particlecount; + int contents;//R00k Added + + if (col == 73) + { + QMB_Blood_Splat(p_blood1, org); + return; + } + else if (col == 225) + { + QMB_Blood_Splat(p_blood2, org); + return; + } + else if (col == 20 && count == 30) + { + color[0] = color[2] = 51; + color[1] = 255; + AddParticle (p_chunk, org, 1, 1, 0.75, color, zerodir); + AddParticle (p_spark, org, 12, 75, 0.4, color, zerodir); + return; + } + else if (col == 226 && count == 20) + { + color[0] = 230; + color[1] = 204; + color[2] = 26; + AddParticle (p_chunk, org, 1, 1, 0.75, color, zerodir); + AddParticle (p_spark, org, 12, 75, 0.4, color, zerodir); + return; + } + else if(col == 111) //we will use this color for flames + { + color[0] = color[1] = color[2] = 255; + AddParticle (p_q3flame, org, 3, 3, 2, color, dir); + return; + } + else if(col == 112) //we will use this color for big flames + { + color[0] = color[1] = color[2] = 255; + AddParticle (p_q3flame, org, 3, 6, 2, color, dir); + return; + } + + switch (count) + { + case 9: + case 10://nailgun + { + color[0] = 200; color[1] = 200; color[2] = 125; + + AddParticle (p_spark, org, 6, 70, 0.6, NULL, zerodir); + + AddParticle (p_chunk, org, 3, 1, 0.75, NULL, zerodir); + + contents = TruePointContents (org);//R00k Added + + if (ISUNDERWATER(contents))//R00k + { + AddParticle (p_bubble, org, 1, 2, 0.825f + ((rand() % 10) - 5) / 40.0, NULL, zerodir); + } + else + + { + AddParticle (p_smoke, org, 1, 4, 0.825f + ((rand() % 10) - 5) / 40.0, NULL, zerodir); + } + } + break; + + case 20://super nailgun + color[0] = 200; color[1] = 200; color[2] = 125; + + AddParticle (p_spark, org, 22, 100, 0.2, NULL, zerodir); + + //AddParticle (p_chunk, org, 6, 2, 0.75, NULL, zerodir); + + contents = TruePointContents (org);//R00k Added + + if (ISUNDERWATER(contents))//R00k + { + AddParticle (p_bubble, org, 1, 2, 0.825f + ((rand() % 10) - 5) / 40.0, NULL, zerodir); + } + else + + { + AddParticle (p_smoke, org, 3, 12, 1.225f + ((rand() % 10) - 5) / 40.0, NULL, zerodir); + } + break; + + case 24:// gunshot + particlecount = count >> 1; + AddParticle (p_gunblast, org, 1, 1.04, 0.2, NULL, zerodir); + for (i=0 ; i>3) ; i++) + { + for (j=0 ; j<3 ; j++) + neworg[j] = org[j] + ((rand() % 24) - 12); + newdir[0] = dir[0] * (10 + (rand() % 5)); + newdir[1] = dir[1] * (10 + (rand() % 5)); + newdir[2] = dir[2] * 15; + d8to24col (color, (col & ~7) + (rand() & 7)); + AddParticle (p_glow, neworg, 1, 3.5, 0.5 + 0.1 * (rand() % 3), color, newdir); + } + return; + } + } +*/ + particlecount = fmax(1, count>>1); + for (i=0 ; iv.Flash_Size; + + if(size == 0 || cl.stats[STAT_ZOOM] == 2) + return; + + switch(rand() % 3 + 1) + { + case 1: + AddParticle (p_muzzleflash, org, 1, size, timemod * frametime, color, zerodir); + break; + case 2: + AddParticle (p_muzzleflash2, org, 1, size, timemod * frametime, color, zerodir); + break; + case 3: + AddParticle (p_muzzleflash3, org, 1, size, timemod * frametime, color, zerodir); + break; + default: + AddParticle (p_muzzleflash, org, 1, size, timemod * frametime, color, zerodir); + break; + } + } +} + +void QMB_RocketTrail (vec3_t start, vec3_t end, trail_type_t type) +{ + col_t color; + + switch (type) + { + case GRENADE_TRAIL://r00K mODIFIED + + if (ISUNDERWATER(TruePointContents(start))) + { + AddParticleTrail (p_bubble, start, end, 1, 0.30, NULL); + } + else + + { + if (r_part_trails.value > 1) + { + color[0] = 15; color[1] = 15; color[2] = 10; + AddParticleTrail (p_alphatrail, start, end, 8, 1, color); + } + else + AddParticleTrail (p_smoke, start, end, 1.45, 0.825, NULL); + } + break; + + case BLOOD_TRAIL: + case SLIGHT_BLOOD_TRAIL: + AddParticleTrail (p_blood3, start, end, type == BLOOD_TRAIL ? 1.35 : 2.4, 2, NULL); + break; + + case TRACER1_TRAIL: + color[0] = color[2] = 0; + color[1] = 124; + AddParticleTrail (p_trailpart, start, end, 3.75, 0.5, color); + break; + + case TRACER2_TRAIL: + color[0] = 255; + color[1] = 77; + color[2] = 0; + AddParticleTrail (p_trailpart, start, end, 1.75, 0.2, color); + break; + + case VOOR_TRAIL: + color[0] = 77; + color[1] = 0; + color[2] = 255; + AddParticleTrail (p_trailpart, start, end, 3.75, 0.5, color); + break; + + case ALT_ROCKET_TRAIL: + + if (ISUNDERWATER(TruePointContents(start))) + { + AddParticleTrail (p_bubble, start, end, 1, 2.5, NULL); + } + else + + { + if (r_part_trails.value > 1) + { + color[0] = 15; color[1] = 15; color[2] = 10; + AddParticleTrail (p_alphatrail, start, end, 8, 1, color); + } + else + { + AddParticleTrail (p_dpfire, start, end, 3, 0.26, NULL); + AddParticleTrail (p_dpsmoke, start, end, 3, 0.825, NULL); + } + } + break; + + case LAVA_TRAIL: + AddParticleTrail (p_lavatrail, start, end, 5, 0.25, NULL); + AddParticleTrail (p_dpsmoke, start, end, 5, 0.825, NULL); + break; + + case BUBBLE_TRAIL: + + if (ISUNDERWATER(TruePointContents(start))) + AddParticleTrail (p_bubble2, start, end, 1.5, 0.825, NULL); + + break; + + case NEHAHRA_SMOKE: + AddParticleTrail (p_smoke, start, end, 0.8, 0.825, NULL); + break; + case RAYGREEN_TRAIL: + color[0] = 0; + color[1] = 255; + color[2] = 0; + AddParticleTrail (p_alphatrail, start, end, 8, 0.6, color); + break; + case RAYRED_TRAIL: + color[0] = 255; + color[1] = 0; + color[2] = 0; + AddParticleTrail (p_alphatrail, start, end, 8, 0.6, color); + break; + case ROCKET_TRAIL: + default: + color[0] = 255; + color[1] = 56; + color[2] = 9; +// AddParticleTrail (p_trailpart, start, end, 6.2, 0.31, color); + if (ISUNDERWATER(TruePointContents(start))) + { + AddParticleTrail (p_trailpart, start, end, 1, 0.30, color); + AddParticleTrail (p_bubble, start, end, 1, 2.5, NULL); + } + else + + { + if (r_part_trails.value > 1) + { + color[0] = 15; color[1] = 15; color[2] = 10; + AddParticleTrail (p_alphatrail, start, end, 8, 3, color); + } + else + { + AddParticleTrail (p_trailpart, start, end, 6.2, 0.31, color); + AddParticleTrail (p_smoke, start, end, 1.8, 0.825, NULL); + } + } + break; + case NAIL_TRAIL://R00k added + + if (ISUNDERWATER(TruePointContents(start))) + { + AddParticleTrail (p_bubble, start, end, 0.25, 0.50, NULL); + } + else + + { + color[0] = 15; color[1] = 15; color[2] = 15; + AddParticleTrail (p_alphatrail, start, end, 1, 0.25, color); + } + break; + } +} + +void QMB_BlobExplosion (vec3_t org) +{ + float theta; + col_t color; + vec3_t neworg, vel; + + color[0] = 60; + color[1] = 100; + color[2] = 240; + AddParticle (p_spark, org, 44, 250, 1.15, color, zerodir); + + color[0] = 90; + color[1] = 47; + color[2] = 207; + AddParticle (p_fire, org, 15, 30, 1.4, color, zerodir); + + vel[2] = 0; + for (theta = 0 ; theta < 6.28318530717958647692528676655901 ; theta += 0.0897597901025655210989326680937001) + { + color[0] = (60 + (rand() & 15)); + color[1] = (65 + (rand() & 15)); + color[2] = (200 + (rand() & 15)); + + #ifdef PSP_VFPU + vel[0] = vfpu_cosf(theta) * 125; + vel[1] = vfpu_sinf(theta) * 125; + neworg[0] = org[0] + vfpu_cosf(theta) * 6; + neworg[1] = org[1] + vfpu_sinf(theta) * 6; + #else + vel[0] = cos(theta) * 125; + vel[1] = sin(theta) * 125; + neworg[0] = org[0] + cos(theta) * 6; + neworg[1] = org[1] + sin(theta) * 6; + #endif + neworg[2] = org[2] + 0 - 10; + AddParticle (p_shockwave, neworg, 1, 4, 0.8, color, vel); + neworg[2] = org[2] + 0 + 10; + AddParticle (p_shockwave, neworg, 1, 4, 0.8, color, vel); + + vel[0] *= 1.15; + vel[1] *= 1.15; + #ifdef PSP_VFPU + neworg[0] = org[0] + vfpu_cosf(theta) * 13; + neworg[1] = org[1] + vfpu_sinf(theta) * 13; + #else + neworg[0] = org[0] + cos(theta) * 13; + neworg[1] = org[1] + sin(theta) * 13; + #endif + neworg[2] = org[2] + 0; + AddParticle (p_shockwave, neworg, 1, 6, 1.0, color, vel); + } +} + +void QMB_LavaSplash (vec3_t org) +{ + int i, j; + float vel; + vec3_t dir, neworg; + + for (i=-16 ; i<16; i++) + { + for (j=-16 ; j<16 ; j++) + { + dir[0] = j * 8 + (rand() & 7); + dir[1] = i * 8 + (rand() & 7); + dir[2] = 256; + + neworg[0] = org[0] + dir[0]; + neworg[1] = org[1] + dir[1]; + neworg[2] = org[2] + (rand() & 63); + + VectorNormalizeFast (dir); + vel = 50 + (rand() & 63); + VectorScale (dir, vel, dir); + } + } +} + +void QMB_TeleportSplash (vec3_t org) +{ + int i, j, k; + vec3_t neworg, angle; + col_t color; + + //QMB_Shockwave_Splash(org, 120); + for (i=-12 ; i<=12 ; i+=6) + { + for (j=-12 ; j<=12 ; j+=6) + { + for (k=-24 ; k<=32 ; k+=8) + { + neworg[0] = org[0] + i + (rand() & 3) - 1; + neworg[1] = org[1] + j + (rand() & 3) - 1; + neworg[2] = org[2] + k + (rand() & 3) - 1; + angle[0] = (rand() & 15) - 7; + angle[1] = (rand() & 15) - 7; + angle[2] = (rand() % 160) - 80; + AddParticle (p_teleflare, neworg, 1, 1.8, 0.30 + (rand() & 7) * 0.02, NULL, angle); + } + } + } + + VectorSet (color, 140, 140, 255); + VectorClear (angle); + for (i=0 ; i<5 ; i++) + { + angle[2] = 0; + for (j=0 ; j<5 ; j++) + { + AngleVectors (angle, NULLVEC, NULLVEC, neworg); + VectorMA (org, 70, neworg, neworg); + AddParticle (p_sparkray, org, 1, 6 + (i & 3), 5, color, neworg); + angle[2] += 360 / 5; + } + angle[0] += 180 / 5; + } +} + + +void QMB_InfernoFlame (vec3_t org) +{ + float frametime = fabs(cl.ctime - cl.oldtime); + + if (ISUNDERWATER(TruePointContents(org))) + return; + + if (frametime) + { + if (r_flametype.value < 1) + { + AddParticle (p_torch_flame, org, 1, 5, 0.5, NULL, zerodir);//R00k + } + else + { + AddParticle (p_inferno_flame, org, 1, 30, 13.125 * frametime, NULL, zerodir); + AddParticle (p_inferno_trail, org, 2, 1.75, 45.0 * frametime, NULL, zerodir); + AddParticle (p_inferno_trail, org, 2, 1.0, 52.5 * frametime, NULL, zerodir); + } + } +} + +void QMB_StaticBubble (entity_t *ent) +{ + AddParticle (p_staticbubble, ent->origin, 1, ent->frame == 1 ? 1.85 : 2.9, 0.001, NULL, zerodir); +} + +void QMB_TorchFlame (vec3_t org) +{ + if (fabs(cl.ctime - cl.oldtime)) + AddParticle (p_torch_flame, org, 2, 2.5, 0.5, NULL, zerodir); +} + +void QMB_FlameGt (vec3_t org, float size, float time) +{ + if (fabs(cl.ctime - cl.oldtime)) + AddParticle (p_flame, org, 1, size, time, NULL, zerodir); +} + +void QMB_BigTorchFlame (vec3_t org) +{ + if (fabs(cl.ctime - cl.oldtime)) + AddParticle (p_torch_flame, org, 2, 7, 0.5, NULL, zerodir); +} + +void QMB_Q3TorchFlame (vec3_t org, float size) +{ + static double flametime = 0; + + if (flametime + 0.125 < cl.time || flametime >= cl.time) + flametime = cl.time; + else + return; + + if (fabs(cl.ctime - cl.oldtime)) + AddParticle (p_q3flame, org, 1, size, 0.25, NULL, zerodir); +} + +void QMB_ShamblerCharge (vec3_t org) +{ + vec3_t pos, vec, dir; + col_t col = {60, 100, 240, 128}; + float time, len; + int i; + + for (i=0 ; i<5 ; i++) + { + VectorClear(vec); + VectorClear(dir); + + VectorCopy(org, pos); + pos[0] += (rand() % 200) - 100; + pos[1] += (rand() % 200) - 100; + pos[2] += (rand() % 200) - 100; + + VectorSubtract(pos, org, vec); + len = VectorLength (vec); + VectorNormalize (vec); + VectorMA(dir, -200, vec, dir); + time = len / 200; + + AddParticle (p_streakwave, pos, 1, 3, time, col, dir); + } +} + +void QMB_LaserSight (void) +{ + float frametime = fabs(cl.time - cl.oldtime); + col_t color; + int c; + + extern cvar_t r_laserpoint; + extern cvar_t scr_ofsx; + //extern cvar_t cl_gun_offset; + + //blubs-- Issue with this is that there's no particle texture assigned, need to either create, or fix rendering of null particle texture. + vec3_t dest, start, stop, forward, right,up; + trace_t trace; + + if (!particle_mode) + return; + + if (frametime) + { + if (qmb_initialized) + { + VectorClear(stop); + AngleVectors (r_refdef.viewangles, forward, right, up); + VectorCopy(cl_entities[cl.viewentity].origin, start); + + start[2] += 16; + start[2] += cl.crouch + bound(-7, scr_ofsx.value, 4); + + VectorMA (start, 0, right, start); + VectorMA (start, 4096, forward, dest); + + c = lt_default; + + switch ((int)r_laserpoint.value) + { + case 1: + color[0] = 000;color[1] = 000;color[2] = 255;color[3] = 50;//B + c = lt_blue; + break; + case 2: + color[0] = 255;color[1] = 000;color[2] = 000;color[3] = 50;//R + c = lt_red; + break; + case 3: + color[0] = 255;color[1] = 255;color[2] = 000;color[3] = 50;//Y + //c = lt_yellow; + c = lt_red; + break; + case 4: + color[0] = 000;color[1] = 255;color[2] = 000;color[3] = 50;//G + c = lt_green; + break; + } + + memset (&trace, 0, sizeof(trace_t)); + trace.fraction = 1; + SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, start, dest, &trace); + + start[2]+=cl.crouch; + AddParticle (p_streaktrail, start, 1, 2, 0.02, color, trace.endpos);// draw the line + //the 2 value above is size + + if (trace.fraction != 1) + { + color[3] = 200; + AddParticle (p_dot, trace.endpos, 1, 4, 0.01, color, zerodir);//pinpoint on wall + if ((cl.maxclients < 2) && (cl.time > cl.laser_point_time)) + { + CL_NewDlight (0, trace.endpos, (rand() % 10 + 30), 0.02, c); + cl.laser_point_time = cl.time + 0.02; + } + } + } + } +} + +void QMB_Lightning_Splash(vec3_t org) +{ + int i, j; + vec3_t neworg, angle; + col_t color; + col_t col2 = {200, 100, 100, 255}; + + VectorSet (color, 40, 40, 128); + VectorClear (angle); + + for (i=0 ; i<5 ; i++) + { + angle[2] = 0; + for (j=0 ; j<5 ; j++) + { + AngleVectors (angle, NULLVEC, NULLVEC, neworg); + VectorMA (org, 20, neworg, neworg); + AddParticle (p_spark, org, 2, 85, 0.05f, NULL, zerodir); + AddParticle (p_spark, org, 2, 100, 0.1f, col2, neworg); + angle[2] += 360 / 5; + } + angle[0] += 180 / 5; + } + color[0] = 224 + (rand() & 31); + color[1] = 100 + (rand() & 31); + color[2] = 0; + + AddParticle (p_spark, org, 1, 70, 0.2, color, zerodir); +} + +void QMB_LightningBeam (vec3_t start, vec3_t end) +{ + float frametime = fabs(cl.time - cl.oldtime); + col_t color = {255,255,255,255}; + trace_t trace; + + if (frametime) + { + if (qmb_initialized && r_part_lightning.value ) + { + if (qmb_initialized && r_part_sparks.value) + { + memset (&trace, 0, sizeof(trace_t)); + if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, start, end, &trace)) + { + if (trace.fraction < 1) + { + VectorCopy (trace.endpos, end); + if ((r_decal_sparks.value) && (particle_mode) && (decals_enabled)) + { + R_SpawnDecalStatic(end, decal_glow, 10); + } + QMB_Lightning_Splash (end); + } + } + } + + //R00k v1.84 moved down here + AddParticle(p_lightningbeam, start, 1, 80, host_frametime * 2, color, end); + } + } +} + +#if 0 +void R_DrawQ3Model (entity_t *ent); + +void QMB_Q3Gunshot (vec3_t org, int skinnum, float alpha) +{ + vec3_t neworg, normal, v, newend; + entity_t *ent; + extern model_t *cl_q3gunshot_mod; + + if (!(ent = CL_NewTempEntity())) + return; + + VectorCopy (org, ent->origin); + ent->model = cl_q3gunshot_mod; + + VectorCopy (cl_entities[cl.viewentity].origin, neworg); + VectorSubtract (ent->origin, neworg, v); + VectorScale (v, 2, v); + VectorAdd (neworg, v, newend); + + if (TraceLineN(neworg, newend, newend, normal)) + vectoangles (normal, ent->angles); + + ent->skinnum = skinnum; + ent->rendermode = TEX_ADDITIVE; + ent->renderamt = alpha; + + R_DrawQ3Model (ent); +} + +void QMB_Q3Teleport (vec3_t org, float alpha) +{ + entity_t *ent; + extern model_t *cl_q3teleport_mod; + + if (!(ent = CL_NewTempEntity())) + return; + + VectorCopy (org, ent->origin); + ent->model = cl_q3teleport_mod; + ent->rendermode = TEX_ADDITIVE; + ent->renderamt = alpha; + + R_DrawQ3Model (ent); +} + + +#else +void QMB_Q3Gunshot (vec3_t org, int skinnum, float alpha) +{ + Con_Printf("Q3 drawing is not enabled!\n"); +} + +void QMB_Q3Teleport (vec3_t org, float alpha) +{ + Con_Printf("Q3 drawing is not enabled!\n"); +} +#endif + + +#define NUMVERTEXNORMALS 162 + +extern float r_avertexnormals[NUMVERTEXNORMALS][3]; +extern vec3_t avelocities[NUMVERTEXNORMALS]; + +/* +=============== +R_EntityParticles +=============== +*/ +void QMB_EntityParticles (entity_t *ent) +{ + int i; + float angle, dist, sp, sy, cp, cy; + vec3_t forward, org; + col_t color = {255,255,0,100}; + + dist = 64; + + if (!avelocities[0][0]) + for (i=0 ; iorigin[0] + r_avertexnormals[i][0]*dist + forward[0]*16; + org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*16; + org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*16; + AddParticle (p_flare, org, 1, 2,0.005, color, forward); + } +} + +//Modified from Quake2 +void QMB_FlyParticles (vec3_t origin, int count) +{ + float frametime = fabs(cl.time - cl.oldtime); + int i; + float angle, sp, sy, cp, cy; + vec3_t forward, org; + float dist = 64; + col_t color = {255,255,255,100}; + + if (frametime) + { + if (count > NUMVERTEXNORMALS) { + count = NUMVERTEXNORMALS; + } + + if (!avelocities[0][0]) + { + for (i=0 ; iefrag; + + while (ef) + { + prev = &ef->leaf->efrags; + while (1) + { + walk = *prev; + if (!walk) + break; + if (walk == ef) + { // remove this fragment + *prev = ef->leafnext; + break; + } + else + prev = &walk->leafnext; + } + + old = ef; + ef = ef->entnext; + + // put it on the free list + old->entnext = cl.free_efrags; + cl.free_efrags = old; + } + + ent->efrag = NULL; +} + +/* +=================== +R_SplitEntityOnNode +=================== +*/ +void R_SplitEntityOnNode (mnode_t *node) +{ + efrag_t *ef; + mplane_t *splitplane; + mleaf_t *leaf; + int sides; + + if (node->contents == CONTENTS_SOLID) + { + return; + } + +// add an efrag if the node is a leaf + + if ( node->contents < 0) + { + if (!r_pefragtopnode) + r_pefragtopnode = node; + + leaf = (mleaf_t *)node; + +// grab an efrag off the free list + ef = cl.free_efrags; + if (!ef) + { + Con_Printf ("Too many efrags!\n"); + return; // no free fragments... + } + cl.free_efrags = cl.free_efrags->entnext; + + ef->entity = r_addent; + +// add the entity link + *lastlink = ef; + lastlink = &ef->entnext; + ef->entnext = NULL; + +// set the leaf links + ef->leaf = leaf; + ef->leafnext = leaf->efrags; + leaf->efrags = ef; + + return; + } + +// NODE_MIXED + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); + + if (sides == 3) + { + // split on this plane + // if this is the first splitter of this bmodel, remember it + if (!r_pefragtopnode) + r_pefragtopnode = node; + } + +// recurse down the contacted sides + if (sides & 1) + R_SplitEntityOnNode (node->children[0]); + + if (sides & 2) + R_SplitEntityOnNode (node->children[1]); +} + + + +/* +=========== +R_AddEfrags +=========== +*/ +void R_AddEfrags (entity_t *ent) +{ + model_t *entmodel; + int i; + + if (!ent->model) + return; + + r_addent = ent; + + lastlink = &ent->efrag; + r_pefragtopnode = NULL; + + entmodel = ent->model; + + for (i=0 ; i<3 ; i++) + { + r_emins[i] = ent->origin[i] + entmodel->mins[i]; + r_emaxs[i] = ent->origin[i] + entmodel->maxs[i]; + } + + R_SplitEntityOnNode (cl.worldmodel->nodes); + + ent->topnode = r_pefragtopnode; +} + + +/* +================ +R_StoreEfrags + +// FIXME: a lot of this goes away with edge-based +================ +*/ +void R_StoreEfrags (efrag_t **ppefrag) +{ + entity_t *pent; + model_t *clmodel; + efrag_t *pefrag; + + + while ((pefrag = *ppefrag) != NULL) + { + pent = pefrag->entity; + clmodel = pent->model; + + switch (clmodel->type) + { + case mod_alias: + case mod_brush: + case mod_sprite: + pent = pefrag->entity; + + if ((pent->visframe != r_framecount) && + (cl_numvisedicts < MAX_VISEDICTS)) + { + cl_visedicts[cl_numvisedicts++] = pent; + + // mark that we've recorded this entity for this frame + pent->visframe = r_framecount; + } + + ppefrag = &pefrag->leafnext; + break; + + default: + Sys_Error ("R_StoreEfrags: Bad entity type %d\n", clmodel->type); + } + } +} + + diff --git a/source/ctr/gl/gl_rlight.c b/source/ctr/gl/gl_rlight.c new file mode 100644 index 0000000..408f40f --- /dev/null +++ b/source/ctr/gl/gl_rlight.c @@ -0,0 +1,399 @@ +/* +Copyright (C) 1996-2001 Id Software, Inc. +Copyright (C) 2002-2009 John Fitzgibbons and others +Copyright (C) 2010-2014 QuakeSpasm developers + +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_light.c + +#include "../../quakedef.h" + +int r_dlightframecount; + +extern cvar_t r_flatlightstyles; //johnfitz + +/* +================== +R_AnimateLight +================== +*/ +void R_AnimateLight (void) +{ + int i,j,k; + +// +// light animations +// 'm' is normal light, 'a' is no light, 'z' is double bright + i = (int)(cl.time*10); + for (j=0 ; jradius * 0.35; + + + VectorSubtract (light->origin, r_origin, v); + if (VectorLength (v) < rad) + { // view is inside the dlight + AddLightBlend (1, 0.5, 0, light->radius * 0.0003); + return; + } + + glBegin (GL_TRIANGLE_FAN); + //glColor4f(lightcolor[0]/255, lightcolor[1]/255, lightcolor[2]/255, 1.0f); + glColor4f (light->color[0]*0.2f,light->color[1]*0.2f,light->color[2]*0.2f, 1.0f); + //glColor4f(1.0f, 0.0f, 0.0f, 1.0f); + for (i=0 ; i<3 ; i++) + v[i] = light->origin[i] - vpn[i]*rad; + glVertex3fv (v); + //glColor3f (0,0,0); + for (i=16 ; i>=0 ; i--) + { + a = i/16.0 * M_PI*2; + for (j=0 ; j<3 ; j++) + v[j] = light->origin[j] + vright[j]*cos(a)*rad + + vup[j]*sin(a)*rad; + glVertex3fv (v); + } + glEnd (); +#endif +} + +/* +============= +R_RenderDlights +============= +*/ +void R_RenderDlights (void) +{ +#if 0 + int i; + dlight_t *l; + + if (!gl_flashblend.value) + return; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + glDepthMask (GL_FALSE); + glDisable (GL_TEXTURE_2D); + glShadeModel (GL_SMOOTH); + glEnable (GL_BLEND); + glBlendFunc (GL_ONE, GL_ONE); + + l = cl_dlights; + for (i=0 ; idie < cl.time || !l->radius) + continue; + R_RenderDlight (l); + } + + glColor3f (1,1,1); + glDisable (GL_BLEND); + glEnable (GL_TEXTURE_2D); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask (GL_TRUE); +#endif +} + + +/* +============================================================================= + +DYNAMIC LIGHTS + +============================================================================= +*/ + +/* +============= +R_MarkLights -- johnfitz -- rewritten to use LordHavoc's lighting speedup +============= +*/ +void R_MarkLights (dlight_t *light, int num, mnode_t *node) +{ + mplane_t *splitplane; + msurface_t *surf; + vec3_t impact; + float dist, l, maxdist; + int i, j, s, t; + +start: + + if (node->contents < 0) + return; + + splitplane = node->plane; + if (splitplane->type < 3) + dist = light->origin[splitplane->type] - splitplane->dist; + else + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + node = node->children[0]; + goto start; + } + if (dist < -light->radius) + { + node = node->children[1]; + goto start; + } + + maxdist = light->radius*light->radius; +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + for (j=0 ; j<3 ; j++) + impact[j] = light->origin[j] - surf->plane->normal[j]*dist; + // clamp center of light to corner and check brightness + l = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0]; + s = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0]; + s = l - s; + l = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1]; + t = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1]; + t = l - t; + // compare to minimum light + if ((s*s+t*t+dist*dist) < maxdist) + { + if (surf->dlightframe != r_dlightframecount) // not dynamic until now + { + surf->dlightbits[num >> 5] = 1U << (num & 31); + surf->dlightframe = r_dlightframecount; + } + else // already dynamic + surf->dlightbits[num >> 5] |= 1U << (num & 31); + } + } + + if (node->children[0]->contents >= 0) + R_MarkLights (light, num, node->children[0]); + if (node->children[1]->contents >= 0) + R_MarkLights (light, num, node->children[1]); +} + +/* +============= +R_PushDlights +============= +*/ +void R_PushDlights (void) +{ + int i; + dlight_t *l; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + l = cl_dlights; + + for (i=0 ; idie < cl.time || !l->radius) + continue; + R_MarkLights (l, i, cl.worldmodel->nodes); + } +} + + +/* +============================================================================= + +LIGHT SAMPLING + +============================================================================= +*/ + +mplane_t *lightplane; +vec3_t lightspot; +vec3_t lightcolor; //johnfitz -- lit support via lordhavoc + +/* +============= +RecursiveLightPoint -- johnfitz -- replaced entire function for lit support via lordhavoc +============= +*/ +int RecursiveLightPoint (vec3_t color, mnode_t *node, vec3_t start, vec3_t end) +{ + float front, back, frac; + vec3_t mid; + +loc0: + if (node->contents < 0) + return false; // didn't hit anything + +// calculate mid point + if (node->plane->type < 3) + { + front = start[node->plane->type] - node->plane->dist; + back = end[node->plane->type] - node->plane->dist; + } + else + { + front = DotProduct(start, node->plane->normal) - node->plane->dist; + back = DotProduct(end, node->plane->normal) - node->plane->dist; + } + + // LordHavoc: optimized recursion + if ((back < 0) == (front < 0)) +// return RecursiveLightPoint (color, node->children[front < 0], start, end); + { + node = node->children[front < 0]; + goto loc0; + } + + frac = front / (front-back); + mid[0] = start[0] + (end[0] - start[0])*frac; + mid[1] = start[1] + (end[1] - start[1])*frac; + mid[2] = start[2] + (end[2] - start[2])*frac; + +// go down front side + if (RecursiveLightPoint (color, node->children[front < 0], start, mid)) + return true; // hit something + else + { + int i, ds, dt; + msurface_t *surf; + // check for impact on this node + VectorCopy (mid, lightspot); + lightplane = node->plane; + + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i = 0;i < node->numsurfaces;i++, surf++) + { + if (surf->flags & SURF_DRAWTILED) + continue; // no lightmaps + + // ericw -- added double casts to force 64-bit precision. + // Without them the zombie at the start of jam3_ericw.bsp was + // incorrectly being lit up in SSE builds. + ds = (int) ((double) DoublePrecisionDotProduct (mid, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]); + dt = (int) ((double) DoublePrecisionDotProduct (mid, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]); + + if (ds < surf->texturemins[0] || dt < surf->texturemins[1]) + continue; + + ds -= surf->texturemins[0]; + dt -= surf->texturemins[1]; + + if (ds > surf->extents[0] || dt > surf->extents[1]) + continue; + + if (surf->samples) + { + // LordHavoc: enhanced to interpolate lighting + byte *lightmap; + int maps, line3, dsfrac = ds & 15, dtfrac = dt & 15, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0; + float scale; + line3 = ((surf->extents[0]>>4)+1)*3; + + lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color + + for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++) + { + scale = (float) d_lightstylevalue[surf->styles[maps]] * 1.0 / 256.0; + r00 += (float) lightmap[ 0] * scale;g00 += (float) lightmap[ 1] * scale;b00 += (float) lightmap[2] * scale; + r01 += (float) lightmap[ 3] * scale;g01 += (float) lightmap[ 4] * scale;b01 += (float) lightmap[5] * scale; + r10 += (float) lightmap[line3+0] * scale;g10 += (float) lightmap[line3+1] * scale;b10 += (float) lightmap[line3+2] * scale; + r11 += (float) lightmap[line3+3] * scale;g11 += (float) lightmap[line3+4] * scale;b11 += (float) lightmap[line3+5] * scale; + lightmap += ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting + } + + color[0] += (float) ((int) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00))); + color[1] += (float) ((int) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00))); + color[2] += (float) ((int) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00))); + } + return true; // success + } + + // go down back side + return RecursiveLightPoint (color, node->children[front >= 0], mid, end); + } +} + +/* +============= +R_LightPoint -- johnfitz -- replaced entire function for lit support via lordhavoc +============= +*/ +int R_LightPoint (vec3_t p) +{ + vec3_t end; + + if (!cl.worldmodel->lightdata) + { + lightcolor[0] = lightcolor[1] = lightcolor[2] = 255; + return 255; + } + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 8192; //johnfitz -- was 2048 + + lightcolor[0] = lightcolor[1] = lightcolor[2] = 0; + RecursiveLightPoint (lightcolor, cl.worldmodel->nodes, p, end); + return ((lightcolor[0] + lightcolor[1] + lightcolor[2]) * (1.0f / 3.0f)); +} \ No newline at end of file diff --git a/source/ctr/gl/gl_rmain.c b/source/ctr/gl/gl_rmain.c new file mode 100644 index 0000000..b3005a4 --- /dev/null +++ b/source/ctr/gl/gl_rmain.c @@ -0,0 +1,1882 @@ +/* +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_main.c + +#include "../../quakedef.h" + +entity_t r_worldentity; + +qboolean r_cache_thrash; // compatability + +vec3_t modelorg, r_entorigin; +entity_t *currententity; + +int r_visframecount; // bumped when going to a new PVS +int r_framecount; // used for dlight push checking + +mplane_t frustum[4]; + +int c_brush_polys, c_alias_polys; + +qboolean envmap; // true during envmap command capture + +int currenttexture = -1; // to avoid unnecessary texture sets + +int cnttextures[2] = {-1, -1}; // cached + +int particletexture; // little dot for particles +int playertextures; // up to 16 color translated skins + +int mirrortexturenum; // quake texturenum, not gltexturenum +qboolean mirror; +mplane_t *mirror_plane; + +// +// view origin +// +vec3_t vup; +vec3_t vpn; +vec3_t vright; +vec3_t r_origin; + +float r_world_matrix[16]; +float r_base_world_matrix[16]; + +// +// screen size info +// +refdef_t r_refdef; + +mleaf_t *r_viewleaf, *r_oldviewleaf; + +texture_t *r_notexture_mip; + +int d_lightstylevalue[256]; // 8.8 fraction of base light value + + +void R_MarkLeaves (void); + +cvar_t r_norefresh = {"r_norefresh","0"}; +cvar_t r_drawentities = {"r_drawentities","1"}; +cvar_t r_drawviewmodel = {"r_drawviewmodel","1"}; +cvar_t r_speeds = {"r_speeds","0"}; +cvar_t r_fullbright = {"r_fullbright","0"}; +cvar_t r_lightmap = {"r_lightmap","0"}; +cvar_t r_shadows = {"r_shadows","0"}; +cvar_t r_mirroralpha = {"r_mirroralpha","1"}; +cvar_t r_wateralpha = {"r_wateralpha","1"}; +cvar_t r_dynamic = {"r_dynamic","1"}; +cvar_t r_novis = {"r_novis","0"}; +cvar_t r_skyfog = {"r_skyfog", "1"}; + +cvar_t gl_finish = {"gl_finish","0"}; +cvar_t gl_clear = {"gl_clear","0"}; +cvar_t gl_cull = {"gl_cull","1"}; +cvar_t gl_texsort = {"gl_texsort","1"}; +cvar_t gl_smoothmodels = {"gl_smoothmodels","1"}; +cvar_t gl_affinemodels = {"gl_affinemodels","0"}; +cvar_t gl_polyblend = {"gl_polyblend","1"}; +cvar_t gl_flashblend = {"gl_flashblend","1"}; +cvar_t gl_playermip = {"gl_playermip","0"}; +cvar_t gl_nocolors = {"gl_nocolors","0"}; +cvar_t gl_keeptjunctions = {"gl_keeptjunctions","0"}; +cvar_t gl_reporttjunctions = {"gl_reporttjunctions","0"}; +cvar_t gl_doubleeyes = {"gl_doubleeys", "1"}; + +//QMB +cvar_t r_explosiontype = {"r_explosiontype", "0",qtrue}; +cvar_t r_laserpoint = {"r_laserpoint", "0",qtrue}; +cvar_t r_part_explosions = {"r_part_explosions", "1",qtrue}; +cvar_t r_part_trails = {"r_part_trails", "1",qtrue}; +cvar_t r_part_sparks = {"r_part_sparks", "1",qtrue}; +cvar_t r_part_spikes = {"r_part_spikes", "1",qtrue}; +cvar_t r_part_gunshots = {"r_part_gunshots", "1",qtrue}; +cvar_t r_part_blood = {"r_part_blood", "1",qtrue}; +cvar_t r_part_telesplash = {"r_part_telesplash", "1",qtrue}; +cvar_t r_part_blobs = {"r_part_blobs", "1",qtrue}; +cvar_t r_part_lavasplash = {"r_part_lavasplash", "1",qtrue}; +cvar_t r_part_flames = {"r_part_flames", "1",qtrue}; +cvar_t r_part_lightning = {"r_part_lightning", "1",qtrue}; +cvar_t r_part_flies = {"r_part_flies", "1",qtrue}; +cvar_t r_part_muzzleflash = {"r_part_muzzleflash", "1",qtrue}; +cvar_t r_flametype = {"r_flametype", "2",qtrue}; +//Shpuld +cvar_t r_model_brightness = { "r_model_brightness", "1", qtrue}; // Toggle high brightness model lighting + +cvar_t r_farclip = {"r_farclip", "4096"}; //far cliping for q3 models + +cvar_t r_flatlightstyles = {"r_flatlightstyles", "0", qfalse}; + +extern cvar_t gl_ztrick; +extern cvar_t scr_fov_viewmodel; + +/* +================= +R_CullBox + +Returns true if the box is completely outside the frustom +================= +*/ +qboolean R_CullBox (vec3_t mins, vec3_t maxs) +{ + int i; + + for (i=0 ; i<4 ; i++) + if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) + return true; + return false; +} + + +void R_RotateForEntity (entity_t *e, unsigned char scale) +{ + glTranslatef (e->origin[0], e->origin[1], e->origin[2]); + + glRotatef (e->angles[1], 0, 0, 1); + glRotatef (-e->angles[0], 0, 1, 0); + glRotatef (e->angles[2], 1, 0, 0); + + if (scale != ENTSCALE_DEFAULT && scale != 0) { + float scalefactor = ENTSCALE_DECODE(scale); + glScalef(scalefactor, scalefactor, scalefactor); + } +} + +void IgnoreInterpolatioFrame (entity_t *e, aliashdr_t *paliashdr) +{ + if (strcmp(e->old_model, e->model->name) && e->model != NULL) + { + strcpy(e->old_model, e->model->name); + // fenix@io.com: model transform interpolation + e->frame_start_time = 0; + e->translate_start_time = 0; + e->rotate_start_time = 0; + e->pose1 = 0; + e->pose2 = paliashdr->frames[e->frame].firstpose; + } +} + + +/* +============= +R_InterpolateEntity + +was R_BlendedRotateForEntity +fenix@io.com: model transform interpolation + +modified by blubswillrule +//fixme (come back and fix this once we can test on psp and view the true issue with interpolation) +============= +*/ + +void R_InterpolateEntity(entity_t *e, int shadow) // Tomaz - New Shadow +{ + float timepassed; + float blend; + vec3_t deltaVec; + int i; + + // positional interpolation + + timepassed = realtime - e->translate_start_time; + + //notes to self (blubs) + //-Added this method, and commented out the check for r_i_model_transforms.value + //tried the snapping interpolation, though it worked, it was still a bit jittery... + //problem with linear interpolation is we don't know the exact time it should take to move from origin1 to origin2... + //looks like the rotation interpolation doesn't work all that great either, rotation could benefit from the snapping interpolation that I use + //if I get this method to work well, make sure we go back and check for r_i_model_transforms again, (because vmodel and other models that don't use interpolation) + //probably go back and edit animations too as I redo the last 2 textures.. + + if (e->translate_start_time == 0 || timepassed > 1) + { + e->translate_start_time = realtime; + VectorCopy (e->origin, e->origin1); + VectorCopy (e->origin, e->origin2); + } + + //our origin has been updated + if (!VectorCompare (e->origin, e->origin2)) + { + e->translate_start_time = realtime; + VectorCopy (e->origin2, e->origin1); + VectorCopy (e->origin, e->origin2); + blend = 0; + } + else + { + blend = timepassed / 0.4;//0.1 not sure what this value should be... + //technically this value should be the total amount of time that we take from 1 position to the next, it's practically how long it should take us to go from one location to the next... + if (cl.paused || blend > 1) + blend = 0; + } + + VectorSubtract (e->origin2, e->origin1, deltaVec); + + glTranslatef (e->origin[0] + (blend * deltaVec[0]), e->origin[1] + (blend * deltaVec[1]), e->origin[2] + (blend * deltaVec[2])); + + // orientation interpolation (Euler angles, yuck!) + timepassed = realtime - e->rotate_start_time; + + if (e->rotate_start_time == 0 || timepassed > 1) + { + e->rotate_start_time = realtime; + VectorCopy (e->angles, e->angles1); + VectorCopy (e->angles, e->angles2); + } + + if (!VectorCompare (e->angles, e->angles2)) + { + e->rotate_start_time = realtime; + VectorCopy (e->angles2, e->angles1); + VectorCopy (e->angles, e->angles2); + blend = 0; + } + else + { + blend = timepassed / 0.1; + if (cl.paused || blend > 1) + blend = 1; + } + + VectorSubtract (e->angles2, e->angles1, deltaVec); + + // always interpolate along the shortest path + for (i = 0; i < 3; i++) + { + if (deltaVec[i] > 180) + { + deltaVec[i] -= 360; + } + else if (deltaVec[i] < -180) + { + deltaVec[i] += 360; + } + } + + glRotatef ((e->angles1[YAW] + ( blend * deltaVec[YAW])) * (M_PI / 180.0f), 0, 0, 1); + if (shadow == 0) + { + glRotatef ((-e->angles1[PITCH] + (-blend * deltaVec[PITCH])) * (M_PI / 180.0f), 0, 1, 0); + glRotatef ((e->angles1[ROLL] + ( blend * deltaVec[ROLL])) * (M_PI / 180.0f), 1, 0, 0); + } +} + +/* +================= +R_FrustumCheckBox +Returns 0 if box completely inside frustum +Returns +N with intersected planes count as N +Returns -1 when completely outside frustum +================= +*/ +int R_FrustumCheckBox (vec3_t mins, vec3_t maxs) +{ + int i, res; + int intersections = 0; + for (i=0 ; i<4 ; i++) + { + res = BoxOnPlaneSide (mins, maxs, &frustum[i]); + if (res == 2) return -1; + if (res == 3) ++intersections; + } + + return intersections; +} + +/* +============================================================= + + SPRITE MODELS + +============================================================= +*/ + +/* +================ +R_GetSpriteFrame +================ +*/ +mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, numframes, frame; + float *pintervals, fullinterval, targettime, time; + + psprite = currententity->model->cache.data; + 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_DrawSpriteModel + +================= +*/ +void R_DrawSpriteModel (entity_t *e) +{ + vec3_t point; + mspriteframe_t *frame; + float *up, *right; + vec3_t v_forward, v_right, v_up; + msprite_t *psprite; + float scale = ENTSCALE_DECODE(e->scale); + if (scale == 0) scale = 1.0f; + + // don't even bother culling, because it's just a single + // polygon without a surface cache + frame = R_GetSpriteFrame (e); + psprite = currententity->model->cache.data; + + if (psprite->type == SPR_ORIENTED) + { // bullet marks on walls + AngleVectors (currententity->angles, v_forward, v_right, v_up); + up = v_up; + right = v_right; + } + else + { // normal sprite + up = vup; + right = vright; + } + + glColor3f (1,1,1); + + GL_DisableMultitexture(); + + GL_Bind(frame->gl_texturenum); + + Fog_DisableGFog (); + + glDisable (GL_ALPHA_TEST); + glEnable (GL_BLEND); + glDepthMask(GL_FALSE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBegin (GL_QUADS); + + glTexCoord2f (0, 1); + VectorMA (e->origin, frame->down * scale, up, point); + VectorMA (point, frame->left * scale, right, point); + glVertex3fv (point); + + glTexCoord2f (0, 0); + VectorMA (e->origin, frame->up * scale, up, point); + VectorMA (point, frame->left * scale, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 0); + VectorMA (e->origin, frame->up * scale, up, point); + VectorMA (point, frame->right * scale, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 1); + VectorMA (e->origin, frame->down * scale, up, point); + VectorMA (point, frame->right * scale, right, point); + glVertex3fv (point); + + glEnd (); + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); + + Fog_EnableGFog (); +} + +/* +============================================================= + + ALIAS MODELS + +============================================================= +*/ + + +#define NUMVERTEXNORMALS 162 + +float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "../../anorms.h" +}; + +vec3_t shadevector; +float shadelight, ambientlight; +extern vec3_t lightcolor; // LordHavoc: .lit support to the definitions at the top + +// precalculated dot products for quantized angles +#define SHADEDOT_QUANT 16 +float r_avertexnormal_dots[SHADEDOT_QUANT][256] = +#include "../../anorm_dots.h" +; + +float *shadedots = r_avertexnormal_dots[0]; + +int lastposenum; + +/* +============= +GL_DrawAliasFrame -- johnfitz -- rewritten to support colored light, lerping, entalpha, multitexture, and r_drawflat +============= +*/ +void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum) +{ + float vertcolor[4]; + trivertx_t *verts; + int *commands; + int count; + float u,v; + + verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); + verts += posenum * paliashdr->poseverts; + commands = (int *)((byte *)paliashdr + paliashdr->commands); + + glColor4f(lightcolor[0]/255, lightcolor[1]/255, lightcolor[2]/255, 1.0f); + + while (1) + { + // get the vertex count and primitive type + count = *commands++; + if (!count) + break; // done + + if (count < 0) + { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } + else + glBegin (GL_TRIANGLE_STRIP); + + do + { + u = ((float *)commands)[0]; + v = ((float *)commands)[1]; + + glTexCoord2f (u, v); + + commands += 2; + + glVertex3f (verts->v[0], verts->v[1], verts->v[2]); + verts++; + } while (--count); + + glEnd (); + } +} + + +/* +============= +GL_DrawAliasBlendedFrame + +fenix@io.com: model animation interpolation +============= +*/ +// fenix@io.com: model animation interpolation +int lastposenum0; +void GL_DrawAliasBlendedFrame (aliashdr_t *paliashdr, int pose1, int pose2, float blend) +{ + // if (r_showtris.value) + // { + // GL_DrawAliasBlendedWireFrame(paliashdr, pose1, pose2, blend); + // return; + // } + trivertx_t* verts1; + trivertx_t* verts2; + vec3_t d; + int *commands; + int count; + float u,v; + + lastposenum0 = pose1; + lastposenum = pose2; + + verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); + verts2 = verts1; + + verts1 += pose1 * paliashdr->poseverts; + verts2 += pose2 * paliashdr->poseverts; + + commands = (int *)((byte *)paliashdr + paliashdr->commands); + + glColor4f(lightcolor[0]/255, lightcolor[1]/255, lightcolor[2]/255, 1.0f); + + while (1) + { + // get the vertex count and primitive type + count = *commands++; + if (!count) + break; // done + + if (count < 0) + { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } + else + glBegin (GL_TRIANGLE_STRIP); + + do + { + u = ((float *)commands)[0]; + v = ((float *)commands)[1]; + + glTexCoord2f (u, v); + + commands += 2; + + VectorSubtract(verts2->v, verts1->v, d); + glVertex3f (verts1->v[0] + (blend * d[0]), verts1->v[1] + (blend * d[1]), verts1->v[2] + (blend * d[2])); + verts1++; + verts2++; + } while (--count); + + glEnd (); + } +} + +/* +============= +GL_DrawAliasShadow +============= +*/ +extern vec3_t lightspot; + +void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) +{ + float s, t, l; + int i, j; + int index; + trivertx_t *v, *verts; + int list; + int *order; + vec3_t point; + float *normal; + float height, lheight; + int count; + + lheight = currententity->origin[2] - lightspot[2]; + + height = 0; + verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); + verts += posenum * paliashdr->poseverts; + order = (int *)((byte *)paliashdr + paliashdr->commands); + + height = -lheight + 1.0; + + while (1) + { + // get the vertex count and primitive type + count = *order++; + if (!count) + break; // done + if (count < 0) + { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } + else + glBegin (GL_TRIANGLE_STRIP); + + do + { + // texture coordinates come from the draw list + // (skipped for shadows) glTexCoord2fv ((float *)order); + order += 2; + + // normals and vertexes come from the frame list + point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; + point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; + point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; + + point[0] -= shadevector[0]*(point[2]+lheight); + point[1] -= shadevector[1]*(point[2]+lheight); + point[2] = height; +// height -= 0.001; + glVertex3fv (point); + + verts++; + } while (--count); + + glEnd (); + } +} + + + +/* +================= +R_SetupAliasFrame +================= +*/ +void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr) +{ + int pose, numposes; + float interval; + + if ((frame >= paliashdr->numframes) || (frame < 0)) + { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + pose = paliashdr->frames[frame].firstpose; + numposes = paliashdr->frames[frame].numposes; + + if (numposes > 1) + { + interval = paliashdr->frames[frame].interval; + pose += (int)(cl.time / interval) % numposes; + } + + GL_DrawAliasFrame(paliashdr, pose); +} + +/* +================= +R_SetupAliasBlendedFrame + +fenix@io.com: model animation interpolation +================= +*/ +//double t1, t2, t3; + +void R_SetupAliasBlendedFrame (int frame, aliashdr_t *paliashdr, entity_t* e) +{ + int pose; + int numposes; + float blend; + + if ((frame >= paliashdr->numframes) || (frame < 0)) + { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + // HACK: if we're a certain distance away, don't bother blending + // cypress -- Lets not care about Z (up).. chances are they're out of the frustum anyway + int dist_x = (cl.viewent.origin[0] - e->origin[0]); + int dist_y = (cl.viewent.origin[1] - e->origin[1]); + int distance_from_client = (int)((dist_x) * (dist_x) + (dist_y) * (dist_y)); // no use sqrting, just slows us down. + + // They're too far away from us to care about blending their frames. + if (distance_from_client >= 160000) { // 400 * 400 + // Fix them from jumping from last lerp + e->pose1 = e->pose2 = paliashdr->frames[frame].firstpose; + e->frame_interval = 0.1; + + GL_DrawAliasFrame (paliashdr, paliashdr->frames[frame].firstpose); + } else { + pose = paliashdr->frames[frame].firstpose; + numposes = paliashdr->frames[frame].numposes; + + if (numposes > 1) + { + e->frame_interval = paliashdr->frames[frame].interval; + pose += (int)(cl.time / e->frame_interval) % numposes; + } + else + { + /* One tenth of a second is a good for most Quake animations. + If the nextthink is longer then the animation is usually meant to pause + (e.g. check out the shambler magic animation in shambler.qc). If its + shorter then things will still be smoothed partly, and the jumps will be + less noticable because of the shorter time. So, this is probably a good + assumption. */ + e->frame_interval = 0.1; + } + + if (e->pose2 != pose) + { + e->frame_start_time = realtime; + e->pose1 = e->pose2; + e->pose2 = pose; + blend = 0; + } + else + blend = (realtime - e->frame_start_time) / e->frame_interval; + // wierd things start happening if blend passes 1 + if (cl.paused || blend > 1) blend = 1; + + if (blend == 1) + GL_DrawAliasFrame (paliashdr, pose); + else + GL_DrawAliasBlendedFrame (paliashdr, e->pose1, e->pose2, blend); + } +} + +/* +================= +R_DrawZombieLimb + +================= +*/ +//Blubs Z hacks: need this declaration. +model_t *Mod_FindName (char *name); + +void R_DrawZombieLimb (entity_t *e, int which) +{ + model_t *clmodel; + aliashdr_t *paliashdr; + entity_t *limb_ent; + + switch(which) { + case 1: + limb_ent = &cl_entities[e->z_head]; + break; + case 2: + limb_ent = &cl_entities[e->z_larm]; + break; + case 3: + limb_ent = &cl_entities[e->z_rarm]; + break; + default: + return; + } + + clmodel = limb_ent->model; + + if (clmodel == NULL) + return; + + VectorCopy(e->origin, r_entorigin); + VectorSubtract(r_origin, r_entorigin, modelorg); + + // locate the proper data + paliashdr = (aliashdr_t *)Mod_Extradata(clmodel);//e->model + c_alias_polys += paliashdr->numtris; + + GL_DisableMultitexture(); + + //Shpuld + if(r_model_brightness.value) + { + lightcolor[0] += 48; + lightcolor[1] += 48; + lightcolor[2] += 48; + } + + glPushMatrix (); + R_RotateForEntity (e, e->scale); + + glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); + glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); + + if (gl_smoothmodels.value) + glShadeModel (GL_SMOOTH); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + IgnoreInterpolatioFrame(e, paliashdr); + R_SetupAliasBlendedFrame (currententity->frame, paliashdr, e); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glShadeModel (GL_FLAT); + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + glPopMatrix (); +} + +/* +================= +R_DrawTransparentAliasModel + +================= +*/ +void R_DrawTransparentAliasModel (entity_t *e) +{ + int i, j; + int lnum; + vec3_t dist; + float add; + model_t *clmodel; + vec3_t mins, maxs; + aliashdr_t *paliashdr; + trivertx_t *verts, *v; + int index; + float s, t, an; + int anim; + + clmodel = currententity->model; + + VectorAdd (currententity->origin, clmodel->mins, mins); + VectorAdd (currententity->origin, clmodel->maxs, maxs); + +// naievil -- fixme: on psp this is == 2 ? + if (R_CullBox (mins, maxs)) + return; + + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // for(int g = 0; g < 3; g++) + // { + // if(lightcolor[g] < 8) + // lightcolor[g] = 8; + // if(lightcolor[g] > 125) + // lightcolor[g] = 125; + // } + + // // + // // get lighting information + // // + + // ambientlight = shadelight = R_LightPoint (currententity->origin); + // for (lnum=0 ; lnum= cl.time) + // { + // VectorSubtract (currententity->origin, + // cl_dlights[lnum].origin, + // dist); + // add = cl_dlights[lnum].radius - Length(dist); + + // if (add > 0) { + // ambientlight += add; + // //ZOID models should be affected by dlights as well + // shadelight += add; + // } + // } + // } + + // // clamp lighting so it doesn't overbright as much + // if (ambientlight > 128) + // ambientlight = 128; + // if (ambientlight + shadelight > 192) + // shadelight = 192 - ambientlight; + + // shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + // shadelight = shadelight / 200.0; + + // an = e->angles[1]/180*M_PI; + // shadevector[0] = cos(-an); + // shadevector[1] = sin(-an); + // shadevector[2] = 1; + // VectorNormalize (shadevector); + + // + // locate the proper data + // + paliashdr = (aliashdr_t *)Mod_Extradata (e->model); + c_alias_polys += paliashdr->numtris; + + // + // draw all the triangles + // + + GL_DisableMultitexture(); + lightcolor[0] = lightcolor[1] = lightcolor[2] = 256.0f; + + glPushMatrix (); + R_RotateForEntity (e, e->scale); + + glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); + glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); + + anim = (int)(cl.time*10) & 3; + GL_Bind(paliashdr->gl_texturenum[e->skinnum][anim]); + + if (gl_smoothmodels.value) + glShadeModel (GL_SMOOTH); + + glEnable(GL_BLEND); + glDisable (GL_ALPHA_TEST); + glDepthMask(GL_FALSE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + IgnoreInterpolatioFrame(e, paliashdr); + R_SetupAliasBlendedFrame (currententity->frame, paliashdr, e); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); + + glShadeModel (GL_FLAT); + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + glPopMatrix (); + + if (r_shadows.value) + { + glPushMatrix (); + R_RotateForEntity (e, e->scale); + glDisable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + glColor4f (0,0,0,0.5); + GL_DrawAliasShadow (paliashdr, lastposenum); + glEnable (GL_TEXTURE_2D); + glDisable (GL_BLEND); + glColor4f (1,1,1,1); + glPopMatrix (); + } +} + +/* +================= +R_DrawAliasModel + +================= +*/ +int doZHack; +extern int zombie_skins[4]; +void R_DrawAliasModel (entity_t *e) +{ + char specChar; + int i, j; + int lnum; + vec3_t dist; + float add; + model_t *clmodel; + vec3_t mins, maxs; + aliashdr_t *paliashdr; + trivertx_t *verts, *v; + int index; + float s, t, an; + int anim; + + clmodel = currententity->model; + + VectorAdd (currententity->origin, clmodel->mins, mins); + VectorAdd (currententity->origin, clmodel->maxs, maxs); + +// naievil -- fixme: on psp this is == 2 ? + if (R_CullBox (mins, maxs)) + return; + + specChar = clmodel->name[strlen(clmodel->name) - 5]; + + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + for(int g = 0; g < 3; g++) + { + if(lightcolor[g] < 8) + lightcolor[g] = 8; + if(lightcolor[g] > 125) + lightcolor[g] = 125; + } + + // + // get lighting information + // + + ambientlight = shadelight = R_LightPoint (currententity->origin); + + // allways give the gun some light + if (e == &cl.viewent && ambientlight < 24) + ambientlight = shadelight = 24; + + for (lnum=0 ; lnum= cl.time) + { + VectorSubtract (currententity->origin, + cl_dlights[lnum].origin, + dist); + add = cl_dlights[lnum].radius - Length(dist); + + if (add > 0) { + ambientlight += add; + //ZOID models should be affected by dlights as well + shadelight += add; + } + } + } + + // clamp lighting so it doesn't overbright as much + if (ambientlight > 128) + ambientlight = 128; + if (ambientlight + shadelight > 192) + shadelight = 192 - ambientlight; + + // ZOID: never allow players to go totally black + i = currententity - cl_entities; + if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "models/player.mdl") */) + if (ambientlight < 8) + ambientlight = shadelight = 8; + + // HACK HACK HACK -- no fullbright colors, so make torches full light + if (!strcmp (clmodel->name, "progs/flame2.mdl") + || !strcmp (clmodel->name, "progs/flame.mdl") ) + ambientlight = shadelight = 256; + + shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + shadelight = shadelight / 200.0; + + an = e->angles[1]/180*M_PI; + shadevector[0] = cos(-an); + shadevector[1] = sin(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + + // + // locate the proper data + // + if(doZHack && specChar == '%') + { + if(clmodel->name[12] == 'c') + paliashdr = (aliashdr_t *) Mod_Extradata(Mod_FindName("models/ai/zcfull.mdl")); + else + paliashdr = (aliashdr_t *) Mod_Extradata(Mod_FindName("models/ai/zfull.mdl")); + } + else + paliashdr = (aliashdr_t *)Mod_Extradata (e->model); + + c_alias_polys += paliashdr->numtris; + + // + // draw all the triangles + // + + GL_DisableMultitexture(); + + //Shpuld + if(r_model_brightness.value) + { + lightcolor[0] += 60; + lightcolor[1] += 60; + lightcolor[2] += 60; + } + + if(specChar == '!' || (e->effects & EF_FULLBRIGHT)) + { + lightcolor[0] = lightcolor[1] = lightcolor[2] = 256; + } + + add = 72.0f - (lightcolor[0] + lightcolor[1] + lightcolor[2]); + if (add > 0.0f) + { + lightcolor[0] += add / 3.0f; + lightcolor[1] += add / 3.0f; + lightcolor[2] += add / 3.0f; + } + + glPushMatrix (); + R_RotateForEntity (e, ENTSCALE_DEFAULT); + + if (!strcmp (clmodel->name, "progs/eyes.mdl") && gl_doubleeyes.value) { + glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8)); +// double size of eyes, since they are really hard to see in gl + glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2); + } else { + + // Special handling of view model to keep FOV from altering look. Pretty good. Not perfect but rather close. + if ((e == &cl.viewent || e == &cl.viewent2) && scr_fov_viewmodel.value) { + float scale = 1.0f / tan (DEG2RAD (scr_fov.value / 2.0f)) * scr_fov_viewmodel.value / 90.0f; + if (e->scale != ENTSCALE_DEFAULT && e->scale != 0) scale *= ENTSCALE_DECODE(e->scale); + glTranslatef (paliashdr->scale_origin[0] * scale, paliashdr->scale_origin[1], paliashdr->scale_origin[2]); + glScalef (paliashdr->scale[0] * scale, paliashdr->scale[1], paliashdr->scale[2]); + } else { + float scale = 1.0f; + if (e->scale != ENTSCALE_DEFAULT && e->scale != 0) scale *= ENTSCALE_DECODE(e->scale); + glTranslatef (paliashdr->scale_origin[0] * scale, paliashdr->scale_origin[1] * scale, paliashdr->scale_origin[2] * scale); + glScalef (paliashdr->scale[0] * scale, paliashdr->scale[1] * scale, paliashdr->scale[2] * scale); + } + + } + + if (specChar == '%')//Zombie body + { + switch(e->skinnum) + { + case 0: + GL_Bind(zombie_skins[0]); + break; + case 1: + GL_Bind(zombie_skins[1]); + break; + case 2: + GL_Bind(zombie_skins[2]); + break; + case 3: + GL_Bind(zombie_skins[3]); + break; + default: //out of bounds? assuming 0 + Con_Printf("Zombie tex out of bounds: Tex[%i]\n",e->skinnum); + GL_Bind(zombie_skins[0]); + break; + } + } + else + { + anim = (int)(cl.time*10) & 3; + GL_Bind(paliashdr->gl_texturenum[e->skinnum][anim]); + } + + // we can't dynamically colormap textures, so they are cached + // seperately for the players. Heads are just uncolored. + if (currententity->colormap != vid.colormap && !gl_nocolors.value) + { + i = currententity - cl_entities; + if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "models/player.mdl") */) + GL_Bind(playertextures - 1 + i); + } + + if (gl_smoothmodels.value) + glShadeModel (GL_SMOOTH); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + IgnoreInterpolatioFrame(e, paliashdr); + R_SetupAliasBlendedFrame (currententity->frame, paliashdr, e); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glShadeModel (GL_FLAT); + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + glPopMatrix (); + + if (doZHack == 0 && specChar == '%')//if we're drawing zombie, also draw its limbs in one call + { + if(e->z_head) + R_DrawZombieLimb(e,1); + if(e->z_larm) + R_DrawZombieLimb(e,2); + if(e->z_rarm) + R_DrawZombieLimb(e,3); + } + + if (r_shadows.value) + { + glPushMatrix (); + R_RotateForEntity (e, e->scale); + glDisable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + glColor4f (0,0,0,0.5); + GL_DrawAliasShadow (paliashdr, lastposenum); + glEnable (GL_TEXTURE_2D); + glDisable (GL_BLEND); + glColor4f (1,1,1,1); + glPopMatrix (); + } + +} + +//================================================================================== + +/* +============= +R_DrawEntitiesOnList +============= +*/ +void R_DrawEntitiesOnList (void) +{ + int i; + + if (!r_drawentities.value) + return; + + int zHackCount = 0; + doZHack = 0; + char specChar; + + // draw sprites seperately, because of alpha blending + for (i=0 ; imodel->name[strlen(currententity->model->name)-5]; + + if(specChar == '(' || specChar == '^')//skip heads and arms: it's faster to do this than a strcmp... + { + continue; + } + doZHack = 0; + if(specChar == '%') + { + if(zHackCount > 5 || ((currententity->z_head != 0) && (currententity->z_larm != 0) && (currententity->z_rarm != 0))) + { + doZHack = 1; + } + else + { + zHackCount ++;//drawing zombie piece by piece. + } + } + + switch (currententity->model->type) + { + case mod_alias: + if(specChar == '$')//This is for smooth alpha, draw in the following loop, not this one + { + continue; + } + R_DrawAliasModel (currententity); + break; + + case mod_brush: + R_DrawBrushModel (currententity); + break; + + default: + break; + } + doZHack = 0; + } + + for (i=0 ; imodel)) + { + continue; + } + + specChar = currententity->model->name[strlen(currententity->model->name)-5]; + + switch (currententity->model->type) + { + case mod_sprite: + R_DrawSpriteModel (currententity); + break; + case mod_alias: + if(specChar == '$')//mdl model with blended alpha + { + R_DrawTransparentAliasModel(currententity); + } + break; + default: break; + } + } +} + +/* +============= +R_DrawView2Model +============= +*/ +void R_DrawView2Model (void) +{ + float ambient[4], diffuse[4]; + int j; + int lnum; + vec3_t dist; + float add; + dlight_t *dl; + int ambientlight, shadelight; + + if (!r_drawviewmodel.value) + return; + + if (chase_active.value) + return; + + if (envmap) + return; + + if (!r_drawentities.value) + return; + + if (cl.stats[STAT_HEALTH] <= 0) + return; + + currententity = &cl.viewent2; + if (!currententity->model) + return; + + j = R_LightPoint (currententity->origin); + + if (j < 24) + j = 24; // allways give some light on gun + ambientlight = j; + shadelight = j; + +// add dynamic lights + for (lnum=0 ; lnumradius) + continue; + if (!dl->radius) + continue; + if (dl->die < cl.time) + continue; + + VectorSubtract (currententity->origin, dl->origin, dist); + add = dl->radius - Length(dist); + if (add > 0) + ambientlight += add; + } + + ambient[0] = ambient[1] = ambient[2] = ambient[3] = (float)ambientlight / 128; + diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = (float)shadelight / 128; + + // hack the depth range to prevent view model from poking into walls + glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); + R_DrawAliasModel (currententity); + glDepthRange (gldepthmin, gldepthmax); +} + + + +/* +============= +R_DrawViewModel +============= +*/ +void R_DrawViewModel (void) +{ + float ambient[4], diffuse[4]; + int j; + int lnum; + vec3_t dist; + float add; + dlight_t *dl; + int ambientlight, shadelight; + + if (!r_drawviewmodel.value) + return; + + if (chase_active.value) + return; + + if (envmap) + return; + + if (!r_drawentities.value) + return; + + if (cl.stats[STAT_HEALTH] <= 0) + return; + + currententity = &cl.viewent; + if (!currententity->model) + return; + + j = R_LightPoint (currententity->origin); + + if (j < 24) + j = 24; // allways give some light on gun + ambientlight = j; + shadelight = j; + +// add dynamic lights + for (lnum=0 ; lnumradius) + continue; + if (!dl->radius) + continue; + if (dl->die < cl.time) + continue; + + VectorSubtract (currententity->origin, dl->origin, dist); + add = dl->radius - Length(dist); + if (add > 0) + ambientlight += add; + } + + ambient[0] = ambient[1] = ambient[2] = ambient[3] = (float)ambientlight / 128; + diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = (float)shadelight / 128; + + // hack the depth range to prevent view model from poking into walls + glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); + R_DrawAliasModel (currententity); + glDepthRange (gldepthmin, gldepthmax); +} + + +/* +============ +R_PolyBlend +============ +*/ +void R_PolyBlend (void) +{ + if (!gl_polyblend.value) + return; + if (!v_blend[3]) + return; + + GL_DisableMultitexture(); + + glDisable (GL_ALPHA_TEST); + glEnable (GL_BLEND); + glDisable (GL_DEPTH_TEST); + glDisable (GL_TEXTURE_2D); + + glLoadIdentity (); + + glRotatef (-90, 1, 0, 0); // put Z going up + glRotatef (90, 0, 0, 1); // put Z going up + + glColor4fv (v_blend); + + glBegin (GL_QUADS); + + glVertex3f (10, 100, 100); + glVertex3f (10, -100, 100); + glVertex3f (10, -100, -100); + glVertex3f (10, 100, -100); + glEnd (); + + glDisable (GL_BLEND); + glEnable (GL_TEXTURE_2D); + glEnable (GL_ALPHA_TEST); +} + + +int SignbitsForPlane (mplane_t *out) +{ + int bits, j; + + // for fast box on planeside test + + bits = 0; + for (j=0 ; j<3 ; j++) + { + if (out->normal[j] < 0) + bits |= 1< 1) + Cvar_Set ("r_fullbright", "0"); + + R_AnimateLight (); + + r_framecount++; + +// build the transformation matrix for the given view angles + 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); + + V_SetContentsColor (r_viewleaf->contents); + V_CalcBlend (); + + r_cache_thrash = false; + + c_brush_polys = 0; + c_alias_polys = 0; + +} + + +void MYgluPerspective( GLdouble fovy, GLdouble aspect, + GLdouble zNear, GLdouble zFar ) +{ + GLdouble xmin, xmax, ymin, ymax; + + ymax = zNear * tan( fovy * M_PI / 360.0 ); + ymin = -ymax; + + xmin = ymin * aspect; + xmax = ymax * aspect; + + glFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); +} + + +/* +============= +R_SetupGL +============= +*/ +void R_SetupGL (void) +{ + float screenaspect; + float yfov; + int i; + extern int glwidth, glheight; + int x, x2, y2, y, w, h; + + // + // set up viewpoint + // + glMatrixMode(GL_PROJECTION); + glLoadIdentity (); + x = r_refdef.vrect.x * glwidth/vid.width; + x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width; + y = (vid.height-r_refdef.vrect.y) * glheight/vid.height; + y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height; + + // fudge around because of frac screen scale + if (x > 0) + x--; + if (x2 < glwidth) + x2++; + if (y2 < 0) + y2--; + if (y < glheight) + y++; + + w = x2 - x; + h = y - y2; + + if (envmap) + { + x = y2 = 0; + w = h = 256; + } + + glViewport (glx + x, gly + y2, w, h); + screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height; + + gluPerspective (r_refdef.fov_y, screenaspect, 4, 4096); + + if (mirror) + { + if (mirror_plane->normal[2]) + glScalef (1, -1, 1); + else + glScalef (-1, 1, 1); + glCullFace(GL_BACK); + } + else + glCullFace(GL_FRONT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity (); + + glRotatef (-90, 1, 0, 0); // put Z going up + glRotatef (90, 0, 0, 1); // put Z going up + glRotatef (-r_refdef.viewangles[2], 1, 0, 0); + glRotatef (-r_refdef.viewangles[0], 0, 1, 0); + glRotatef (-r_refdef.viewangles[1], 0, 0, 1); + glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); + + glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix); + + // + // set drawing parms + // + if (gl_cull.value) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); + + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); +} + +/* +================ +R_RenderScene + +r_refdef must be set before the first call +================ +*/ +void R_RenderScene (void) +{ + R_SetupFrame (); + + R_SetFrustum (); + + R_SetupGL (); + + R_MarkLeaves (); // done here so we know if we're in water + + R_DrawWorld (); // adds static entities to the list + + S_ExtraUpdate (); // don't let sound get messed up if going slow + + R_DrawEntitiesOnList (); + + GL_DisableMultitexture(); + + //R_RenderDlights (); + + R_DrawParticles (); +} + + +/* +============= +R_Clear +============= +*/ +void R_Clear (void) +{ + if (r_mirroralpha.value != 1.0) + { + if (gl_clear.value) + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear (GL_DEPTH_BUFFER_BIT); + gldepthmin = 0; + gldepthmax = 0.5; + glDepthFunc (GL_LEQUAL); + } + else if (gl_ztrick.value) + { + static int trickframe; + + if (gl_clear.value) + glClear (GL_COLOR_BUFFER_BIT); + + trickframe++; + if (trickframe & 1) + { + gldepthmin = 0; + gldepthmax = 0.49999; + glDepthFunc (GL_LEQUAL); + } + else + { + gldepthmin = 1; + gldepthmax = 0.5; + glDepthFunc (GL_GEQUAL); + } + } + else + { + if (gl_clear.value) + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear (GL_DEPTH_BUFFER_BIT); + gldepthmin = 0; + gldepthmax = 1; + glDepthFunc (GL_LEQUAL); + } + + glDepthRange (gldepthmin, gldepthmax); +} + +/* +============= +R_Mirror +============= +*/ +void R_Mirror (void) +{ + float d; + msurface_t *s; + entity_t *ent; + + if (!mirror) + return; + + memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); + + d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; + VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); + + d = DotProduct (vpn, mirror_plane->normal); + VectorMA (vpn, -2*d, mirror_plane->normal, vpn); + + r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; + r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; + r_refdef.viewangles[2] = -r_refdef.viewangles[2]; + + ent = &cl_entities[cl.viewentity]; + if (cl_numvisedicts < MAX_VISEDICTS) + { + cl_visedicts[cl_numvisedicts] = ent; + cl_numvisedicts++; + } + + gldepthmin = 0.5; + gldepthmax = 1; + glDepthRange (gldepthmin, gldepthmax); + glDepthFunc (GL_LEQUAL); + + R_RenderScene (); + R_DrawWaterSurfaces (); + + gldepthmin = 0; + gldepthmax = 0.5; + glDepthRange (gldepthmin, gldepthmax); + glDepthFunc (GL_LEQUAL); + + // blend on top + glEnable (GL_BLEND); + glMatrixMode(GL_PROJECTION); + if (mirror_plane->normal[2]) + glScalef (1,-1,1); + else + glScalef (-1,1,1); + glCullFace(GL_FRONT); + glMatrixMode(GL_MODELVIEW); + + glLoadMatrixf (r_base_world_matrix); + + glColor4f (1,1,1,r_mirroralpha.value); + s = cl.worldmodel->textures[mirrortexturenum]->texturechain; + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; + glDisable (GL_BLEND); + glColor4f (1,1,1,1); +} + +/* +================ +R_RenderView + +r_refdef must be set before the first call +================ +*/ +void R_RenderView (void) +{ + double time1, time2; + GLfloat colors[4] = {(GLfloat) 0.0, (GLfloat) 0.0, (GLfloat) 1, (GLfloat) 0.20}; + + if (r_norefresh.value) + return; + + if (!r_worldentity.model || !cl.worldmodel) + Sys_Error ("R_RenderView: NULL worldmodel"); + + if (r_speeds.value) + { + glFinish (); + time1 = Sys_FloatTime (); + c_brush_polys = 0; + c_alias_polys = 0; + } + + mirror = false; + + if (gl_finish.value) + glFinish (); + + R_Clear (); + + // render normal view + + Fog_EnableGFog (); //johnfitz + + R_RenderScene (); + R_DrawViewModel (); + R_DrawView2Model (); + R_DrawWaterSurfaces (); + + Fog_DisableGFog (); //johnfitz + + // render mirror view + R_Mirror (); + + R_PolyBlend (); + + if (r_speeds.value) + { +// glFinish (); + time2 = Sys_FloatTime (); + Con_Printf ("%3i ms %4i wpoly %4i epoly\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys); + } +} diff --git a/source/ctr/gl/gl_rmisc.c b/source/ctr/gl/gl_rmisc.c new file mode 100644 index 0000000..f09d4e1 --- /dev/null +++ b/source/ctr/gl/gl_rmisc.c @@ -0,0 +1,516 @@ +/* +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 "../../quakedef.h" + +extern cvar_t r_flatlightstyles; + +int decal_blood1, decal_blood2, decal_blood3, decal_q3blood, decal_burn, decal_mark, decal_glow; +int zombie_skins[4]; + +/* +================== +R_InitOtherTextures +================== +*/ +void R_InitOtherTextures (void) +{ + //static decals + decal_blood1 = loadtextureimage ("textures/decals/blood_splat01", 0, 0, qfalse, qtrue); + decal_blood2 = loadtextureimage ("textures/decals/blood_splat02", 0, 0, qfalse, qtrue); + decal_blood3 = loadtextureimage ("textures/decals/blood_splat03", 0, 0, qfalse, qtrue); + decal_q3blood = loadtextureimage ("textures/decals/blood_stain", 0, 0, qfalse, qtrue); + decal_burn = loadtextureimage ("textures/decals/explo_burn01", 0, 0, qfalse, qtrue); + decal_mark = loadtextureimage ("textures/decals/particle_burn01", 0, 0, qfalse, qtrue); + decal_glow = loadtextureimage ("textures/decals/glow2", 0, 0, qfalse, qtrue); + + // external zombie skins + zombie_skins[0] = loadtextureimage ("models/ai/zfull.mdl_0", 0, 0, qtrue, qfalse); + zombie_skins[1] = loadtextureimage ("models/ai/zfull.mdl_1", 0, 0, qtrue, qfalse); + zombie_skins[2] = loadtextureimage ("models/ai/zfull.mdl_2", 0, 0, qtrue, qfalse); + zombie_skins[3] = loadtextureimage ("models/ai/zfull.mdl_3", 0, 0, qtrue, qfalse); +} + +/* +================== +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; + } + } +} + +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}, +}; +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 +=============== +*/ +void R_Envmap_f (void) +{ + byte buffer[256*256*4]; + char name[1024]; + + 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 (); +} + +/* +=============== +R_Init +=============== +*/ +extern bool new3ds_flag; +void R_Init (void) +{ + extern byte *hunk_base; + extern cvar_t gl_finish; + + Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); + Cmd_AddCommand ("envmap", R_Envmap_f); + Cmd_AddCommand ("pointfile", R_ReadPointFile_f); + + Cvar_RegisterVariable (&r_norefresh); + Cvar_RegisterVariable (&r_lightmap); + Cvar_RegisterVariable (&r_fullbright); + Cvar_RegisterVariable (&r_drawentities); + Cvar_RegisterVariable (&r_drawviewmodel); + Cvar_RegisterVariable (&r_shadows); + Cvar_RegisterVariable (&r_mirroralpha); + Cvar_RegisterVariable (&r_wateralpha); + Cvar_RegisterVariable (&r_dynamic); + Cvar_RegisterVariable (&r_novis); + Cvar_RegisterVariable (&r_speeds); + + Cvar_RegisterVariable (&r_farclip); + + Cvar_RegisterVariable (&gl_finish); + Cvar_RegisterVariable (&gl_clear); + Cvar_RegisterVariable (&gl_texsort); + + Cvar_SetValue("gl_clear", 1); + + if (gl_mtexable) + Cvar_SetValue ("gl_texsort", 0.0); + + 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); + + Cvar_RegisterVariable (&r_explosiontype); + Cvar_RegisterVariable (&r_laserpoint); + Cvar_RegisterVariable (&r_part_explosions); + Cvar_RegisterVariable (&r_part_trails); + Cvar_RegisterVariable (&r_part_sparks); + Cvar_RegisterVariable (&r_part_gunshots); + Cvar_RegisterVariable (&r_part_blood); + Cvar_RegisterVariable (&r_part_telesplash); + Cvar_RegisterVariable (&r_part_blobs); + Cvar_RegisterVariable (&r_part_lavasplash); + Cvar_RegisterVariable (&r_part_flames); + Cvar_RegisterVariable (&r_part_lightning); + Cvar_RegisterVariable (&r_part_flies); + Cvar_RegisterVariable (&r_part_muzzleflash); + Cvar_RegisterVariable (&r_flametype); + Cvar_RegisterVariable (&r_model_brightness); + Cvar_RegisterVariable (&r_skyfog); + + Cvar_RegisterVariable (&r_flatlightstyles); + + R_InitParticles (); + R_InitOtherTextures (); + R_InitParticleTexture (); + + Sky_Init (); //johnfitz + Fog_Init (); //johnfitz + +#ifdef GLTEST + Test_Init (); +#endif + + playertextures = texture_extension_number; + texture_extension_number += 16; + + if (new3ds_flag == true) + Cvar_SetValue("r_dynamic", 1); + else + Cvar_SetValue("r_dynamic", 0); +} + +/* +=============== +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; + extern byte **player_8bit_texels_tbl; + + GL_DisableMultitexture(); + + top = 0xf0; + bottom = (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 ("R_TranslateSkin: s&3"); + + 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 ; iskinwidth, paliashdr->skinheight, false, false, true); +#else + scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512; + scaled_height = gl_max_size.value < 256 ? gl_max_size.value : 256; + + // allow users to crunch sizes down even more if they want + scaled_width >>= (int)gl_playermip.value; + scaled_height >>= (int)gl_playermip.value; + + 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> 1; + for (j=0 ; j>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> 1; + for (j=0 ; j>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 ; inumleafs ; i++) + cl.worldmodel->leafs[i].efrags = NULL; + + r_viewleaf = NULL; + R_ClearParticles (); + + GL_BuildLightmaps (); + + Sky_NewMap (); //johnfitz -- skybox in worldspawn + Fog_NewMap (); // johnfitz -- global fog in worldspawn + + // identify sky texture + skytexturenum = -1; + mirrortexturenum = -1; + for (i=0 ; inumtextures ; 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_LoadSkys (); +} + + +/* +==================== +R_TimeRefresh_f + +For program optimization +==================== +*/ +void R_TimeRefresh_f (void) +{ + int i; + float start, stop, time; + int startangle; + vrect_t vr; + + glDrawBuffer (GL_FRONT); + glFinish (); + + start = Sys_FloatTime (); + for (i=0 ; i<128 ; i++) + { + r_refdef.viewangles[1] = i/128.0*360.0; + R_RenderView (); + } + + glFinish (); + stop = Sys_FloatTime (); + time = stop-start; + Con_Printf ("%f seconds (%f fps)\n", time, 128/time); + + glDrawBuffer (GL_BACK); + GL_EndRendering (); +} + +void D_FlushCaches (void) +{ +} + + diff --git a/source/ctr/gl/gl_rsurf.c b/source/ctr/gl/gl_rsurf.c new file mode 100644 index 0000000..4899df6 --- /dev/null +++ b/source/ctr/gl/gl_rsurf.c @@ -0,0 +1,1814 @@ +/* +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_surf.c: surface-related refresh code + +#include "../../quakedef.h" + +#ifndef GL_RGBA4 +#define GL_RGBA4 0 +#endif + + +int lightmap_bytes; // 1, 2, or 4 + +int lightmap_textures; + +unsigned blocklights[3*18*18]; // LordHavoc: .lit support (*3 for RGB) to the definitions at the top + +#define BLOCK_WIDTH 128 +#define BLOCK_HEIGHT 128 + +#define MAX_LIGHTMAPS 64 +int active_lightmaps; + +typedef struct glRect_s { + unsigned char l,t,w,h; +} glRect_t; + +glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; +qboolean lightmap_modified[MAX_LIGHTMAPS]; +glRect_t lightmap_rectchange[MAX_LIGHTMAPS]; + +int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; + +// the lightmap texture data needs to be kept in +// main memory so texsubimage can update properly +byte lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT]; + +// For gl_texsort 0 +msurface_t *skychain = NULL; +msurface_t *waterchain = NULL; + +extern char skybox_name[32]; + +void R_RenderDynamicLightmaps (msurface_t *fa); + +/* +=============== +R_AddDynamicLights +=============== +*/ +void R_AddDynamicLights (msurface_t *surf) +{ + int lnum; + int sd, td; + float dist, rad, minlight; + vec3_t impact, local; + int s, t; + int i; + int smax, tmax; + mtexinfo_t *tex; + //johnfitz -- lit support via lordhavoc + float cred, cgreen, cblue, brightness; + unsigned *bl; + //johnfitz + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnumdlightbits[lnum >> 5] & (1U << (lnum & 31)))) + continue; // not lit by this light + + rad = cl_dlights[lnum].radius; + dist = DotProduct (cl_dlights[lnum].origin, surf->plane->normal) - + surf->plane->dist; + rad -= fabs(dist); + minlight = cl_dlights[lnum].minlight; + if (rad < minlight) + continue; + minlight = rad - minlight; + + for (i=0 ; i<3 ; i++) + { + impact[i] = cl_dlights[lnum].origin[i] - + surf->plane->normal[i]*dist; + } + + local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; + local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; + + local[0] -= surf->texturemins[0]; + local[1] -= surf->texturemins[1]; + + //johnfitz -- lit support via lordhavoc + bl = blocklights; + cred = cl_dlights[lnum].color[0] * 256.0f; + cgreen = cl_dlights[lnum].color[1] * 256.0f; + cblue = cl_dlights[lnum].color[2] * 256.0f; + //johnfitz + for (t = 0 ; t td) + dist = sd + (td>>1); + else + dist = td + (sd>>1); + if (dist < minlight) + //johnfitz -- lit support via lordhavoc + { + brightness = rad - dist; + bl[0] += (int) (brightness * cred); + bl[1] += (int) (brightness * cgreen); + bl[2] += (int) (brightness * cblue); + } + bl += 3; + //johnfitz + } + } + } +} + + +/* +=============== +R_BuildLightMap + +Combine and scale multiple lightmaps into the 8.8 format in blocklights +=============== +*/ +void R_BuildLightMap (msurface_t *surf, byte *dest, int stride) +{ + int blocksize, smax, tmax; + int t; + int i, j, size; + byte *lightmap; + unsigned scale; + int maps; + int lightadj[4]; + unsigned *bl; + + surf->cached_dlight = (surf->dlightframe == r_framecount); + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + size = smax*tmax; + lightmap = surf->samples; + +// set to full bright if no light data + if (r_fullbright.value || !cl.worldmodel->lightdata) + { + // LordHavoc: .lit support begin + bl = blocklights; + for (i=0 ; istyles[maps] != 255; maps++) + { + scale = (float)d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + // LordHavoc: .lit support begin + bl = blocklights; + for (i=0 ; idlightframe == r_framecount) + R_AddDynamicLights (surf); + +// bound, invert, and shift +store: + stride -= (smax<<2); + bl = blocklights; + for (i=0 ; i> 7;if (t > 255) t = 255;*dest++ = t; + t = bl[1] >> 7;if (t > 255) t = 255;*dest++ = t; + t = bl[2] >> 7;if (t > 255) t = 255;*dest++ = t; + bl += 3; + *dest++ = 255; + // LordHavoc: .lit support end + } + } + +} + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and base texture +=============== +*/ +texture_t *R_TextureAnimation (texture_t *base) +{ + int reletive; + int count; + + if (currententity->frame) + { + if (base->alternate_anims) + base = base->alternate_anims; + } + + if (!base->anim_total) + return base; + + reletive = (int)(cl.time*10) % base->anim_total; + + count = 0; + while (base->anim_min > reletive || base->anim_max <= reletive) + { + base = base->anim_next; + if (!base) + Sys_Error ("R_TextureAnimation: broken cycle"); + if (++count > 100) + Sys_Error ("R_TextureAnimation: infinite cycle"); + } + + return base; +} + + +/* +============================================================= + + BRUSH MODELS + +============================================================= +*/ + + +extern int solidskytexture; +extern int alphaskytexture; +extern float speedscale; // for top sky and bottom sky + +void DrawGLWaterPoly (glpoly_t *p); +void DrawGLWaterPolyLightmap (glpoly_t *p); + +lpMTexFUNC qglMTexCoord2fSGIS = NULL; +lpSelTexFUNC qglSelectTextureSGIS = NULL; + +qboolean mtexenabled = false; + +void GL_SelectTexture (GLenum target); + +void GL_DisableMultitexture(void) +{ + if (mtexenabled) { + glDisable(GL_TEXTURE_2D); + GL_SelectTexture(TEXTURE0_SGIS); + mtexenabled = false; + } +} + +void GL_EnableMultitexture(void) +{ + if (gl_mtexable) { + GL_SelectTexture(TEXTURE1_SGIS); + glEnable(GL_TEXTURE_2D); + mtexenabled = true; + } +} + +#if 0 +/* +================ +R_DrawSequentialPoly + +Systems that have fast state and texture changes can +just do everything as it passes with no need to sort +================ +*/ +void R_DrawSequentialPoly (msurface_t *s) +{ + glpoly_t *p; + float *v; + int i; + texture_t *t; + + // + // normal lightmaped poly + // + if (! (s->flags & (SURF_DRAWSKY|SURF_DRAWTURB|SURF_UNDERWATER) ) ) + { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + + glDisable (GL_BLEND); + + return; + } + + // + // subdivided water surface warp + // + if (s->flags & SURF_DRAWTURB) + { + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + return; + } + + // + // subdivided sky warp + // + if (s->flags & SURF_DRAWSKY) + { + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale; + + EmitSkyPolys (s); + + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale; + EmitSkyPolys (s); + if (gl_lightmap_format == GL_LUMINANCE) + glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + + glDisable (GL_BLEND); + } + + // + // underwater warped with lightmap + // + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + DrawGLWaterPoly (p); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + DrawGLWaterPolyLightmap (p); + glDisable (GL_BLEND); +} +#else +/* +================ +R_DrawSequentialPoly + +Systems that have fast state and texture changes can +just do everything as it passes with no need to sort +================ +*/ +void R_DrawSequentialPoly (msurface_t *s) +{ + glpoly_t *p; + float *v; + int i; + texture_t *t; + vec3_t nv, dir; + float ss, ss2, length; + float s1, t1; + glRect_t *theRect; + + // + // normal lightmaped poly + // + + if (! (s->flags & (SURF_DRAWSKY|SURF_DRAWTURB|SURF_UNDERWATER) ) ) + { + R_RenderDynamicLightmaps (s); + if (gl_mtexable) { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + // Binds world to texture env 0 + GL_SelectTexture(TEXTURE0_SGIS); + GL_Bind (t->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + // Binds lightmap to texenv 1 + GL_EnableMultitexture(); // Same as SelectTexture (TEXTURE1) + GL_Bind (lightmap_textures + s->lightmaptexturenum); + i = s->lightmaptexturenum; + if (lightmap_modified[i]) + { + lightmap_modified[i] = false; + theRect = &lightmap_rectchange[i]; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); + theRect->l = BLOCK_WIDTH; + theRect->t = BLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + glBegin(GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + qglMTexCoord2fSGIS (TEXTURE0_SGIS, v[3], v[4]); + qglMTexCoord2fSGIS (TEXTURE1_SGIS, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + return; + } else { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + + glDisable (GL_BLEND); + } + + return; + } + + // + // subdivided water surface warp + // + + if (s->flags & SURF_DRAWTURB) + { + GL_DisableMultitexture(); + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + return; + } + + // + // subdivided sky warp + // + if (s->flags & SURF_DRAWSKY) + { + GL_DisableMultitexture(); + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127; + + EmitSkyPolys (s); + + glEnable (GL_BLEND); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127; + EmitSkyPolys (s); + + glDisable (GL_BLEND); + return; + } + + // + // underwater warped with lightmap + // + R_RenderDynamicLightmaps (s); + if (gl_mtexable) { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_SelectTexture(TEXTURE0_SGIS); + GL_Bind (t->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + GL_EnableMultitexture(); + GL_Bind (lightmap_textures + s->lightmaptexturenum); + i = s->lightmaptexturenum; + if (lightmap_modified[i]) + { + lightmap_modified[i] = false; + theRect = &lightmap_rectchange[i]; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); + theRect->l = BLOCK_WIDTH; + theRect->t = BLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + qglMTexCoord2fSGIS (TEXTURE0_SGIS, v[3], v[4]); + qglMTexCoord2fSGIS (TEXTURE1_SGIS, v[5], v[6]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); + + } else { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + DrawGLWaterPoly (p); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + DrawGLWaterPolyLightmap (p); + glDisable (GL_BLEND); + } +} +#endif + + +/* +================ +DrawGLWaterPoly + +Warp the vertex coordinates +================ +*/ +void DrawGLWaterPoly (glpoly_t *p) +{ + int i; + float *v; + float s, t, os, ot; + vec3_t nv; + + GL_DisableMultitexture(); + + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); +} + +void DrawGLWaterPolyLightmap (glpoly_t *p) +{ + int i; + float *v; + float s, t, os, ot; + vec3_t nv; + + GL_DisableMultitexture(); + + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); +} + +/* +================ +DrawGLPoly +================ +*/ +void DrawGLPoly (glpoly_t *p) +{ + int i; + float *v; + + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); +} + +// rbaldwin2 -- This is based on DrawGLWaterPolyLightmap and designed to be cheaper +void DrawGLPolyLightmap (glpoly_t *p) +{ + int i; + float *v; + float s, t, os, ot; + vec3_t nv; + + GL_DisableMultitexture(); + + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + + nv[0] = v[0]; + nv[1] = v[1]; + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); +} + +/* +================ +R_BlendLightmaps +================ +*/ + +void R_BlendLightmaps () +{ + int i, j; + glpoly_t *p; + float *v; + glRect_t *theRect; + + if (r_fullbright.value) { + return; + } + + glEnable(GL_MODULATE); + glColor4f(1,1,1,1); + glDepthMask(GL_FALSE); // don't bother writing Z + + glEnable (GL_BLEND); + glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); + + for (i=0 ; it, + BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); + theRect->l = BLOCK_WIDTH; + theRect->t = BLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + for ( ; p ; p=p->chain) + { + if (p->flags & SURF_UNDERWATER) + DrawGLWaterPolyLightmap (p); + else + { + DrawGLPolyLightmap(p); + } + } + } + + glEnable(GL_REPLACE); + glDisable (GL_BLEND); + glDepthMask (GL_TRUE); // back to normal Z buffering +} + +/* +================ +R_RenderBrushPoly +================ +*/ +void R_RenderBrushPoly (msurface_t *fa) +{ + texture_t *t; + byte *base; + int maps; + glRect_t *theRect; + int smax, tmax; + + c_brush_polys++; + + if (fa->flags & SURF_DRAWSKY) + { + if (strcmp(skybox_name, "") == 0) + EmitBothSkyLayers (fa); + return; + } + + t = R_TextureAnimation (fa->texinfo->texture); + GL_Bind (t->gl_texturenum); + + if (fa->flags & SURF_DRAWTURB) + { // warp texture, no lightmaps + EmitWaterPolys (fa); + return; + } + + if (fa->flags & TEXFLAG_NODRAW) + return; + else if (fa->flags & TEXFLAG_LIGHT) { + DrawGLPoly(fa->polys); + return; + } + + if (fa->flags & SURF_UNDERWATER) + DrawGLWaterPoly (fa->polys); + else + DrawGLPoly (fa->polys); + + // add the poly to the proper lightmap chain + + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + // check for lightmap modification + for (maps = 0 ; maps < MAXLIGHTMAPS && fa->styles[maps] != 255 ; + maps++) + if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) + goto dynamic; + + if (fa->dlightframe == r_framecount // dynamic this frame + || fa->cached_dlight) // dynamic previously + { +dynamic: + if (r_dynamic.value) + { + lightmap_modified[fa->lightmaptexturenum] = true; + theRect = &lightmap_rectchange[fa->lightmaptexturenum]; + if (fa->light_t < theRect->t) { + if (theRect->h) + theRect->h += theRect->t - fa->light_t; + theRect->t = fa->light_t; + } + if (fa->light_s < theRect->l) { + if (theRect->w) + theRect->w += theRect->l - fa->light_s; + theRect->l = fa->light_s; + } + smax = (fa->extents[0]>>4)+1; + tmax = (fa->extents[1]>>4)+1; + if ((theRect->w + theRect->l) < (fa->light_s + smax)) + theRect->w = (fa->light_s-theRect->l)+smax; + if ((theRect->h + theRect->t) < (fa->light_t + tmax)) + theRect->h = (fa->light_t-theRect->t)+tmax; + base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; + base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; + R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); + } + } +} + +/* +================ +R_RenderDynamicLightmaps +Multitexture +================ +*/ +void R_RenderDynamicLightmaps (msurface_t *fa) +{ + texture_t *t; + byte *base; + int maps; + glRect_t *theRect; + int smax, tmax; + + c_brush_polys++; + + if (fa->flags & ( SURF_DRAWSKY | SURF_DRAWTURB) ) + return; + + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + // check for lightmap modification + for (maps = 0 ; maps < MAXLIGHTMAPS && fa->styles[maps] != 255 ; + maps++) + if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) + goto dynamic; + + if (fa->dlightframe == r_framecount // dynamic this frame + || fa->cached_dlight) // dynamic previously + { +dynamic: + if (r_dynamic.value) + { + lightmap_modified[fa->lightmaptexturenum] = true; + theRect = &lightmap_rectchange[fa->lightmaptexturenum]; + if (fa->light_t < theRect->t) { + if (theRect->h) + theRect->h += theRect->t - fa->light_t; + theRect->t = fa->light_t; + } + if (fa->light_s < theRect->l) { + if (theRect->w) + theRect->w += theRect->l - fa->light_s; + theRect->l = fa->light_s; + } + smax = (fa->extents[0]>>4)+1; + tmax = (fa->extents[1]>>4)+1; + if ((theRect->w + theRect->l) < (fa->light_s + smax)) + theRect->w = (fa->light_s-theRect->l)+smax; + if ((theRect->h + theRect->t) < (fa->light_t + tmax)) + theRect->h = (fa->light_t-theRect->t)+tmax; + base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; + base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; + R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); + } + } +} + +/* +================ +R_MirrorChain +================ +*/ +void R_MirrorChain (msurface_t *s) +{ + if (mirror) + return; + mirror = true; + mirror_plane = s->plane; +} + + +#if 0 +/* +================ +R_DrawWaterSurfaces +================ +*/ +void R_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (r_wateralpha.value == 1.0) + return; + + // + // go back to the world matrix + // + glLoadMatrixf (r_world_matrix); + + glEnable (GL_BLEND); + glColor4f (1,1,1,r_wateralpha.value); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + for (i=0 ; inumtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + if ( !(s->flags & SURF_DRAWTURB) ) + continue; + + // set modulate mode explicitly + GL_Bind (t->gl_texturenum); + + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + + t->texturechain = NULL; + } + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glColor4f (1,1,1,1); + glDisable (GL_BLEND); +} +#else +/* +================ +R_DrawWaterSurfaces +================ +*/ +void R_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (r_wateralpha.value == 1.0 && gl_texsort.value) + return; + + // + // go back to the world matrix + // + + glLoadMatrixf (r_world_matrix); + + if (r_wateralpha.value < 1.0) { + glEnable (GL_BLEND); + glColor4f (1,1,1,r_wateralpha.value); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + + if (!gl_texsort.value) { + if (!waterchain) + return; + + for ( s = waterchain ; s ; s=s->texturechain) { + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + } + + waterchain = NULL; + } else { + + for (i=0 ; inumtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + if ( !(s->flags & SURF_DRAWTURB ) ) + continue; + + // set modulate mode explicitly + + GL_Bind (t->gl_texturenum); + + for ( ; s ; s=s->texturechain) + EmitWaterPolys (s); + + t->texturechain = NULL; + } + + } + + if (r_wateralpha.value < 1.0) { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glColor4f (1,1,1,1); + glDisable (GL_BLEND); + } + +} + +#endif + +/* +================ +DrawTextureChains +================ +*/ +void DrawTextureChains (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (!gl_texsort.value) { + GL_DisableMultitexture(); + + if (skychain) { + //R_DrawSkyChain(skychain); + skychain = NULL; + } + + return; + } + + glEnable(GL_ALPHA_TEST); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + for (i=0 ; inumtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + else if (i == mirrortexturenum && r_mirroralpha.value != 1.0) + { + R_MirrorChain (s); + continue; + } + else + { + if ((s->flags & SURF_DRAWTURB) && r_wateralpha.value != 1.0) + continue; // draw translucent water later + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + } + + t->texturechain = NULL; + } + + glDisable(GL_ALPHA_TEST); +} +/* +================= +R_DrawBrushModel +================= +*/ +void R_DrawBrushModel (entity_t *e) +{ + int j, k; + vec3_t mins, maxs; + int i, numsurfaces; + msurface_t *psurf; + float dot; + mplane_t *pplane; + model_t *clmodel; + qboolean rotated; + + currententity = e; + currenttexture = -1; + + clmodel = e->model; + + if (e->angles[0] || e->angles[1] || e->angles[2]) + { + rotated = true; + for (i=0 ; i<3 ; i++) + { + mins[i] = e->origin[i] - clmodel->radius; + maxs[i] = e->origin[i] + clmodel->radius; + } + } + else + { + rotated = false; + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + } + + if (R_CullBox (mins, maxs)) + return; + + glColor4f(1,1,1,1); + memset (lightmap_polys, 0, sizeof(lightmap_polys)); + + VectorSubtract (r_refdef.vieworg, e->origin, modelorg); + if (rotated) + { + vec3_t temp; + vec3_t forward, right, up; + + VectorCopy (modelorg, temp); + AngleVectors (e->angles, forward, right, up); + modelorg[0] = DotProduct (temp, forward); + modelorg[1] = -DotProduct (temp, right); + modelorg[2] = DotProduct (temp, up); + } + + psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; + +// calculate dynamic lighting for bmodel if it's not an +// instanced model + if (clmodel->firstmodelsurface != 0 && !gl_flashblend.value) + { + for (k=0 ; knodes + clmodel->hulls[0].firstclipnode); + } + } + + glPushMatrix (); + + // naievil -- fixme + /* + //Crow_bar half_life render. + if (ISADDITIVE(e)) + { + //Con_DPrintf("ISADDITIVE:brush\n"); + float deg = e->renderamt; + float alpha1 = deg; + float alpha2 = 1 - deg; + if(deg <= 0.7) + sceGuDepthMask(GU_TRUE); + + sceGuEnable (GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, + GU_COLOR(alpha1,alpha1,alpha1,alpha1), + GU_COLOR(alpha2,alpha2,alpha2,alpha2)); + dlight = qfalse; + } + else if (ISSOLID(e)) + { + sceGuEnable(GU_ALPHA_TEST); + int c = (int)(e->renderamt * 255.0f); + sceGuAlphaFunc(GU_GREATER, c, 0xff); + dlight = qfalse; + } + else if (ISGLOW(e)) + { + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + sceGuDepthMask(GU_TRUE); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0xFFFFFFFF); + R_GlowSetupBegin(e); + } + else if (ISTEXTURE(e)) + { + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuColor(GU_RGBA(255, 255, 255, (int)(e->renderamt * 255.0f))); + dlight = qfalse; + } + else if (ISCOLOR(e)) + { + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuColor(GU_RGBA((int)(e->rendercolor[0] * 255.0f), + (int)(e->rendercolor[1] * 255.0f), + (int)(e->rendercolor[2] * 255.0f), 255)); + } + */ + + e->angles[0] = -e->angles[0]; // stupid quake bug + R_RotateForEntity (e, e->scale); + e->angles[0] = -e->angles[0]; // stupid quake bug + + // + // draw texture + // + for (i=0 ; inummodelsurfaces ; 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_RenderBrushPoly (psurf); + } + } + + R_BlendLightmaps(); + glPopMatrix (); +} + +/* +============================================================= + + WORLD MODEL + +============================================================= +*/ + +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode (mnode_t *node) +{ + int i, c, side, *pindex; + vec3_t acceptpt, rejectpt; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double d, dot; + vec3_t mins, maxs; + + if (node->contents == CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + if (R_CullBox (node->minmaxs, node->minmaxs+3)) + return; + +// 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); + + return; + } + +// 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]); + +// draw stuff + c = node->numsurfaces; + + if (c) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + if (dot < 0 -BACKFACE_EPSILON) + side = SURF_PLANEBACK; + else if (dot > BACKFACE_EPSILON) + side = 0; + { + for ( ; c ; c--, surf++) + { + if (surf->visframe != r_framecount) + continue; + + // don't backface underwater surfaces, because they warp + if ( !(surf->flags & SURF_UNDERWATER) && ( (dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) ) + continue; // wrong side + + // if sorting by texture, just store it out + if (gl_texsort.value) + { + if (!mirror + || surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum]) + { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + } /*else if (surf->flags & SURF_DRAWSKY) { + surf->texturechain = skychain; + skychain = surf; + } else if (surf->flags & SURF_DRAWTURB) { + surf->texturechain = waterchain; + waterchain = surf; + } else + R_DrawSequentialPoly (surf);*/ + + } + } + + } + +// recurse down the back side + R_RecursiveWorldNode (node->children[!side]); +} + + +void R_AddBrushModelToChains (entity_t * e) +{ + model_t * clmodel = e->model; + vec3_t mins, maxs; + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + + if (R_CullBox(mins, maxs)) + { + return; + } + + msurface_t * psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; + + /* + if (clmodel->firstmodelsurface != 0) + { + for (int k = 0; k < MAX_DLIGHTS; k++) + { + if ((cl_dlights[k].die < cl.time) || + (!cl_dlights[k].radius)) + continue; + R_MarkLights (&cl_dlights[k], 1<nodes + clmodel->hulls[0].firstclipnode); + } + } + */ + + for (int j = 0; j < clmodel->nummodelsurfaces; j++, psurf++) + { + // find which side of the node we are on + mplane_t * pplane = psurf->plane; + float 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))) + { + // psurf->flags &= ~SURF_NEEDSCLIPPING; + // psurf->flags |= SURF_NEEDSCLIPPING * (frustum_check > 1); + + psurf->texturechain = psurf->texinfo->texture->texturechain; + psurf->texinfo->texture->texturechain = psurf; + } + } +} + +void R_AddStaticBrushModelsToChains () +{ + // Con_Printf("static models %d\n", cl_numstaticbrushmodels); + for (int i = 0; i < cl_numstaticbrushmodels; i++) + { + // if (i >= 1) return; + R_AddBrushModelToChains(cl_staticbrushmodels[i]); + } +} + +/* +============= +R_DrawWorld +============= +*/ +void R_DrawWorld (void) +{ + entity_t ent; + int i; + + memset (&ent, 0, sizeof(ent)); + ent.model = cl.worldmodel; + + VectorCopy (r_refdef.vieworg, modelorg); + + currententity = &ent; + currenttexture = -1; + + glColor3f (1,1,1); + memset (lightmap_polys, 0, sizeof(lightmap_polys)); + + R_ClearSkyBox (); + if (strcmp(skybox_name, "") != 0) + R_DrawSkyBox(); + + R_RecursiveWorldNode (cl.worldmodel->nodes); + + R_AddStaticBrushModelsToChains (); // shpuld + + DrawTextureChains (); + Fog_SetupFrame (/*false*/); //johnfitz + + R_BlendLightmaps(); +} + + +/* +=============== +R_MarkLeaves +=============== +*/ +void R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + byte solid[4096]; + + if (r_oldviewleaf == r_viewleaf && !r_novis.value) + return; + + if (mirror) + return; + + r_visframecount++; + r_oldviewleaf = r_viewleaf; + + if (r_novis.value) + { + vis = solid; + memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); + } + else + vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); + + for (i=0 ; inumleafs ; i++) + { + if (vis[i>>3] & (1<<(i&7))) + { + node = (mnode_t *)&cl.worldmodel->leafs[i+1]; + do + { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + + + +/* +============================================================================= + + LIGHTMAP ALLOCATION + +============================================================================= +*/ + +// returns a texture number and the position inside it +int AllocBlock (int w, int h, int *x, int *y) +{ + int i, j; + int best, best2; + int bestx; + int texnum; + + for (texnum=0 ; texnum= best) + break; + if (allocated[texnum][i+j] > best2) + best2 = allocated[texnum][i+j]; + } + if (j == w) + { // this is a valid spot + *x = i; + *y = best = best2; + } + } + + if (best + h > BLOCK_HEIGHT) + continue; + + for (i=0 ; iedges; + lnumverts = fa->numedges; + vertpage = 0; + + // + // draw texture + // + poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); + poly->next = fa->polys; + poly->flags = fa->flags; + fa->polys = poly; + poly->numverts = lnumverts; + + for (i=0 ; isurfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + vec = r_pcurrentvertbase[r_pedge->v[0]].position; + } + else + { + r_pedge = &pedges[-lindex]; + vec = r_pcurrentvertbase[r_pedge->v[1]].position; + } + s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; + s /= fa->texinfo->texture->width; + + t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; + t /= fa->texinfo->texture->height; + + VectorCopy (vec, poly->verts[i]); + poly->verts[i][3] = s; + poly->verts[i][4] = t; + + // + // lightmap texture coordinates + // + s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; + s -= fa->texturemins[0]; + s += fa->light_s*16; + s += 8; + s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width; + + t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; + t -= fa->texturemins[1]; + t += fa->light_t*16; + t += 8; + t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height; + + poly->verts[i][5] = s; + poly->verts[i][6] = t; + } + + // + // remove co-linear points - Ed + // + if (!gl_keeptjunctions.value && !(fa->flags & SURF_UNDERWATER) ) + { + for (i = 0 ; i < lnumverts ; ++i) + { + vec3_t v1, v2; + float *prev, *this, *next; + float f; + + prev = poly->verts[(i + lnumverts - 1) % lnumverts]; + this = poly->verts[i]; + next = poly->verts[(i + 1) % lnumverts]; + + VectorSubtract( this, prev, v1 ); + VectorNormalize( v1 ); + VectorSubtract( next, prev, v2 ); + VectorNormalize( v2 ); + + // skip co-linear points + #define COLINEAR_EPSILON 0.001 + if ((fabs( v1[0] - v2[0] ) <= COLINEAR_EPSILON) && + (fabs( v1[1] - v2[1] ) <= COLINEAR_EPSILON) && + (fabs( v1[2] - v2[2] ) <= COLINEAR_EPSILON)) + { + int j; + for (j = i + 1; j < lnumverts; ++j) + { + int k; + for (k = 0; k < VERTEXSIZE; ++k) + poly->verts[j - 1][k] = poly->verts[j][k]; + } + --lnumverts; + ++nColinElim; + // retry next vertex next time, which is now current vertex + --i; + } + } + } + poly->numverts = lnumverts; + +} + +/* +======================== +GL_CreateSurfaceLightmap +======================== +*/ +void GL_CreateSurfaceLightmap (msurface_t *surf) +{ + int smax, tmax, s, t, l, i; + byte *base; + + if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) + return; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + + surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); + base = lightmaps + surf->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; + base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; + R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes); +} + + +/* +================== +GL_BuildLightmaps + +Builds the lightmap texture +with all the surfaces from all brush models +================== +*/ +void GL_BuildLightmaps (void) +{ + int i, j; + model_t *m; + extern qboolean isPermedia; + + memset (allocated, 0, sizeof(allocated)); + + r_framecount = 1; // no dlightcache + + if (!lightmap_textures) + { + lightmap_textures = texture_extension_number; + texture_extension_number += MAX_LIGHTMAPS; + } + + gl_lightmap_format = GL_LUMINANCE; + // default differently on the Permedia + if (isPermedia) + gl_lightmap_format = GL_RGBA; + + if (COM_CheckParm ("-lm_1")) + gl_lightmap_format = GL_LUMINANCE; + if (COM_CheckParm ("-lm_a")) + gl_lightmap_format = GL_ALPHA; + if (COM_CheckParm ("-lm_i")) + gl_lightmap_format = GL_INTENSITY; + if (COM_CheckParm ("-lm_2")) + gl_lightmap_format = GL_RGBA4; + if (COM_CheckParm ("-lm_4")) + gl_lightmap_format = GL_RGBA; + + switch (gl_lightmap_format) + { + case GL_RGBA: + lightmap_bytes = 4; + break; + case GL_RGBA4: + lightmap_bytes = 2; + break; + case GL_LUMINANCE: + case GL_INTENSITY: + case GL_ALPHA: + lightmap_bytes = 1; + break; + } + + for (j=1 ; jname[0] == '*') + continue; + r_pcurrentvertbase = m->vertexes; + currentmodel = m; + for (i=0 ; inumsurfaces ; i++) + { + GL_CreateSurfaceLightmap (m->surfaces + i); + if ( m->surfaces[i].flags & SURF_DRAWTURB ) + continue; + + if ( m->surfaces[i].flags & SURF_DRAWSKY ) + continue; + + BuildSurfaceDisplayList (m->surfaces + i); + } + } + + if (!gl_texsort.value) + GL_SelectTexture(TEXTURE1_SGIS); + + // + // upload all lightmaps that were filled + // + for (i=0 ; i 126) + x += 8; + else + x += (font_kerningamount[(int)(start[j] - 33)] + 1); + + 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 (); +} + +/* +=============================================================================== + +Press somthing printing + +=============================================================================== +*/ + +char scr_usestring[64]; +char scr_usestring2[64]; +float scr_usetime_off = 0.0f; +int button_pic_x; +extern qpic_t *b_abutton; +extern qpic_t *b_bbutton; +extern qpic_t *b_ybutton; +extern qpic_t *b_xbutton; +extern qpic_t *b_left; +extern qpic_t *b_right; +extern qpic_t *b_up; +extern qpic_t *b_down; +extern qpic_t *b_lt; +extern qpic_t *b_rt; +extern qpic_t *b_start; +extern qpic_t *b_select; +extern qpic_t *b_zlt; +extern qpic_t *b_zrt; + +/* +============== +SCR_UsePrint + +Similiar to above, but will also print the current button for the action. +============== +*/ + +qpic_t *GetButtonIcon (char *buttonname) +{ + int j; + int l; + char *b; + l = strlen(buttonname); + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, buttonname, l) ) + { + // naievil -- need to fix these + if (!strcmp(Key_KeynumToString(j), "PADUP")) + return b_up; + else if (!strcmp(Key_KeynumToString(j), "PADDOWN")) + return b_down; + else if (!strcmp(Key_KeynumToString(j), "PADLEFT")) + return b_left; + else if (!strcmp(Key_KeynumToString(j), "PADRIGHT")) + return b_right; + else if (!strcmp(Key_KeynumToString(j), "SELECT")) + return b_select; + else if (!strcmp(Key_KeynumToString(j), "ABUTTON")) + return b_abutton; + else if (!strcmp(Key_KeynumToString(j), "BBUTTON")) + return b_bbutton; + else if (!strcmp(Key_KeynumToString(j), "XBUTTON")) + return b_xbutton; + else if (!strcmp(Key_KeynumToString(j), "YBUTTON")) + return b_ybutton; + else if (!strcmp(Key_KeynumToString(j), "LTRIGGER")) + return b_lt; + else if (!strcmp(Key_KeynumToString(j), "RTRIGGER")) + return b_rt; + else if (!strcmp(Key_KeynumToString(j), "ZLTRIGGER")) + return b_zlt; + else if (!strcmp(Key_KeynumToString(j), "ZRTRIGGER")) + return b_zrt; + } + } + return b_abutton; +} + +char *GetUseButtonL () +{ + int j; + int l; + char *b; + l = strlen("+use"); + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, "+use", l) ) + { + if (!strcmp(Key_KeynumToString(j), "SELECT") || + !strcmp(Key_KeynumToString(j), "LTRIGGER") || + !strcmp(Key_KeynumToString(j), "RTRIGGER") || + !strcmp(Key_KeynumToString(j), "HOME")) + return " "; + else + return " "; + } + } + return " "; +} + +char *GetGrenadeButtonL () +{ + int j; + int l; + char *b; + l = strlen("+grenade"); + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, "+grenade", l) ) + { + if (!strcmp(Key_KeynumToString(j), "SELECT") || + !strcmp(Key_KeynumToString(j), "LTRIGGER") || + !strcmp(Key_KeynumToString(j), "RTRIGGER") || + !strcmp(Key_KeynumToString(j), "HOME")) + return " "; + else + return " "; + } + } + return " "; +} + +char *GetPerkName (int perk) +{ + switch (perk) + { + case 1: + return "Quick Revive"; + case 2: + return "Juggernog"; + case 3: + return "Speed Cola"; + case 4: + return "Double Tap"; + case 5: + return "Stamin-Up"; + case 6: + return "PhD Flopper"; + case 7: + return "Deadshot Daiquiri"; + case 8: + return "Mule Kick"; + default: + return "NULL"; + } +} + +void SCR_UsePrint (int type, int cost, int weapon) +{ + //naievil -- fixme + char s[128]; + char c[128]; + + switch (type) + { + case 0://clear + strcpy(s, ""); + strcpy(c, ""); + break; + case 1://door + strcpy(s, va("Hold %s to open Door\n", GetUseButtonL())); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 2://debris + strcpy(s, va("Hold %s to remove Debris\n", GetUseButtonL())); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 3://ammo + strcpy(s, va("Hold %s to buy Ammo for %s\n", GetUseButtonL(), pr_strings+sv_player->v.Weapon_Name_Touch)); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 4://weapon + strcpy(s, va("Hold %s to buy %s\n", GetUseButtonL(), pr_strings+sv_player->v.Weapon_Name_Touch)); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 5://window + strcpy(s, va("Hold %s to Rebuild Barrier\n", GetUseButtonL())); + strcpy(c, ""); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 6://box + strcpy(s, va("Hold %s to for Mystery Box\n", GetUseButtonL())); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 7://box take + strcpy(s, va("Hold %s for %s\n", GetUseButtonL(), pr_strings+sv_player->v.Weapon_Name_Touch)); + strcpy(c, ""); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 8://power + strcpy(s, "The Power must be Activated first\n"); + strcpy(c, ""); + button_pic_x = 100; + break; + case 9://perk + strcpy(s, va("Hold %s to buy %s\n", GetUseButtonL(), GetPerkName(weapon))); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 10://turn on power + strcpy(s, va("Hold %s to Turn On the Power\n", GetUseButtonL())); + strcpy(c, ""); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 11://turn on trap + strcpy(s, va("Hold %s to Activate the Trap\n", GetUseButtonL())); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 12://PAP + strcpy(s, va("Hold %s to Pack-a-Punch\n", GetUseButtonL())); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 13://revive + strcpy(s, va("Hold %s to Fix your Code.. :)\n", GetUseButtonL())); + strcpy(c, ""); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 14://use teleporter (free) + strcpy(s, va("Hold %s to use Teleporter\n", GetUseButtonL())); + strcpy(c, ""); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 15://use teleporter (cost) + strcpy(s, va("Hold %s to use Teleporter\n", GetUseButtonL())); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 16://tp cooldown + strcpy(s, "Teleporter is cooling down\n"); + strcpy(c, ""); + button_pic_x = 100; + break; + case 17://link + strcpy(s, va("Hold %s to initiate link to pad\n", GetUseButtonL())); + strcpy(c, ""); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 18://no link + strcpy(s, "Link not active\n"); + strcpy(c, ""); + button_pic_x = 100; + break; + case 19://finish link + strcpy(s, va("Hold %s to link pad with core\n", GetUseButtonL())); + strcpy(c, ""); + button_pic_x = getTextWidth("Hold ", 1); + break; + case 20://buyable ending + strcpy(s, va("Hold %s to End the Game\n", GetUseButtonL())); + strcpy(c, va("[Cost: %i]\n", cost)); + button_pic_x = getTextWidth("Hold ", 1); + break; + default: + Con_Printf ("No type defined in engine for useprint\n"); + break; + } + + strncpy (scr_usestring, va(s), sizeof(scr_usestring)-1); + strncpy (scr_usestring2, va(c), sizeof(scr_usestring2)-1); + scr_usetime_off = 0.1; +} + + +void SCR_DrawUseString (void) +{ + int y; + int x; + + if (cl.stats[STAT_HEALTH] < 0) + return; +// the finale prints the characters one at a time + + y = 160; + x = (vid.width - getTextWidth(scr_usestring, 1))/2; + + Draw_ColoredStringCentered(y, scr_usestring, 255, 255, 255, 255, 1); + Draw_ColoredStringCentered(y + 10, scr_usestring2, 255, 255, 255, 255, 1); + + if (button_pic_x != 100) + Draw_Pic (x + button_pic_x, y - 4, GetButtonIcon("+use")); +} + +void SCR_CheckDrawUseString (void) +{ + scr_copytop = 1; + + scr_usetime_off -= host_frametime; + + if (scr_usetime_off <= 0 && !cl.intermission) + return; + if (key_dest != key_game) + return; + if (cl.stats[STAT_HEALTH] <= 0) + return; + + SCR_DrawUseString (); +} + +//============================================================================= + +/* +==================== +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/tanf(fov_x/360*M_PI); + + a = atanf (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; + 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_fov_viewmodel); + 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 (&scr_showfps); + Cvar_RegisterVariable (&scr_loadscreen); + Cvar_RegisterVariable (&cl_crosshair_debug); + + 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); + + hitmark = Draw_CachePic("gfx/hud/hit_marker"); + + + scr_initialized = true; +} + +//============================================================================ + +/* +============== +SCR_DrawFPS -- johnfitz +============== +*/ +void SCR_DrawFPS (void) +{ + static double oldtime = 0; + static double lastfps = 0; + static int oldframecount = 0; + double elapsed_time; + int frames; + + elapsed_time = realtime - oldtime; + frames = r_framecount - oldframecount; + + if (elapsed_time < 0 || frames < 0) + { + oldtime = realtime; + oldframecount = r_framecount; + return; + } + // update value every 3/4 second + if (elapsed_time > 0.75) + { + lastfps = frames / elapsed_time; + oldtime = realtime; + oldframecount = r_framecount; + } + + if (scr_showfps.value) + { + char st[16]; + sprintf (st, "%4.0f fps", lastfps); + Draw_String (300, 0, st); + } +} + + +//============================================================================= + +/* +============== +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); +} + +int Random_Int (int max_int) +{ + float f; + f = (rand ()&0x7fff) / ((float)0x7fff) * max_int; + if (f > 0) + return (int)(f + 0.5) + 1; + else + return (int)(f - 0.5) + 1; +} +/* +============== +SCR_DrawLoadScreen +============== +*/ + +/* + Creds to the following people from the 2020 + Loading Screen Hint Submission/Contest: + + * BCDeshiG + * Derped_Crusader + * Aidan + * yasen + * greg + * Asher + * Bernerd + * Omar Alejandro + * TheSmashers +*/ + +// 47 character limit + +double loadingtimechange; +int loadingdot; +char *lodinglinetext; +qpic_t *awoo; +char *ReturnLoadingtex (void) +{ + int StringNum = Random_Int(80); + switch(StringNum) + { + case 1: + return "Released in 1996, Quake is over 25 years old!"; + break; + case 2: + return "Use the Kar98k to be the hero we need!"; + break; + case 3: + return "Lots of modern engines are based on Quake!"; + break; + case 4: + return "NZ:P began development on September 27 2009!"; + break; + case 5: + return "NZ:P was first released on December 25, 2010!"; + break; + case 6: + return "NZ:P Beta 1.1 has over 300,000 downloads!"; + break; + case 7: + return "NZ:P has been downloaded over 500,000 times!"; + break; + case 8: + return "A lot of people have worked on NZ:P!"; + break; + case 9: + return "Blubswillrule, or \"blubs\", is from the US."; + break; + case 10: + return "Jukki is from Finland."; + break; + case 11: + return "Ju[s]tice, or \"tom\" is from Lithuania."; + break; + case 12: + return "This game has given us bad sleeping habits!"; + break; + case 13: + return "We had a lot of fun making this game!"; + break; + case 14: + return "Pro Tip: you can make your own custom map!"; + break; + case 15: + return "Try Retro Mode, it's in the Graphics Settings!"; + break; + case 16: + return "Tired of our maps? Go make your own!"; + break; + case 17: + return "Slay zombies & be grateful."; + break; + case 18: + return "Custom maps, CUSTOM MAPS!"; + break; + case 19: + return "Go outside & build a snowman!"; + break; + case 20: + return "Please surround yourself with zombies!"; + break; + case 21: + return "Don't play for too long.. zombies may eat you."; + break; + case 22: + return "That was epic... EPIC FOR THE WIIIN!"; //why + break; + case 23: + return "Mikeage and Citra are awesome 3DS emulators!"; + break; + case 24: + return "You dead yet?"; + break; + case 25: + return "Now 21% cooler!"; + break; + case 26: + return "your lg is nothink on the lan!"; //what + break; + case 27: + return "I'm not your chaotic on dm6!"; + break; + case 28: + return "Shoot or knife zombies to kill them, up to you!"; + break; + case 29: + return "How many people forgot to Compile today?"; + break; + case 30: + return "ggnore"; + break; + case 31: + return "NZ:P is also on PC, Switch, Vita, and PSP!"; + break; + case 32: + return "Submerge your device in water for godmode!"; + break; + case 33: + return "10/10/10 was a good day."; + break; + case 34: + return "Also check out \"Halo Revamped\" for 3DS!"; + break; + case 35: + return "CypressImplex, or \"Ivy\", is from the USA."; + break; + case 36: + return "Zombies don't like bullets."; + break; + case 37: + return "Thanks for being an awesome fan!"; + break; + case 38: + return "Removed Herobrine"; + break; + case 39: + return "Pack-a-Punch the Kar98k to get to round 100000."; + break; + case 40: + return "I feel like I'm being gaslit."; + break; + case 41: + return "Heads up! You will die if you are killed!"; + break; + case 42: + return "Zombies legally can't kill you if you say no!"; + break; + case 43: + return "Please help me find the meaning of . Thanks."; + break; + case 44: + return "Discord is ONLY for Thomas the Tank Engine RP!"; + break; + case 45: + return "\"Get rid of the 21% tip, it's an MLP reference.\""; + break; + case 46: + return "You're playing on a 3DS!"; + break; + case 47: + return "Don't leak the beta!"; + break; + case 48: + return "Jugger-Nog increases your health!"; + break; + case 49: + return "greg was here"; + break; + case 50: + return "Where the hell is the Mystery Box?!"; + break; + case 51: + return "Zombies like getting shot.. I think."; + break; + case 52: + return "pro tip: aiming helps"; + break; + case 53: + return "\"my mom gave me plunger money\""; + break; + case 54: + return "dolphin dive on top of your friend for god mode"; + break; + case 55: + return "no free rides. ass, grass, or cash!"; + break; + case 56: + return "nzp-team.github.io/latest/game.html"; + break; + case 57: + return "im an mlg gamer girl so its pretty guaranteed"; + break; + case 58: + return "this is a w because you cant have enough fnaf"; + break; + case 59: + return "i hope santa drops bombs on the uk"; + break; + case 60: + return "Hoyl shit, bro! You fucking ported fortnite!"; + break; + case 61: + return "icarly feet futtishist."; + break; + case 62: + return "Well, it's impossible to play, I'm disgusted."; + break; + case 63: + return "I like my women to not be cartoons"; + break; + case 64: + return "Plot twist: NZP was always broken"; + break; + case 65: + return "testing some think."; + break; + case 66: + return "fnaf is older than gay marriage in the us"; + break; + case 67: + return "i want that twink Obliterated"; + break; + case 68: + return "i think he started the femboy transition process"; + break; + case 69: + return "nice"; + break; + case 70: + return "He's FUCKING annoying"; + break; + case 71: + return "yeah pog female bikers"; + break; + case 72: + return "Its either a stroke of genius or just a stroke"; + break; + case 73: + return "Play some Custom Maps!"; + break; + case 74: + return "Real OGs play on \"Old\" 3DS models!"; + break; + case 75: + return "Adding this tip improved framerate by 39%!"; + break; + case 76: + return "The NZ in NZP stands for New Zealand!"; + break; + case 77: + return "The P in NZP stands for Professional!"; + break; + case 78: + return "Remember to stay hydrated!"; + break; + case 79: + return "cofe"; + break; + } + return "wut wut"; +} +qboolean load_screen_exists; +void SCR_DrawLoadScreen (void) +{ + + if (developer.value) { + return; + } + if (!con_forcedup) { + return; + } + + if (loadingScreen) { + Draw_FillByColor(0, 0, 400, 240, 0, 0, 0, 255); + if (!loadscreeninit) { + load_screen_exists = qfalse; + + char* lpath; + lpath = (char*)Z_Malloc(sizeof(char)*32); + strcpy(lpath, "gfx/lscreen/"); + strcat(lpath, loadname2); + + lscreen = Draw_CachePic(lpath); + awoo = Draw_CachePic("gfx/menu/awoo"); + + if (lscreen != NULL) + load_screen_exists = qtrue; + + loadscreeninit = qtrue; + } + + if (load_screen_exists == qtrue) + Draw_StretchPic(scr_vrect.x, scr_vrect.y, lscreen, 400, 240); + + Draw_FillByColor(0, 0, 480, 24, 0, 0, 0, 175); + Draw_FillByColor(0, 216, 480, 24, 0, 0, 0, 175); + + Draw_ColoredString(2, 4, loadnamespec, 255, 255, 0, 255, 2); + } + + if (loadingtimechange < Sys_FloatTime ()) + { + lodinglinetext = ReturnLoadingtex(); + loadingtimechange = Sys_FloatTime () + 5; + } + + if (key_dest == key_game) { + Draw_ColoredStringCentered(225, lodinglinetext, 255, 255, 255, 255, 1); + + if (strcmp(lodinglinetext, "Please help me find the meaning of . Thanks.") == 0) { + Draw_Pic(280, 225, awoo); + } + } +} + + +/* +================== +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 + if (!cl.worldmodel || cls.signon != SIGNONS)//blubs here, undid it actually + { + con_forcedup = qtrue; + } + else + { + con_forcedup = qfalse; + } + + 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 (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, true); + 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; + } + + + 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 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 (cl.stats[STAT_ZOOM] == 1) + { + if(!original_fov) { + original_fov = scr_fov.value; + original_view_fov = scr_fov_viewmodel.value; + } + + if(scr_fov.value > (GetWeaponZoomAmmount() + 1))//+1 for accounting for floating point inaccurraces + { + scr_fov.value += ((original_fov - GetWeaponZoomAmmount()) - scr_fov.value) * 0.25; + scr_fov_viewmodel.value += ((original_view_fov - GetWeaponZoomAmmount()) - scr_fov_viewmodel.value) * 0.25; + Cvar_SetValue("fov",scr_fov.value); + Cvar_SetValue("r_viewmodel_fov", scr_fov_viewmodel.value); + } + } + else if (cl.stats[STAT_ZOOM] == 2) + { + Cvar_SetValue ("fov", 30); + Cvar_SetValue ("r_viewmodel_fov", 30); + zoomin_time = 0; + } + else if (cl.stats[STAT_ZOOM] == 0 && original_fov != 0) + { + if(scr_fov.value < (original_fov + 1))//+1 for accounting for floating point inaccuracies + { + scr_fov.value += (original_fov - scr_fov.value) * 0.25; + scr_fov_viewmodel.value += (original_view_fov - scr_fov_viewmodel.value) * 0.25; + Cvar_SetValue("fov",scr_fov.value); + Cvar_SetValue("r_viewmodel_fov", scr_fov_viewmodel.value); + } + else + { + original_fov = 0; + original_view_fov = 0; + } + } + + if (oldfov != scr_fov.value) + { + oldfov = scr_fov.value; + vid.recalc_refdef = qtrue; + } + + if (oldscreensize != scr_viewsize.value) + { + oldscreensize = scr_viewsize.value; + vid.recalc_refdef = qtrue; + } + + if (vid.recalc_refdef) + SCR_CalcRefdef (); + +// +// do 3D refresh drawing, and then update the screen +// + SCR_SetUpToDrawConsole (); + + V_RenderView (); + + GL_Set2D (); + + Draw_Crosshair (); + + //muff - to show FPS on screen + SCR_DrawFPS (); + SCR_CheckDrawCenterString (); + SCR_CheckDrawUseString (); + HUD_Draw (); + SCR_DrawConsole (); + M_Draw (); + + if(scr_loadscreen.value) { + SCR_DrawLoadScreen(); + } + + Draw_LoadingFill(); + + V_UpdatePalette (); + + GL_EndRendering (); +} + diff --git a/source/ctr/gl/gl_test.c b/source/ctr/gl/gl_test.c new file mode 100644 index 0000000..b07ce7b --- /dev/null +++ b/source/ctr/gl/gl_test.c @@ -0,0 +1,182 @@ +/* +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" + +#ifdef GLTEST + +typedef struct +{ + plane_t *plane; + vec3_t origin; + vec3_t normal; + vec3_t up; + vec3_t right; + vec3_t reflect; + float length; +} puff_t; + +#define MAX_PUFFS 64 + +puff_t puffs[MAX_PUFFS]; + + +void Test_Init (void) +{ +} + + + +plane_t junk; +plane_t *HitPlane (vec3_t start, vec3_t end) +{ + trace_t trace; + +// fill in a default trace + memset (&trace, 0, sizeof(trace_t)); + trace.fraction = 1; + trace.allsolid = true; + VectorCopy (end, trace.endpos); + + SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace); + + junk = trace.plane; + return &junk; +} + +void Test_Spawn (vec3_t origin) +{ + int i; + puff_t *p; + vec3_t temp; + vec3_t normal; + vec3_t incoming; + plane_t *plane; + float d; + + for (i=0,p=puffs ; ilength <= 0) + break; + } + if (i == MAX_PUFFS) + return; + + VectorSubtract (r_refdef.vieworg, origin, incoming); + VectorSubtract (origin, incoming, temp); + plane = HitPlane (r_refdef.vieworg, temp); + + VectorNormalize (incoming); + d = DotProduct (incoming, plane->normal); + VectorSubtract (vec3_origin, incoming, p->reflect); + VectorMA (p->reflect, d*2, plane->normal, p->reflect); + + VectorCopy (origin, p->origin); + VectorCopy (plane->normal, p->normal); + + CrossProduct (incoming, p->normal, p->up); + + CrossProduct (p->up, p->normal, p->right); + + p->length = 8; +} + +void DrawPuff (puff_t *p) +{ + vec3_t pts[2][3]; + int i, j; + float s, d; + + for (i=0 ; i<2 ; i++) + { + if (i == 1) + { + s = 6; + d = p->length; + } + else + { + s = 2; + d = 0; + } + + for (j=0 ; j<3 ; j++) + { + pts[i][0][j] = p->origin[j] + p->up[j]*s + p->reflect[j]*d; + pts[i][1][j] = p->origin[j] + p->right[j]*s + p->reflect[j]*d; + pts[i][2][j] = p->origin[j] + -p->right[j]*s + p->reflect[j]*d; + } + } + + glColor3f (1, 0, 0); + +#if 0 + glBegin (GL_LINES); + glVertex3fv (p->origin); + glVertex3f (p->origin[0] + p->length*p->reflect[0], + p->origin[1] + p->length*p->reflect[1], + p->origin[2] + p->length*p->reflect[2]); + + glVertex3fv (pts[0][0]); + glVertex3fv (pts[1][0]); + + glVertex3fv (pts[0][1]); + glVertex3fv (pts[1][1]); + + glVertex3fv (pts[0][2]); + glVertex3fv (pts[1][2]); + + glEnd (); +#endif + + glBegin (GL_QUADS); + for (i=0 ; i<3 ; i++) + { + j = (i+1)%3; + glVertex3fv (pts[0][j]); + glVertex3fv (pts[1][j]); + glVertex3fv (pts[1][i]); + glVertex3fv (pts[0][i]); + } + glEnd (); + + glBegin (GL_TRIANGLES); + glVertex3fv (pts[1][0]); + glVertex3fv (pts[1][1]); + glVertex3fv (pts[1][2]); + glEnd (); + + p->length -= host_frametime*2; +} + + +void Test_Draw (void) +{ + int i; + puff_t *p; + + for (i=0, p=puffs ; ilength > 0) + DrawPuff (p); + } +} + +#endif diff --git a/source/ctr/gl/gl_vidctr.c b/source/ctr/gl/gl_vidctr.c new file mode 100644 index 0000000..181e26b --- /dev/null +++ b/source/ctr/gl/gl_vidctr.c @@ -0,0 +1,252 @@ +/* +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 <3ds.h> +#include +#include "../../quakedef.h" + +unsigned d_8to24table[256]; +unsigned char d_15to8table[65536]; + +int texture_mode = GL_LINEAR; + +int texture_extension_number = 1; + +float gldepthmin, gldepthmax; + +cvar_t gl_ztrick = {"gl_ztrick","0"}; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +static float vid_gamma = 1.0; + +qboolean is8bit = false; +qboolean isPermedia = true; +qboolean gl_mtexable = false; + +/* +=============== +GL_Init +=============== +*/ +void GL_Init (void) +{ + pglInitEx(0x040000, 0x100000); + + gl_vendor = glGetString (GL_VENDOR); + gl_renderer = glGetString (GL_RENDERER); + gl_version = glGetString (GL_VERSION); + gl_extensions = glGetString (GL_EXTENSIONS); + + glClearDepth (1.0f); + glClearColor ((float)(16/255),(float)(32/255),(float)(64/255),1); + glCullFace(GL_FRONT); + glEnable(GL_TEXTURE_2D); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glShadeModel (GL_FLAT); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glClear(GL_COLOR_BUFFER_BIT); +} + + +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + *x = *y = 0; + *width = 400; + *height = 240; +} + + +void GL_EndRendering (void) +{ + // naievil -- this requies the new version of picagl to be used properly + //glFinish(); + pglSwapBuffersEx(1,0); +} + +void VID_SetPalette (unsigned char *palette) +{ + byte *pal; + unsigned r,g,b; + unsigned v; + int r1,g1,b1; + int j,k,l,m; + unsigned short i; + unsigned *table; + FILE *f; + char s[255]; + int dist, bestdist; + +// +// 8 8 8 encoding +// + pal = palette; + table = d_8to24table; + for (i=0 ; i<256 ; i++) + { + r = pal[0]; + g = pal[1]; + b = pal[2]; + pal += 3; + + v = (255<<24) + (r<<0) + (g<<8) + (b<<16); + *table++ = v; + } + d_8to24table[255] &= 0xffffff; // 255 is transparent + + // JACK: 3D distance calcs - k is last closest, l is the distance. + for (i=0; i < (1<<15); i++) { + /* Maps + 000000000000000 + 000000000011111 = Red = 0x1F + 000001111100000 = Blue = 0x03E0 + 111110000000000 = Grn = 0x7C00 + */ + r = ((i & 0x1F) << 3)+4; + g = ((i & 0x03E0) >> 2)+4; + b = ((i & 0x7C00) >> 7)+4; + pal = (unsigned char *)d_8to24table; + for (v=0,k=0,bestdist=10000*10000; v<256; v++,pal+=4) { + r1 = (int)r - (int)pal[0]; + g1 = (int)g - (int)pal[1]; + b1 = (int)b - (int)pal[2]; + dist = (r1*r1)+(g1*g1)+(b1*b1); + if (dist < bestdist) { + k=v; + bestdist = dist; + } + } + d_15to8table[i]=k; + } +} + +void VID_ShiftPalette (unsigned char *palette) +{ + //VID_SetPalette(palette); +} + +qboolean VID_Is8bit(void) +{ + return is8bit; +} + +static void Check_Gamma (unsigned char *pal) +{ + float f, inf; + unsigned char palette[768]; + int i; + + if ((i = COM_CheckParm("-gamma")) == 0) + vid_gamma = 0.7; + else + vid_gamma = Q_atof(com_argv[i+1]); + + for (i=0 ; i<768 ; i++) + { + f = pow ( (pal[i]+1)/256.0 , vid_gamma ); + inf = f*255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + palette[i] = inf; + } + + memcpy (pal, palette, sizeof(palette)); + + BuildGammaTable (vid_gamma); //Diabolickal HLBSP +} + +void VID_Init (unsigned char *palette) +{ + int i; + char gldir[MAX_OSPATH]; + int width = 400; + int height = 240; + + Cvar_RegisterVariable (&gl_ztrick); + + vid.maxwarpwidth = width; + vid.maxwarpheight = height; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + + vid.conwidth = 320; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth*3 / 4; + + if ((i = COM_CheckParm("-conheight")) != 0) + vid.conheight = Q_atoi(com_argv[i+1]); + if (vid.conheight < 200) + vid.conheight = 200; + + if (vid.conheight > height) + vid.conheight = height; + if (vid.conwidth > width) + vid.conwidth = width; + + vid.width = 400; + vid.height = 240; + + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + vid.numpages = 2; + + GL_Init(); + + sprintf (gldir, "%s/glquake", com_gamedir); + Sys_mkdir (gldir); + + Check_Gamma(palette); + VID_SetPalette(palette); + + Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void VID_Shutdown (void) +{ +} + +void VID_Update (vrect_t *rects) +{ +} diff --git a/source/ctr/gl/gl_warp.c b/source/ctr/gl/gl_warp.c new file mode 100644 index 0000000..f930a97 --- /dev/null +++ b/source/ctr/gl/gl_warp.c @@ -0,0 +1,785 @@ +/* +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. + +*/ +// gl_warp.c -- sky and water polygons + +#include "../../quakedef.h" + +extern model_t *loadmodel; + +int skytexturenum; + +int solidskytexture; +int alphaskytexture; +float speedscale; // for top sky and bottom sky + +int skytexorder[5] = {0,2,1,3,4}; +int skyimage[5]; // Where sky images are stored +char skybox_name[32] = ""; //name of current skybox, or "" if no skybox +// cut off down for half skybox +char *suf[5] = {"rt", "bk", "lf", "ft", "up" }; + +msurface_t *warpface; + +extern cvar_t gl_subdivide_size; + +void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) +{ + int i, j; + float *v; + + mins[0] = mins[1] = mins[2] = 9999; + maxs[0] = maxs[1] = maxs[2] = -9999; + v = verts; + for (i=0 ; i maxs[j]) + maxs[j] = *v; + } +} + +void SubdividePolygon (int numverts, float *verts) +{ + int i, j, k; + vec3_t mins, maxs; + float m; + float *v; + vec3_t front[64], back[64]; + int f, b; + float dist[64]; + float frac; + glpoly_t *poly; + float s, t; + + if (numverts > 60) + Sys_Error ("numverts = %i", numverts); + + BoundPoly (numverts, verts, mins, maxs); + + for (i=0 ; i<3 ; i++) + { + m = (mins[i] + maxs[i]) * 0.5; + m = gl_subdivide_size.value * floor (m/gl_subdivide_size.value + 0.5); + if (maxs[i] - m < 8) + continue; + if (m - mins[i] < 8) + continue; + + // cut it + v = verts + i; + for (j=0 ; j= 0) + { + VectorCopy (v, front[f]); + f++; + } + if (dist[j] <= 0) + { + VectorCopy (v, back[b]); + b++; + } + if (dist[j] == 0 || dist[j+1] == 0) + continue; + if ( (dist[j] > 0) != (dist[j+1] > 0) ) + { + // clip point + frac = dist[j] / (dist[j] - dist[j+1]); + for (k=0 ; k<3 ; k++) + front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]); + f++; + b++; + } + } + + SubdividePolygon (f, front[0]); + SubdividePolygon (b, back[0]); + return; + } + + poly = Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); + poly->next = warpface->polys; + warpface->polys = poly; + poly->numverts = numverts; + for (i=0 ; iverts[i]); + s = DotProduct (verts, warpface->texinfo->vecs[0]); + t = DotProduct (verts, warpface->texinfo->vecs[1]); + poly->verts[i][3] = s; + poly->verts[i][4] = t; + } +} + +/* +================ +GL_SubdivideSurface + +Breaks a polygon up along axial 64 unit +boundaries so that turbulent and sky warps +can be done reasonably. +================ +*/ +void GL_SubdivideSurface (msurface_t *fa) +{ + vec3_t verts[64]; + int numverts; + int i; + int lindex; + float *vec; + texture_t *t; + + warpface = fa; + + // + // convert edges back to a normal polygon + // + numverts = 0; + for (i=0 ; inumedges ; i++) + { + lindex = loadmodel->surfedges[fa->firstedge + i]; + + if (lindex > 0) + vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; + else + vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; + VectorCopy (vec, verts[numverts]); + numverts++; + } + + SubdividePolygon (numverts, verts[0]); +} + +//========================================================= + + + +// speed up sin calculations - Ed +float turbsin[] = +{ + #include "gl_warp_sin.h" +}; +#define TURBSCALE (256.0 / (2 * M_PI)) + +/* +============= +EmitWaterPolys + +Does a water warp on the pre-fragmented glpoly_t chain +============= +*/ +void EmitWaterPolys (msurface_t *fa) +{ + glpoly_t *p; + float *v; + int i; + float s, t, os, ot; + + + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + os = v[3]; + ot = v[4]; + + s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255]; + s *= (1.0/64); + + t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255]; + t *= (1.0/64); + + glTexCoord2f (s, t); + glVertex3fv (v); + } + glEnd (); + } +} + + + + +/* +============= +EmitSkyPolys +============= +*/ +void EmitSkyPolys (msurface_t *fa) +{ + glpoly_t *p; + float *v; + int i; + float s, t; + vec3_t dir; + float length; + + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + VectorSubtract (v, r_origin, dir); + dir[2] *= 3; // flatten the sphere + + length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; + length = sqrt (length); + length = 6*63/length; + + dir[0] *= length; + dir[1] *= length; + + s = (speedscale + dir[0]) * (1.0/128); + t = (speedscale + dir[1]) * (1.0/128); + + glTexCoord2f (s, t); + glVertex3fv (v); + } + glEnd (); + } +} + +/* +=============== +EmitBothSkyLayers + +Does a sky warp on the pre-fragmented glpoly_t chain +This will be called for brushmodels, the world +will have them chained together. +=============== +*/ +void EmitBothSkyLayers (msurface_t *fa) +{ + int i; + int lindex; + float *vec; + + GL_DisableMultitexture(); + + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + glEnable (GL_BLEND); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + glDisable (GL_BLEND); +} + +#ifndef QUAKE2 +/* +================= +R_DrawSkyChain +================= +*/ +void R_DrawSkyChain (msurface_t *s) +{ + msurface_t *fa; + + GL_DisableMultitexture(); + + // used when gl_texsort is on + GL_Bind(solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + glEnable (GL_BLEND); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + glDisable (GL_BLEND); +} + +#endif + +/* +================================================================= + + Quake 2 environment sky + +================================================================= +*/ + +/* +================== +Sky_LoadSkyBox +================== +*/ +//char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; +void Sky_LoadSkyBox(char* name) +{ + if (strcmp(skybox_name, name) == 0) + return; //no change + + //turn off skybox if sky is set to "" + if (name[0] == '0') { + skybox_name[0] = 0; + return; + } + + // Do sides one way and top another, bottom is not done + for (int i = 0; i < 4; i++) + { + int mark = Hunk_LowMark (); + + if(!(skyimage[i] = loadtextureimage (va("gfx/env/%s%s", name, suf[i]), 0, 0, false, false)) && + !(skyimage[i] = loadtextureimage (va("gfx/env/%s_%s", name, suf[i]), 0, 0, false, false))) + { + Con_Printf("Sky: %s[%s] not found, used std\n", name, suf[i]); + if(!(skyimage[i] = loadtextureimage (va("gfx/env/skybox%s", suf[i]), 0, 0, false, false))) + { + Sys_Error("STD SKY NOT FOUND!"); + } + + } + Hunk_FreeToLowMark (mark); + } + + int mark = Hunk_LowMark (); + if(!(skyimage[4] = loadtextureimage (va("gfx/env/%sup", name), 0, 0, false, false)) && + !(skyimage[4] = loadtextureimage (va("gfx/env/%s_up", name), 0, 0, false, false))) + { + Con_Printf("Sky: %s[%s] not found, used std\n", name, suf[4]); + if(!(skyimage[4] = loadtextureimage (va("gfx/env/skybox%s", suf[4]), 0, 0, false, false))) + { + Sys_Error("STD SKY NOT FOUND!"); + } + + } + Hunk_FreeToLowMark (mark); + + strcpy(skybox_name, name); +} + +/* +================= +Sky_NewMap +================= +*/ +void Sky_NewMap (void) +{ + char key[128], value[4096]; + char *data; + + //purge old sky textures + //UnloadSkyTexture (); + + // + // initially no sky + // + Sky_LoadSkyBox (""); //not used + + // + // read worldspawn (this is so ugly, and shouldn't it be done on the server?) + // + data = cl.worldmodel->entities; + if (!data) + return; //FIXME: how could this possibly ever happen? -- if there's no + // worldspawn then the sever wouldn't send the loadmap message to the client + + data = COM_Parse(data); + + if (!data) //should never happen + return; // error + + if (com_token[0] != '{') //should never happen + return; // error + + while (1) + { + data = COM_Parse(data); + + if (!data) + return; // error + + if (com_token[0] == '}') + break; // end of worldspawn + + if (com_token[0] == '_') + strcpy(key, com_token + 1); + else + strcpy(key, com_token); + while (key[strlen(key)-1] == ' ') // remove trailing spaces + key[strlen(key)-1] = 0; + + data = COM_Parse(data); + if (!data) + return; // error + + strcpy(value, com_token); + + if (!strcmp("sky", key)) + Sky_LoadSkyBox(value); + else if (!strcmp("skyname", key)) //half-life + Sky_LoadSkyBox(value); + else if (!strcmp("qlsky", key)) //quake lives + Sky_LoadSkyBox(value); + } +} + +/* +================= +Sky_SkyCommand_f +================= +*/ +void Sky_SkyCommand_f (void) +{ + switch (Cmd_Argc()) + { + case 1: + Con_Printf("\"sky\" is \"%s\"\n", skybox_name); + break; + case 2: + Sky_LoadSkyBox(Cmd_Argv(1)); + break; + default: + Con_Printf("usage: sky \n"); + } +} + +/* +============= +Sky_Init +============= +*/ +void Sky_Init (void) +{ + int i; + + Cmd_AddCommand ("sky",Sky_SkyCommand_f); + + for (i=0; i<5; i++) + skyimage[i] = NULL; +} + +static vec3_t skyclip[6] = { + {1,1,0}, + {1,-1,0}, + {0,-1,1}, + {0,1,1}, + {1,0,1}, + {-1,0,1} +}; +int c_sky; + +// 1 = s, 2 = t, 3 = 2048 +static int st_to_vec[6][3] = +{ + {3,-1,2}, + {-3,1,2}, + + {1,3,2}, + {-1,-3,2}, + + {-2,-1,3}, // 0 degrees yaw, look straight up + {2,-1,-3} // look straight down + +// {-1,2,3}, +// {1,2,-3} +}; + +// s = [0]/[2], t = [1]/[2] +static int vec_to_st[6][3] = +{ + {-2,3,1}, + {2,3,-1}, + + {1,3,2}, + {-1,3,-2}, + + {-2,-1,3}, + {-2,1,-3} + +// {-1,2,3}, +// {1,2,-3} +}; + +static float skymins[2][6], skymaxs[2][6]; + +/* +============== +R_ClearSkyBox +============== +*/ +void R_ClearSkyBox (void) +{ + int i; + + for (i=0 ; i<5 ; i++) + { + skymins[0][i] = skymins[1][i] = 9999; + skymaxs[0][i] = skymaxs[1][i] = -9999; + } +} + + +void MakeSkyVec (float s, float t, int axis) +{ + vec3_t v, b; + int j, k; + + b[0] = s*2048; + b[1] = t*2048; + b[2] = 2048; + + for (j=0 ; j<3 ; j++) + { + k = st_to_vec[axis][j]; + if (k < 0) + v[j] = -b[-k - 1]; + else + v[j] = b[k - 1]; + v[j] += r_origin[j]; + } + + // avoid bilerp seam + s = (s+1)*0.5; + t = (t+1)*0.5; + + if (s < 1.0/512) + s = 1.0/512; + else if (s > 511.0/512) + s = 511.0/512; + if (t < 1.0/512) + t = 1.0/512; + else if (t > 511.0/512) + t = 511.0/512; + + t = 1.0 - t; + glTexCoord2f (s, t); + glVertex3fv (v); +} + +/* +============== +R_DrawSkyBox +============== +*/ + +float skynormals[5][3] = { + { 1.f, 0.f, 0.f }, + { -1.f, 0.f, 0.f }, + { 0.f, 1.f, 0.f }, + { 0.f, -1.f, 0.f }, + { 0.f, 0.f, 1.f } +}; + +float skyrt[5][3] = { + { 0.f, -1.f, 0.f }, + { 0.f, 1.f, 0.f }, + { 1.f, 0.f, 0.f }, + { -1.f, 0.f, 0.f }, + { 0.f, -1.f, 0.f } +}; + +float skyup[5][3] = { + { 0.f, 0.f, 1.f }, + { 0.f, 0.f, 1.f }, + { 0.f, 0.f, 1.f }, + { 0.f, 0.f, 1.f }, + { -1.f, 0.f, 0.f } +}; + +void R_DrawSkyBox (void) +{ + int i, j, k; + vec3_t v; + float s, t; + + //Fog_DisableGFog(); + //Fog_SetColorForSkyS(); + + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + + float skydepth = 1000.0f; + + for (i=0 ; i<5 ; i++) + { + const int vertex_count = 4; + glvert_t sky_vertices[vertex_count]; + + // check if poly needs to be drawn at all + float dot = DotProduct(skynormals[i], vpn); + // < 0 check would work at fov 90 or less, just guess a value that's high enough? + if (dot < -0.25f) continue; + + GL_Bind(skyimage[skytexorder[i]]); + + // if direction is not up, cut "down" vector to zero to only render half cube + //float upnegfact = i == 4 ? 1.0f : 0.0f; + float upnegfact = 1.0f; + + float skyboxtexsize = 256.f; + // move ever so slightly less towards forward to make edges overlap a bit, just to not have shimmering pixels between sky edges + float forwardfact = 0.99f; + + glBegin(GL_QUADS); + + sky_vertices[0].s = 0.5f / skyboxtexsize; + sky_vertices[0].t = (skyboxtexsize - .5f) / skyboxtexsize; + sky_vertices[0].x = r_origin[0] + (forwardfact * skynormals[i][0] - skyrt[i][0] - skyup[i][0] * upnegfact) * skydepth; + sky_vertices[0].y = r_origin[1] + (forwardfact * skynormals[i][1] - skyrt[i][1] - skyup[i][1] * upnegfact) * skydepth; + sky_vertices[0].z = r_origin[2] + (forwardfact * skynormals[i][2] - skyrt[i][2] - skyup[i][2] * upnegfact) * skydepth; + v[0] = sky_vertices[0].x; + v[1] = sky_vertices[0].y; + v[2] = sky_vertices[0].z; + glTexCoord2f (sky_vertices[0].s, sky_vertices[0].t); + glVertex3fv (v); + + sky_vertices[1].s = 0.5f / skyboxtexsize; + sky_vertices[1].t = 0.5f / skyboxtexsize; + sky_vertices[1].x = r_origin[0] + (forwardfact * skynormals[i][0] - skyrt[i][0] + skyup[i][0]) * skydepth; + sky_vertices[1].y = r_origin[1] + (forwardfact * skynormals[i][1] - skyrt[i][1] + skyup[i][1]) * skydepth; + sky_vertices[1].z = r_origin[2] + (forwardfact * skynormals[i][2] - skyrt[i][2] + skyup[i][2]) * skydepth; + v[0] = sky_vertices[1].x; + v[1] = sky_vertices[1].y; + v[2] = sky_vertices[1].z; + glTexCoord2f (sky_vertices[1].s, sky_vertices[1].t); + glVertex3fv (v); + + sky_vertices[2].s = (skyboxtexsize - .5f) / skyboxtexsize; + sky_vertices[2].t = 0.5f / skyboxtexsize; + sky_vertices[2].x = r_origin[0] + (forwardfact * skynormals[i][0] + skyrt[i][0] + skyup[i][0]) * skydepth; + sky_vertices[2].y = r_origin[1] + (forwardfact * skynormals[i][1] + skyrt[i][1] + skyup[i][1]) * skydepth; + sky_vertices[2].z = r_origin[2] + (forwardfact * skynormals[i][2] + skyrt[i][2] + skyup[i][2]) * skydepth; + v[0] = sky_vertices[2].x; + v[1] = sky_vertices[2].y; + v[2] = sky_vertices[2].z; + glTexCoord2f (sky_vertices[2].s, sky_vertices[2].t); + glVertex3fv (v); + + sky_vertices[3].s = (skyboxtexsize - .5f) / skyboxtexsize; + sky_vertices[3].t = (skyboxtexsize - .5f) / skyboxtexsize; + sky_vertices[3].x = r_origin[0] + (forwardfact * skynormals[i][0] + skyrt[i][0] - skyup[i][0] * upnegfact) * skydepth; + sky_vertices[3].y = r_origin[1] + (forwardfact * skynormals[i][1] + skyrt[i][1] - skyup[i][1] * upnegfact) * skydepth; + sky_vertices[3].z = r_origin[2] + (forwardfact * skynormals[i][2] + skyrt[i][2] - skyup[i][2] * upnegfact) * skydepth; + v[0] = sky_vertices[3].x; + v[1] = sky_vertices[3].y; + v[2] = sky_vertices[3].z; + glTexCoord2f (sky_vertices[3].s, sky_vertices[3].t); + glVertex3fv (v); + + glEnd(); + } + + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + + //Fog_SetColorForSkyE(); //setup for Sky + //Fog_EnableGFog(); //setup for Sky +} + +//=============================================================== + +/* +============= +R_InitSky + +A sky texture is 256*128, with the right side being a masked overlay +============== +*/ +void R_InitSky (miptex_t *mt) +{ + int i, j, p; + byte *src; + unsigned trans[128*128]; + unsigned transpix; + int r, g, b; + unsigned *rgba; + extern int skytexturenum; + + src = (byte *)mt + mt->offsets[0]; + + // make an average value for the back to avoid + // a fringe on the top level + + r = g = b = 0; + for (i=0 ; i<128 ; i++) + for (j=0 ; j<128 ; j++) + { + p = src[i*256 + j + 128]; + rgba = &d_8to24table[p]; + trans[(i*128) + j] = *rgba; + r += ((byte *)rgba)[0]; + g += ((byte *)rgba)[1]; + b += ((byte *)rgba)[2]; + } + + ((byte *)&transpix)[0] = r/(128*128); + ((byte *)&transpix)[1] = g/(128*128); + ((byte *)&transpix)[2] = b/(128*128); + ((byte *)&transpix)[3] = 0; + + + if (!solidskytexture) + solidskytexture = texture_extension_number++; + GL_Bind (solidskytexture ); + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + + for (i=0 ; i<128 ; i++) + for (j=0 ; j<128 ; j++) + { + p = src[i*256 + j]; + if (p == 0) + trans[(i*128) + j] = transpix; + else + trans[(i*128) + j] = d_8to24table[p]; + } + + if (!alphaskytexture) + alphaskytexture = texture_extension_number++; + GL_Bind(alphaskytexture); + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + diff --git a/source/ctr/gl/gl_warp_sin.h b/source/ctr/gl/gl_warp_sin.h new file mode 100644 index 0000000..22976a7 --- /dev/null +++ b/source/ctr/gl/gl_warp_sin.h @@ -0,0 +1,51 @@ +/* +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. + +*/ + 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677, + 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916, + 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998, + 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632, + 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068, + 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368, + 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562, + 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759, + 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222, + 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394, + 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883, + 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398, + 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647, + 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193, + 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281, + 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633, + 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677, + -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916, + -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998, + -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632, + -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068, + -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368, + -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562, + -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759, + -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222, + -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394, + -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883, + -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398, + -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647, + -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193, + -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281, + -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633, diff --git a/source/ctr/glquake.h b/source/ctr/glquake.h new file mode 100644 index 0000000..85a4a65 --- /dev/null +++ b/source/ctr/glquake.h @@ -0,0 +1,368 @@ +/* +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. + +*/ + +#ifdef _WIN32 +#include +#endif + +#include +#include + +void GL_BeginRendering (int *x, int *y, int *width, int *height); +void GL_EndRendering (void); + + +#ifdef _WIN32 +// Function prototypes for the Texture Object Extension routines +typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *, + const GLboolean *); +typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint); +typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *); +typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *); +typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint); +typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *, + const GLclampf *); +typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *); + +extern BINDTEXFUNCPTR bindTexFunc; +extern DELTEXFUNCPTR delTexFunc; +extern TEXSUBIMAGEPTR TexSubImage2DFunc; +#endif + +extern int texture_extension_number; +extern int texture_mode; + +extern float gldepthmin, gldepthmax; + +void GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap, qboolean alpha); +void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha); +int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel); +int GL_FindTexture (char *identifier); + +typedef struct +{ + float x, y, z; + float s, t; + float r, g, b; +} glvert_t; + +extern glvert_t glv; + +extern int glx, gly, glwidth, glheight; + +#ifdef _WIN32 +extern PROC glArrayElementEXT; +extern PROC glColorPointerEXT; +extern PROC glTexturePointerEXT; +extern PROC glVertexPointerEXT; +#endif + + +/* +--------------------------------- +half-life Render Modes. Crow_bar +--------------------------------- +*/ + +#define TEX_COLOR 1 +#define TEX_TEXTURE 2 +#define TEX_GLOW 3 +#define TEX_SOLID 4 +#define TEX_ADDITIVE 5 +#define TEX_LMPOINT 6 //for light point + +#define ISCOLOR(ent) ((ent)->rendermode == TEX_COLOR && ((ent)->rendercolor[0] <= 1|| \ + (ent)->rendercolor[1] <= 1|| \ + (ent)->rendercolor[2] <= 1)) + +#define ISTEXTURE(ent) ((ent)->rendermode == TEX_TEXTURE && (ent)->renderamt > 0 && (ent)->renderamt <= 1) +#define ISGLOW(ent) ((ent)->rendermode == TEX_GLOW && (ent)->renderamt > 0 && (ent)->renderamt <= 1) +#define ISSOLID(ent) ((ent)->rendermode == TEX_SOLID && (ent)->renderamt > 0 && (ent)->renderamt <= 1) +#define ISADDITIVE(ent) ((ent)->rendermode == TEX_ADDITIVE && (ent)->renderamt > 0 && (ent)->renderamt <= 1) + +#define ISLMPOINT(ent) ((ent)->rendermode == TEX_LMPOINT && ((ent)->rendercolor[0] <= 1|| \ + (ent)->rendercolor[1] <= 1|| \ + (ent)->rendercolor[2] <= 1)) +/* +--------------------------------- +//half-life Render Modes +--------------------------------- +*/ + + +// r_local.h -- private refresh defs + +#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) + // normalizing factor so player model works out to about + // 1 pixel per triangle +#define MAX_LBM_HEIGHT 480 + +#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf + +#define SKYSHIFT 7 +#define SKYSIZE (1 << SKYSHIFT) +#define SKYMASK (SKYSIZE - 1) + +#define BACKFACE_EPSILON 0.01 + + +void R_TimeRefresh_f (void); +void R_ReadPointFile_f (void); +texture_t *R_TextureAnimation (texture_t *base); + +typedef struct surfcache_s +{ + struct surfcache_s *next; + struct surfcache_s **owner; // NULL is an empty chunk of memory + int lightadj[MAXLIGHTMAPS]; // checked for strobe flush + int dlight; + int size; // including header + unsigned width; + unsigned height; // DEBUG only needed for debug + float mipscale; + struct texture_s *texture; // checked for animating textures + byte data[4]; // width*height elements +} surfcache_t; + +typedef enum +{ + pm_classic, pm_qmb, pm_quake3, pm_mixed +} part_mode_t; + +typedef struct +{ + pixel_t *surfdat; // destination for generated surface + int rowbytes; // destination logical width in bytes + msurface_t *surf; // description for surface to generate + fixed8_t lightadj[MAXLIGHTMAPS]; + // adjust for lightmap levels for dynamic lighting + texture_t *texture; // corrected for animating textures + int surfmip; // mipmapped ratio of surface texels / world pixels + int surfwidth; // in mipmapped texels + int surfheight; // in mipmapped texels +} drawsurf_t; + + +typedef enum { + pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2 +} ptype_t; + +typedef byte col_t[4]; + +typedef struct particle_s +{ + struct particle_s *next; + vec3_t org, endorg; + col_t color; + float growth; + vec3_t vel; + float ramp; + ptype_t type; + float rotangle; + float rotspeed; + float size; + float start; + float die; + byte hit; + byte texindex; + byte bounces; +} particle_t; + + +//==================================================== + + +extern entity_t r_worldentity; +extern qboolean r_cache_thrash; // compatability +extern vec3_t modelorg, r_entorigin; +extern entity_t *currententity; +extern int r_visframecount; // ??? what difs? +extern int r_framecount; +extern mplane_t frustum[4]; +extern int c_brush_polys, c_alias_polys; + + +// +// view origin +// +extern vec3_t vup; +extern vec3_t vpn; +extern vec3_t vright; +extern vec3_t r_origin; + +// +// screen size info +// +extern refdef_t r_refdef; +extern mleaf_t *r_viewleaf, *r_oldviewleaf; +extern texture_t *r_notexture_mip; +extern int d_lightstylevalue[256]; // 8.8 fraction of base light value + +extern qboolean envmap; +extern int currenttexture; +extern int cnttextures[2]; +extern int particletexture; +extern int playertextures; + +extern int skytexturenum; // index in cl.loadmodel, not gl texture object + +extern cvar_t r_norefresh; +extern cvar_t r_drawentities; +extern cvar_t r_drawworld; +extern cvar_t r_drawviewmodel; +extern cvar_t r_speeds; +extern cvar_t r_waterwarp; +extern cvar_t r_fullbright; +extern cvar_t r_lightmap; +extern cvar_t r_shadows; +extern cvar_t r_mirroralpha; +extern cvar_t r_wateralpha; +extern cvar_t r_dynamic; +extern cvar_t r_novis; +extern cvar_t r_farclip; +extern cvar_t r_skyfog; + +extern cvar_t r_laserpoint; +extern cvar_t r_particle_count; +extern cvar_t r_part_explosions; +extern cvar_t r_part_trails; +extern cvar_t r_part_sparks; +extern cvar_t r_part_spikes; +extern cvar_t r_part_gunshots; +extern cvar_t r_part_blood; +extern cvar_t r_part_telesplash; +extern cvar_t r_part_blobs; +extern cvar_t r_part_lavasplash; +extern cvar_t r_part_flames; +extern cvar_t r_part_lightning; +extern cvar_t r_part_flies; +extern cvar_t r_bounceparticles; +extern cvar_t r_explosiontype; +extern cvar_t r_part_muzzleflash; +extern cvar_t r_flametype; +extern cvar_t r_bounceparticles; +extern cvar_t r_decal_blood; +extern cvar_t r_decal_bullets; +extern cvar_t r_decal_sparks; +extern cvar_t r_decal_explosions; +extern cvar_t r_coronas; +extern cvar_t r_model_brightness; + +extern cvar_t gl_clear; +extern cvar_t gl_cull; +extern cvar_t gl_poly; +extern cvar_t gl_texsort; +extern cvar_t gl_smoothmodels; +extern cvar_t gl_affinemodels; +extern cvar_t gl_polyblend; +extern cvar_t gl_keeptjunctions; +extern cvar_t gl_reporttjunctions; +extern cvar_t gl_flashblend; +extern cvar_t gl_nocolors; +extern cvar_t gl_doubleeyes; + +extern int gl_lightmap_format; +extern int gl_solid_format; +extern int gl_alpha_format; + +extern cvar_t gl_max_size; +extern cvar_t gl_playermip; + +extern int mirrortexturenum; // quake texturenum, not gltexturenum +extern qboolean mirror; +extern mplane_t *mirror_plane; + +extern float r_world_matrix[16]; + +extern const char *gl_vendor; +extern const char *gl_renderer; +extern const char *gl_version; +extern const char *gl_extensions; + +void R_TranslatePlayerSkin (int playernum); +void GL_Bind (int texnum); + +// Multitexture +#define TEXTURE0_SGIS 0x835E +#define TEXTURE1_SGIS 0x835F + +#ifndef _WIN32 +#define APIENTRY /* */ +#endif + +typedef void (APIENTRY *lpMTexFUNC) (GLenum, GLfloat, GLfloat); +typedef void (APIENTRY *lpSelTexFUNC) (GLenum); +extern lpMTexFUNC qglMTexCoord2fSGIS; +extern lpSelTexFUNC qglSelectTextureSGIS; + +extern qboolean gl_mtexable; + +void GL_DisableMultitexture(void); +void GL_EnableMultitexture(void); + +//johnfitz -- fog functions called from outside gl_fog.c +void Fog_ParseServerMessage (void); +float *Fog_GetColor (void); +float Fog_GetDensity (void); +void Fog_EnableGFog (void); +void Fog_DisableGFog (void); +void Fog_StartAdditive (void); +void Fog_StopAdditive (void); +void Fog_SetupFrame (void); +void Fog_NewMap (void); +void Fog_Init (void); +void Fog_SetupState (void); + +void Sky_Init (void); +void Sky_NewMap (void); + +qboolean VID_Is8bit(void); + + +// naievil -- fixme: none of these work +//----------------------------------------------------- +void QMB_InitParticles (void); +void QMB_ClearParticles (void); +void QMB_DrawParticles (void); +void QMB_Q3TorchFlame (vec3_t org, float size); +void QMB_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count); +void QMB_RocketTrail (vec3_t start, vec3_t end, trail_type_t type); +void QMB_BlobExplosion (vec3_t org); +void QMB_ParticleExplosion (vec3_t org); +void QMB_LavaSplash (vec3_t org); +void QMB_TeleportSplash (vec3_t org); +void QMB_InfernoFlame (vec3_t org); +void QMB_StaticBubble (entity_t *ent); +void QMB_ColorMappedExplosion (vec3_t org, int colorStart, int colorLength); +void QMB_TorchFlame (vec3_t org); +void QMB_FlameGt (vec3_t org, float size, float time); +void QMB_BigTorchFlame (vec3_t org); +void QMB_ShamblerCharge (vec3_t org); +void QMB_LightningBeam (vec3_t start, vec3_t end); +//void QMB_GenSparks (vec3_t org, byte col[3], float count, float size, float life); +void QMB_EntityParticles (entity_t *ent); +void QMB_MuzzleFlash (vec3_t org); +void QMB_RayFlash (vec3_t org, float weapon); +void QMB_MuzzleFlashLG (vec3_t org); +void QMB_Q3Gunshot (vec3_t org, int skinnum, float alpha); +void QMB_Q3Teleport (vec3_t org, float alpha); +void QMB_Q3TorchFlame (vec3_t org, float size); + +extern qboolean qmb_initialized; \ No newline at end of file diff --git a/source/ctr/in_ctr.c b/source/ctr/in_ctr.c new file mode 100644 index 0000000..237a9b0 --- /dev/null +++ b/source/ctr/in_ctr.c @@ -0,0 +1,240 @@ +/* +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. + +*/ +// in_ctr.c -- for the Nintendo 3DS + +#include "../quakedef.h" +#include +#include <3ds.h> + +extern int bind_grab; + +extern bool new3ds_flag; +extern bool croshhairmoving; +extern float crosshair_opacity; + +extern cvar_t in_analog_strafe; +extern cvar_t in_x_axis_adjust; +extern cvar_t in_y_axis_adjust; +extern cvar_t in_mlook; //Heffo - mlook cvar + +cvar_t in_anub_mode = {"in_anub_mode", "0", true}; + +void IN_Init (void) +{ + Cvar_RegisterVariable (&in_analog_strafe); + Cvar_RegisterVariable (&in_anub_mode); + + if (new3ds_flag) { + Cvar_SetValue("in_anub_mode", 1); + } +} + +void IN_Shutdown (void) +{ + +} + +void IN_Commands (void) +{ + +} + +float IN_CalcInput(int axis, float speed, float tolerance, float acceleration) { + + float value = ((float) axis / 154.0f); + + if (value == 0.0f) { + return 0.0f; + } + + float abs_value = fabs(value); + + if (abs_value < tolerance) { + return 0.0f; + } + + abs_value -= tolerance; + abs_value /= (1.0f - tolerance); + abs_value = powf(abs_value, acceleration); + abs_value *= speed; + + if (value < 0.0f) { + value = -abs_value; + } else { + value = abs_value; + } + return value; +} + +extern cvar_t scr_fov; +extern int original_fov, final_fov; +touchPosition old_touch, cur_touch; +void IN_Move (usercmd_t *cmd) +{ + // Touch based viewangles based on Quake2CTR + // This was originally based on ctrQuake, however + // that implementation was less elegant and had + // a weird jerk bug when tapping the screen. + if(hidKeysDown() & KEY_TOUCH) + hidTouchRead(&old_touch); + + if((hidKeysHeld() & KEY_TOUCH)) + { + hidTouchRead(&cur_touch); + + if(cur_touch.px < 268) + { + int tx = cur_touch.px - old_touch.px; + int ty = cur_touch.py - old_touch.py; + + if(m_pitch.value < 0) + ty = -ty; + + cl.viewangles[YAW] -= abs(tx) > 1 ? tx * sensitivity.value * 0.33f : 0; + cl.viewangles[PITCH] += abs(ty) > 1 ? ty * sensitivity.value * 0.33f : 0; + } + + old_touch = cur_touch; + } + + // TODO: Detect circle pad pro? + circlePosition left; + circlePosition right; + + V_StopPitchDrift(); + + // Read the pad states + hidCircleRead(&left); + hidCstickRead(&right); + + // Convert the inputs to floats in the range [-1, 1]. + // Implement the dead zone. + float speed; + float deadZone = in_tolerance.value; + float acceleration = in_acceleration.value; + float look_x, look_y; + + // + // Analog look tweaks + // + speed = sensitivity.value; + + if (!in_anub_mode.value) + speed -= 2; + else + speed += 8; + + // cut look speed in half when facing enemy, unless mag is empty + if ((in_aimassist.value) && (sv_player->v.facingenemy == 1) && cl.stats[STAT_CURRENTMAG] > 0) { + speed *= 0.5; + } + // additionally, slice look speed when ADS/scopes + if (cl.stats[STAT_ZOOM] == 1) + speed *= 0.5; + else if (cl.stats[STAT_ZOOM] == 2) + speed *= 0.25; + + // Are we using the left or right stick for looking? + if (!in_anub_mode.value) { // Left + look_x = IN_CalcInput(left.dx, speed, deadZone, acceleration); + look_y = IN_CalcInput(left.dy, speed, deadZone, acceleration); + } else { // Right + look_x = IN_CalcInput(right.dx, speed, deadZone, acceleration); + look_y = IN_CalcInput(right.dy, speed, deadZone, acceleration); + } + + const float yawScale = 30.0f; + cl.viewangles[YAW] -= yawScale * look_x * host_frametime; + + // Set the pitch. + const bool invertPitch = m_pitch.value < 0; + const float pitchScale = yawScale * (invertPitch ? 1 : -1); + + cl.viewangles[PITCH] += pitchScale * look_y * host_frametime; + + // Don't look too far up or down. + if (cl.viewangles[PITCH] > 80.0f) + cl.viewangles[PITCH] = 80.0f; + if (cl.viewangles[PITCH] < -70.0f) + cl.viewangles[PITCH] = -70.0f; + + // Ability to move with the left nub on NEW model systems + float move_x, move_y; + float input_x, input_y; + + if (in_anub_mode.value) { + input_x = left.dx; + input_y = left.dy; + } else { + input_x = right.dx; + input_y = right.dy; + } + + cl_backspeed = cl_forwardspeed = cl_sidespeed = sv_player->v.maxspeed; + cl_sidespeed *= 0.8; + cl_backspeed *= 0.7; + + move_x = IN_CalcInput(input_x, cl_sidespeed, deadZone, acceleration); + + if (input_y > 0) + move_y = IN_CalcInput(input_y, cl_forwardspeed, deadZone, acceleration); + else + move_y = IN_CalcInput(input_y, cl_backspeed, deadZone, acceleration); + + // cypress -- explicitly setting instead of adding so we always prioritize + // analog movement over standard bindings if both are at play + if (move_x != 0 || move_y != 0) { + cmd->sidemove = move_x; + cmd->forwardmove = move_y; + } + + // crosshair stuff + if (input_x < 50 && input_x > -50 && input_y < 50 && input_y > -50) { + croshhairmoving = false; + + crosshair_opacity += 22; + + if (crosshair_opacity >= 255) + crosshair_opacity = 255; + } else { + croshhairmoving = true; + crosshair_opacity -= 8; + if (crosshair_opacity <= 128) + crosshair_opacity = 128; + } +} + +// +// ctr software keyboard courtesy of libctru samples +// +void IN_SwitchKeyboard(void) +{ + static SwkbdState swkbd; + static char console_buffer[64]; + SwkbdButton button = SWKBD_BUTTON_NONE; + + swkbdInit(&swkbd, SWKBD_TYPE_QWERTY, 2, -1); + swkbdSetInitialText(&swkbd, console_buffer); + swkbdSetHintText(&swkbd, "Enter Quake console command"); + swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Send", true); + button = swkbdInputText(&swkbd, console_buffer, sizeof(console_buffer)); + + Cbuf_AddText(va("%s\n", console_buffer)); +} \ No newline at end of file diff --git a/source/ctr/keys.c b/source/ctr/keys.c new file mode 100644 index 0000000..25078b8 --- /dev/null +++ b/source/ctr/keys.c @@ -0,0 +1,947 @@ +/* +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" + +/* + +key up events are sent even if in console mode + +*/ + + +#define MAXCMDLINE 256 +char key_lines[32][MAXCMDLINE]; +int key_linepos; +int shift_down=false; +int key_lastpress; + +int edit_line=0; +int history_line=0; + +keydest_t key_dest; + +int key_count; // incremented every key event + +char *keybindings[256]; +char *dtbindings[256]; +qboolean consolekeys[256]; // if true, can't be rebound while in console +qboolean menubound[256]; // if true, can't be rebound while in menu +int keyshift[256]; // key to map to if shift held down in console +int key_repeats[256]; // if > 1, it is autorepeating +qboolean keydown[256]; + +typedef struct +{ + char *name; + int keynum; +} keyname_t; + +keyname_t keynames[] = +{ + #ifdef _3DS + {"TAB", K_TAB}, + {"START", K_ESCAPE}, + {"SELECT", K_SELECT}, + {"SPACE", K_SPACE}, + {"BACKSPACE", K_BACKSPACE}, + {"PADUP", K_UPARROW}, + {"PADDOWN", K_DOWNARROW}, + {"PADLEFT", K_LEFTARROW}, + {"PADRIGHT", K_RIGHTARROW}, + #else + {"TAB", K_TAB}, + {"ENTER", K_ENTER}, + {"ESCAPE", K_ESCAPE}, + {"SPACE", K_SPACE}, + {"BACKSPACE", K_BACKSPACE}, + {"UPARROW", K_UPARROW}, + {"DOWNARROW", K_DOWNARROW}, + {"LEFTARROW", K_LEFTARROW}, + {"RIGHTARROW", K_RIGHTARROW}, + #endif + + {"ALT", K_ALT}, + {"CTRL", K_CTRL}, + {"SHIFT", K_SHIFT}, + + {"F1", K_F1}, + {"F2", K_F2}, + {"F3", K_F3}, + {"F4", K_F4}, + {"F5", K_F5}, + {"F6", K_F6}, + {"F7", K_F7}, + {"F8", K_F8}, + {"F9", K_F9}, + {"F10", K_F10}, + {"F11", K_F11}, + {"F12", K_F12}, + + {"INS", K_INS}, + {"DEL", K_DEL}, + {"PGDN", K_PGDN}, + {"PGUP", K_PGUP}, + {"HOME", K_HOME}, + {"END", K_END}, + + {"MOUSE1", K_MOUSE1}, + {"MOUSE2", K_MOUSE2}, + {"MOUSE3", K_MOUSE3}, + + {"JOY1", K_JOY1}, + {"JOY2", K_JOY2}, + {"JOY3", K_JOY3}, + {"JOY4", K_JOY4}, + + #ifdef _3DS + {"ABUTTON", K_AUX1}, + {"BBUTTON", K_AUX2}, + {"XBUTTON", K_AUX3}, + {"YBUTTON", K_AUX4}, + {"LTRIGGER", K_AUX5}, + {"ZLTRIGGER", K_AUX6}, + {"RTRIGGER", K_AUX7}, + {"ZRTRIGGER", K_AUX8}, + {"T1", K_AUX9}, + {"T2", K_AUX10}, + {"T3", K_AUX11}, + {"T4", K_AUX12}, + {"T5", K_AUX13}, + {"T6", K_AUX14}, + {"AUX15", K_AUX15}, + {"AUX16", K_AUX16}, + {"AUX17", K_AUX17}, + {"AUX18", K_AUX18}, + {"AUX19", K_AUX19}, + {"AUX20", K_AUX20}, + {"AUX21", K_AUX21}, + {"AUX22", K_AUX22}, + {"AUX23", K_AUX23}, + {"AUX24", K_AUX24}, + {"AUX25", K_AUX25}, + {"AUX26", K_AUX26}, + {"AUX27", K_AUX27}, + {"AUX28", K_AUX28}, + {"AUX29", K_AUX29}, + {"AUX30", K_AUX30}, + {"AUX31", K_AUX31}, + {"AUX32", K_AUX32}, + #else + {"AUX1", K_AUX1}, + {"AUX2", K_AUX2}, + {"AUX3", K_AUX3}, + {"AUX4", K_AUX4}, + {"AUX5", K_AUX5}, + {"AUX6", K_AUX6}, + {"AUX7", K_AUX7}, + {"AUX8", K_AUX8}, + {"AUX9", K_AUX9}, + {"AUX10", K_AUX10}, + {"AUX11", K_AUX11}, + {"AUX12", K_AUX12}, + {"AUX13", K_AUX13}, + {"AUX14", K_AUX14}, + {"AUX15", K_AUX15}, + {"AUX16", K_AUX16}, + {"AUX17", K_AUX17}, + {"AUX18", K_AUX18}, + {"AUX19", K_AUX19}, + {"AUX20", K_AUX20}, + {"AUX21", K_AUX21}, + {"AUX22", K_AUX22}, + {"AUX23", K_AUX23}, + {"AUX24", K_AUX24}, + {"AUX25", K_AUX25}, + {"AUX26", K_AUX26}, + {"AUX27", K_AUX27}, + {"AUX28", K_AUX28}, + {"AUX29", K_AUX29}, + {"AUX30", K_AUX30}, + {"AUX31", K_AUX31}, + {"AUX32", K_AUX32}, + #endif + + {"PAUSE", K_PAUSE}, + + {"MWHEELUP", K_MWHEELUP}, + {"MWHEELDOWN", K_MWHEELDOWN}, + + {"SEMICOLON", ';'}, // because a raw semicolon seperates commands + + {NULL,0} +}; + +/* +============================================================================== + + LINE TYPING INTO THE CONSOLE + +============================================================================== +*/ + + +/* +==================== +Key_Console + +Interactive line editing and console scrollback +==================== +*/ +extern qboolean console_enabled; +void Key_Console (int key) +{ + char *cmd; + + if (key == K_SELECT) + { + IN_SwitchKeyboard(); + return; + } + + if (key == K_JOY3 || key == K_ENTER) + { + Cbuf_AddText (key_lines[edit_line]+1); // skip the > + Cbuf_AddText ("\n"); + Con_Printf ("%s\n",key_lines[edit_line]); + edit_line = (edit_line + 1) & 31; + history_line = edit_line; + key_lines[edit_line][0] = ']'; + key_linepos = 1; + if (cls.state == ca_disconnected) + SCR_UpdateScreen (); // force an update, because the command + // may take some time + // for clientside cmds + if (cls.state == ca_connected){ + pr_global_struct->CMD_STRING = (key_lines[edit_line-1]+1 - pr_strings); + PR_ExecuteProgram (pr_global_struct->ParseClientCommand); + } + return; + } + + if (key == K_TAB) + { // command completion + cmd = Cmd_CompleteCommand (key_lines[edit_line]+1); + if (!cmd) + cmd = Cvar_CompleteVariable (key_lines[edit_line]+1); + if (cmd) + { + Q_strcpy (key_lines[edit_line]+1, cmd); + key_linepos = Q_strlen(cmd)+1; + key_lines[edit_line][key_linepos] = ' '; + key_linepos++; + key_lines[edit_line][key_linepos] = 0; + return; + } + } + + if (key == K_BACKSPACE || key == K_LEFTARROW) + { + if (key_linepos > 1) + key_linepos--; + return; + } + + if (key == K_UPARROW) + { + do + { + history_line = (history_line - 1) & 31; + } while (history_line != edit_line + && !key_lines[history_line][1]); + if (history_line == edit_line) + history_line = (edit_line+1)&31; + Q_strcpy(key_lines[edit_line], key_lines[history_line]); + key_linepos = Q_strlen(key_lines[edit_line]); + return; + } + + if (key == K_DOWNARROW) + { + if (history_line == edit_line) return; + do + { + history_line = (history_line + 1) & 31; + } + while (history_line != edit_line + && !key_lines[history_line][1]); + if (history_line == edit_line) + { + key_lines[edit_line][0] = ']'; + key_linepos = 1; + } + else + { + Q_strcpy(key_lines[edit_line], key_lines[history_line]); + key_linepos = Q_strlen(key_lines[edit_line]); + } + return; + } + + if (key == K_PGUP || key==K_MWHEELUP) + { + con_backscroll += 2; + if (con_backscroll > con_totallines - (vid.height>>3) - 1) + con_backscroll = con_totallines - (vid.height>>3) - 1; + return; + } + + if (key == K_PGDN || key==K_MWHEELDOWN) + { + con_backscroll -= 2; + if (con_backscroll < 0) + con_backscroll = 0; + return; + } + + if (key == K_HOME) + { + con_backscroll = con_totallines - (vid.height>>3) - 1; + return; + } + + if (key == K_END) + { + console_enabled = false; + con_backscroll = 0; + return; + } + + if (key < 32 || key > 127) + return; // non printable + + if (key_linepos < MAXCMDLINE-1) + { + key_lines[edit_line][key_linepos] = key; + key_linepos++; + key_lines[edit_line][key_linepos] = 0; + } + +} + +//============================================================================ + +char chat_buffer[32]; +qboolean team_message = false; + +void Key_Message (int key) +{ + static int chat_bufferlen = 0; + + if (key == K_ENTER) + { + if (team_message) + Cbuf_AddText ("say_team \""); + else + Cbuf_AddText ("say \""); + Cbuf_AddText(chat_buffer); + Cbuf_AddText("\"\n"); + + key_dest = key_game; + chat_bufferlen = 0; + chat_buffer[0] = 0; + return; + } + + if (key == K_ESCAPE) + { + console_enabled = false; + key_dest = key_game; + chat_bufferlen = 0; + chat_buffer[0] = 0; + return; + } + + if (key < 32 || key > 127) + return; // non printable + + if (key == K_BACKSPACE) + { + if (chat_bufferlen) + { + chat_bufferlen--; + chat_buffer[chat_bufferlen] = 0; + } + return; + } + + if (chat_bufferlen == 31) + return; // all full + + chat_buffer[chat_bufferlen++] = key; + chat_buffer[chat_bufferlen] = 0; +} + +//============================================================================ + + +/* +=================== +Key_StringToKeynum + +Returns a key number to be used to index keybindings[] by looking at +the given string. Single ascii characters return themselves, while +the K_* names are matched up. +=================== +*/ +int Key_StringToKeynum (char *str) +{ + keyname_t *kn; + + if (!str || !str[0]) + return -1; + if (!str[1]) + return str[0]; + + for (kn=keynames ; kn->name ; kn++) + { + if (!Q_strcasecmp(str,kn->name)) + return kn->keynum; + } + return -1; +} + +/* +=================== +Key_KeynumToString + +Returns a string (either a single ascii char, or a K_* name) for the +given keynum. +FIXME: handle quote special (general escape sequence?) +=================== +*/ +char *Key_KeynumToString (int keynum) +{ + keyname_t *kn; + static char tinystr[2]; + + if (keynum == -1) + return ""; + if (keynum > 32 && keynum < 127) + { // printable ascii + tinystr[0] = keynum; + tinystr[1] = 0; + return tinystr; + } + + for (kn=keynames ; kn->name ; kn++) + if (keynum == kn->keynum) + return kn->name; + + return ""; +} + + +/* +=================== +Key_SetBinding +=================== +*/ +void Key_SetBinding (int keynum, char *binding) +{ + char *new; + int l; + + if (keynum == -1) + return; + +// free old bindings + if (keybindings[keynum]) + { + Z_Free (keybindings[keynum]); + keybindings[keynum] = NULL; + } + +// allocate memory for new binding + l = Q_strlen (binding); + new = Z_Malloc (l+1); + Q_strcpy (new, binding); + new[l] = 0; + keybindings[keynum] = new; +} + +/* +=================== +Key_SetDTBinding +=================== +*/ +void Key_SetDTBinding (int keynum, char *binding) +{ + char *new; + int l; + + if (keynum == -1) + return; + +// free old bindings + if (dtbindings[keynum]) + { + Z_Free (dtbindings[keynum]); + dtbindings[keynum] = NULL; + } + +// allocate memory for new binding + l = Q_strlen (binding); + new = Z_Malloc (l+1); + Q_strcpy (new, binding); + new[l] = 0; + dtbindings[keynum] = new; +} + +/* +=================== +Key_Unbind_f +=================== +*/ +void Key_Unbind_f (void) +{ + int b; + + if (Cmd_Argc() != 2) + { + Con_Printf ("unbind : remove commands from a key\n"); + return; + } + + b = Key_StringToKeynum (Cmd_Argv(1)); + if (b==-1) + { + Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); + return; + } + + Key_SetBinding (b, ""); +} + +void Key_Unbindall_f (void) +{ + int i; + + for (i=0 ; i<256 ; i++) + if (keybindings[i]) + Key_SetBinding (i, ""); +} + + +/* +=================== +Key_Bind_f +=================== +*/ +void Key_Bind_f (void) +{ + int i, c, b; + char cmd[1024]; + + c = Cmd_Argc(); + + if (c != 2 && c != 3) + { + Con_Printf ("bind [command] : attach a command to a key\n"); + return; + } + b = Key_StringToKeynum (Cmd_Argv(1)); + if (b==-1) + { + Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); + return; + } + + if (c == 2) + { + if (keybindings[b]) + Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] ); + else + Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); + return; + } + +// copy the rest of the command line + cmd[0] = 0; // start out with a null string + for (i=2 ; i< c ; i++) + { + if (i > 2) + strcat (cmd, " "); + strcat (cmd, Cmd_Argv(i)); + } + + Key_SetBinding (b, cmd); +} + +/* +=================== +Key_Binddt_f +=================== +*/ +void Key_Binddt_f (void) +{ + int i, c, b; + char cmd[1024]; + + c = Cmd_Argc(); + + if (c != 2 && c != 3) + { + Con_Printf ("binddt [command] : attach a command to a double tap key\n"); + return; + } + b = Key_StringToKeynum (Cmd_Argv(1)); + if (b==-1) + { + Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); + return; + } + + if (c == 2) + { + if (dtbindings[b]) + Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), dtbindings[b] ); + else + Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); + return; + } + +// copy the rest of the command line + cmd[0] = 0; // start out with a null string + for (i=2 ; i< c ; i++) + { + if (i > 2) + strcat (cmd, " "); + strcat (cmd, Cmd_Argv(i)); + } + + Key_SetDTBinding (b, cmd); +} + +/* +============ +Key_WriteBindings + +Writes lines containing "bind key value" +============ +*/ +void Key_WriteBindings (FILE *f) +{ + int i; + + for (i=0 ; i<256 ; i++) + if (keybindings[i]) + if (*keybindings[i]) + fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]); +} + +/* +============ +Key_WriteDTBindings + +Writes lines containing "binddt key value" +============ +*/ +void Key_WriteDTBindings (FILE *f) +{ + int i; + + for (i=0 ; i<256 ; i++) + if (dtbindings[i]) + if (*dtbindings[i]) + fprintf (f, "binddt \"%s\" \"%s\"\n", Key_KeynumToString(i), dtbindings[i]); +} + +/* +=================== +Key_Init +=================== +*/ +void Key_Init (void) +{ + int i; + + for (i=0 ; i<32 ; i++) + { + key_lines[i][0] = ']'; + key_lines[i][1] = 0; + } + key_linepos = 1; + +// +// init ascii characters in console mode +// + for (i=32 ; i<128 ; i++) + consolekeys[i] = true; + consolekeys[K_ENTER] = true; + consolekeys[K_TAB] = true; + consolekeys[K_LEFTARROW] = true; + consolekeys[K_RIGHTARROW] = true; + consolekeys[K_UPARROW] = true; + consolekeys[K_DOWNARROW] = true; + consolekeys[K_BACKSPACE] = true; + consolekeys[K_PGUP] = true; + consolekeys[K_PGDN] = true; + consolekeys[K_SHIFT] = true; + consolekeys[K_MWHEELUP] = true; + consolekeys[K_MWHEELDOWN] = true; + consolekeys[K_SELECT] = true; + consolekeys['`'] = false; + consolekeys['~'] = false; + + for (i=0 ; i<256 ; i++) + keyshift[i] = i; + for (i='a' ; i<='z' ; i++) + keyshift[i] = i - 'a' + 'A'; + keyshift['1'] = '!'; + keyshift['2'] = '@'; + keyshift['3'] = '#'; + keyshift['4'] = '$'; + keyshift['5'] = '%'; + keyshift['6'] = '^'; + keyshift['7'] = '&'; + keyshift['8'] = '*'; + keyshift['9'] = '('; + keyshift['0'] = ')'; + keyshift['-'] = '_'; + keyshift['='] = '+'; + keyshift[','] = '<'; + keyshift['.'] = '>'; + keyshift['/'] = '?'; + keyshift[';'] = ':'; + keyshift['\''] = '"'; + keyshift['['] = '{'; + keyshift[']'] = '}'; + keyshift['`'] = '~'; + keyshift['\\'] = '|'; + + menubound[K_ESCAPE] = true; + for (i=0 ; i<12 ; i++) + menubound[K_F1+i] = true; + +// +// register our functions +// + Cmd_AddCommand ("bind",Key_Bind_f); + Cmd_AddCommand ("binddt",Key_Binddt_f); + Cmd_AddCommand ("unbind",Key_Unbind_f); + Cmd_AddCommand ("unbindall",Key_Unbindall_f); + + +} + +/* +=================== +Key_Event + +Called by the system between frames for both key up and key down events +Should NOT be called during an interrupt! +=================== +*/ +int lastkey; +double lastkeytime; +int oldkey; +double oldkeytime; +void Key_Event (int key, qboolean down) +{ + char *kb; + char cmd[1024]; + + oldkey = lastkey; + keydown[key] = down; + lastkey = key; + + keydown[key] = down; + + if (!down) + key_repeats[key] = 0; + + key_lastpress = key; + key_count++; + if (key_count <= 0) + { + return; // just catching keys for Con_NotifyBox + } + +// update auto-repeat status + if (down) + { + oldkeytime = lastkeytime; + lastkeytime = Sys_FloatTime(); + key_repeats[key]++; + if (key != K_BACKSPACE && key != K_PAUSE && key_repeats[key] > 1) + { + return; // ignore most autorepeats + } + + if (key >= 200 && !keybindings[key]) + Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) ); + } + + if (key == K_SHIFT) + shift_down = down; + +// +// handle escape specialy, so the user can never unbind it +// + if (key == K_ESCAPE) + { + if (!down) + return; + switch (key_dest) + { + case key_message: + Key_Message (key); + break; + case key_menu: + case key_menu_pause: + M_Keydown (key); + break; + case key_game: + case key_console: + console_enabled = false; + M_ToggleMenu_f (); + break; + default: + Sys_Error ("Bad key_dest"); + } + return; + } + +// +// key up events only generate commands if the game key binding is +// a button command (leading + sign). These will occur even in console mode, +// to keep the character from continuing an action started before a console +// switch. Button commands include the kenum as a parameter, so multiple +// downs can be matched with ups +// + if (!down) + { + kb = keybindings[key]; + if (kb && kb[0] == '+') + { + sprintf (cmd, "-%s %i\n", kb+1, key); + Cbuf_AddText (cmd); + } + if (keyshift[key] != key) + { + kb = keybindings[keyshift[key]]; + if (kb && kb[0] == '+') + { + sprintf (cmd, "-%s %i\n", kb+1, key); + Cbuf_AddText (cmd); + } + } + return; + } + +// +// during demo playback, most keys bring up the main menu +// + if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game) + { + M_ToggleMenu_f (); + return; + } + +// +// if not a consolekey, send to the interpreter no matter what mode is +// + if ( ((key_dest == key_menu || key_dest == key_menu_pause) && menubound[key]) + || (key_dest == key_console && !consolekeys[key]) + || (key_dest == key_game && ( !con_forcedup || !consolekeys[key] ) ) ) + { + if (oldkey == key && ((oldkeytime + 0.3) > lastkeytime)) + { + kb = dtbindings[key]; + if (kb) + { + if (kb[0] == '+') + { // button commands add keynum as a parm + sprintf (cmd, kb, key); + Cbuf_AddText (cmd); + } + else + { + Cbuf_AddText (kb); + Cbuf_AddText ("\n"); + } + } + oldkey = 0; + oldkeytime = 0; + lastkeytime = 0; + lastkey = 0; + } + + kb = keybindings[key]; + if (kb) + { + if (kb[0] == '+') + { // button commands add keynum as a parm + sprintf (cmd, "%s %i\n", kb, key); + Cbuf_AddText (cmd); + } + else + { + Cbuf_AddText (kb); + Cbuf_AddText ("\n"); + } + } + return; + } + + if (!down) + return; // other systems only care about key down events + + if (shift_down) + { + key = keyshift[key]; + } + + switch (key_dest) + { + case key_message: + Key_Message (key); + break; + case key_menu: + case key_menu_pause: + M_Keydown (key); + break; + + case key_game: + case key_console: + Key_Console (key); + break; + default: + Sys_Error ("Bad key_dest"); + } +} + + +/* +=================== +Key_ClearStates +=================== +*/ +void Key_ClearStates (void) +{ + int i; + + for (i=0 ; i<256 ; i++) + { + keydown[i] = false; + key_repeats[i] = 0; + } +} + diff --git a/source/ctr/keys.h b/source/ctr/keys.h new file mode 100644 index 0000000..2203ff3 --- /dev/null +++ b/source/ctr/keys.h @@ -0,0 +1,137 @@ +/* +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. + +*/ + +// +// these are the key numbers that should be passed to Key_Event +// +#define K_TAB 9 +#define K_ENTER 13 +#define K_ESCAPE 27 +#define K_SPACE 32 + +// normal keys should be passed as lowercased ascii + +#define K_BACKSPACE 127 +#define K_UPARROW 128 +#define K_DOWNARROW 129 +#define K_LEFTARROW 130 +#define K_RIGHTARROW 131 + +#define K_ALT 132 +#define K_CTRL 133 +#define K_SHIFT 134 +#define K_F1 135 +#define K_F2 136 +#define K_F3 137 +#define K_F4 138 +#define K_F5 139 +#define K_F6 140 +#define K_F7 141 +#define K_F8 142 +#define K_F9 143 +#define K_F10 144 +#define K_F11 145 +#define K_F12 146 +#define K_INS 147 +#define K_DEL 148 +#define K_PGDN 149 +#define K_PGUP 150 +#define K_HOME 151 +#define K_END 152 + +#define K_PAUSE 255 + +// +// mouse buttons generate virtual keys +// +#define K_MOUSE1 200 +#define K_MOUSE2 201 +#define K_MOUSE3 202 + +// +// joystick buttons +// +#define K_JOY1 203 +#define K_JOY2 204 +#define K_JOY3 205 +#define K_JOY4 206 + +// +// aux keys are for multi-buttoned joysticks to generate so they can use +// the normal binding process +// +#define K_AUX1 207 +#define K_AUX2 208 +#define K_AUX3 209 +#define K_AUX4 210 +#define K_AUX5 211 +#define K_AUX6 212 +#define K_AUX7 213 +#define K_AUX8 214 +#define K_AUX9 215 +#define K_AUX10 216 +#define K_AUX11 217 +#define K_AUX12 218 +#define K_AUX13 219 +#define K_AUX14 220 +#define K_AUX15 221 +#define K_AUX16 222 +#define K_AUX17 223 +#define K_AUX18 224 +#define K_AUX19 225 +#define K_AUX20 226 +#define K_AUX21 227 +#define K_AUX22 228 +#define K_AUX23 229 +#define K_AUX24 230 +#define K_AUX25 231 +#define K_AUX26 232 +#define K_AUX27 233 +#define K_AUX28 234 +#define K_AUX29 235 +#define K_AUX30 236 +#define K_AUX31 237 +#define K_AUX32 238 + +// JACK: Intellimouse(c) Mouse Wheel Support + +#define K_MWHEELUP 239 +#define K_MWHEELDOWN 240 + +// naievil -- this is for using the select button for a game key, which is important +#define K_SELECT 241 + + + +typedef enum {key_game, key_console, key_message, key_menu, key_menu_pause} keydest_t; + +extern keydest_t key_dest; +extern char *keybindings[256]; +extern char *dtbindings[256]; +extern int key_repeats[256]; +extern int key_count; // incremented every key event +extern int key_lastpress; + +void Key_Event (int key, qboolean down); +void Key_Init (void); +void Key_WriteBindings (FILE *f); +void Key_SetBinding (int keynum, char *binding); +void Key_WriteDTBindings (FILE *f); +void Key_ClearStates (void); \ No newline at end of file diff --git a/source/ctr/menu.c b/source/ctr/menu.c new file mode 100644 index 0000000..1b1287c --- /dev/null +++ b/source/ctr/menu.c @@ -0,0 +1,2415 @@ +/* +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 + +extern cvar_t r_wateralpha; +extern cvar_t r_vsync; +extern cvar_t in_disable_analog; +extern cvar_t in_analog_strafe; +extern cvar_t in_x_axis_adjust; +extern cvar_t in_y_axis_adjust; +extern cvar_t crosshair; +extern cvar_t r_dithering; +//extern cvar_t r_retro; +extern cvar_t waypoint_mode; +extern cvar_t in_anub_mode; + +extern int loadingScreen; +extern char* loadname2; +extern char* loadnamespec; +extern qboolean loadscreeninit; + +char* game_build_date; + +// Backgrounds +qpic_t *menu_bk; + +// Map screens +qpic_t *menu_ndu; +qpic_t *menu_wh; +qpic_t *menu_wh2; +//qpic_t *menu_kn; +qpic_t *menu_ch; +//qpic_t *menu_wn; +qpic_t *menu_custom; +qpic_t *menu_cuthum; + +achievement_list_t achievement_list[MAX_ACHIEVEMENTS]; + +void (*vid_menudrawfn)(void); +void (*vid_menukeyfn)(int key); + +enum +{ + m_none, + m_start, + m_main, + m_paused_menu, + m_singleplayer, + m_load, + m_save, + m_custommaps, + m_setup, + m_net, + m_options, + m_video, + m_keys, + m_help, + m_quit, + m_restart, + m_credits, + m_exit, + m_serialconfig, + m_modemconfig, + m_lanconfig, + m_gameoptions, + m_search, + m_slist, +} m_state; + +void M_Start_Menu_f (void); +void M_Menu_Main_f (void); + void M_Menu_SinglePlayer_f (void); + void M_Menu_CustomMaps_f (void); + void M_Menu_Options_f (void); + void M_Menu_Keys_f (void); + void M_Menu_Video_f (void); + void M_Menu_Credits_f (void); + void M_Menu_Quit_f (void); +void M_Menu_GameOptions_f (void); + +void M_Main_Draw (void); + void M_SinglePlayer_Draw (void); + void M_Menu_CustomMaps_Draw (void); + void M_Options_Draw (void); + void M_Keys_Draw (void); + void M_Video_Draw (void); + void M_Menu_Credits_Draw (void); + void M_Quit_Draw (void); + +void M_Main_Key (int key); + void M_SinglePlayer_Key (int key); + void M_Menu_CustomMaps_Key (int key); + void M_Options_Key (int key); + void M_Keys_Key (int key); + void M_Video_Key (int key); + void M_Menu_Credits_Key (int key); + void M_Quit_Key (int key); +void M_GameOptions_Key (int key); + +qboolean m_entersound; // play after drawing a frame, so caching + // won't disrupt the sound +qboolean m_recursiveDraw; + +int m_return_state; +qboolean m_return_onerror; +char m_return_reason [32]; + +typedef struct +{ + int occupied; + int map_allow_game_settings; + int map_use_thumbnail; + char* map_name; + char* map_name_pretty; + char* map_desc_1; + char* map_desc_2; + char* map_desc_3; + char* map_desc_4; + char* map_desc_5; + char* map_desc_6; + char* map_desc_7; + char* map_desc_8; + char* map_author; + char* map_thumbnail_path; +} usermap_t; + +usermap_t custom_maps[50]; + +/* +================ +M_DrawCharacter + +Draws one solid graphics character +================ +*/ +void M_DrawCharacter (int cx, int line, int num) +{ + Draw_Character ( cx + ((vid.width - 320)>>1), line, num); +} + +void M_Print (int cx, int cy, char *str) +{ + while (*str) + { + M_DrawCharacter (cx, cy, (*str)+128); + str++; + cx += 8; + } +} + +void M_PrintWhite (int cx, int cy, char *str) +{ + while (*str) + { + M_DrawCharacter (cx, cy, *str); + str++; + cx += 8; + } +} + +void M_DrawTransPic (int x, int y, qpic_t *pic) +{ + Draw_TransPic (x + ((vid.width - 320)>>1), y, pic); +} + +void M_DrawPic (int x, int y, qpic_t *pic) +{ + Draw_Pic (x + ((vid.width - 320)>>1), y, pic); +} + +byte identityTable[256]; +byte translationTable[256]; + +void M_BuildTranslationTable(int top, int bottom) +{ + int j; + byte *dest, *source; + + for (j = 0; j < 256; j++) + identityTable[j] = j; + dest = translationTable; + source = identityTable; + memcpy (dest, 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]; +} + + +void M_DrawTransPicTranslate (int x, int y, qpic_t *pic) +{ + Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y, pic, translationTable); +} + + +void M_DrawTextBox (int x, int y, int width, int lines) +{ + +} + +//============================================================================= + +void M_Load_Menu_Pics () +{ + menu_bk = Draw_CachePic("gfx/menu/menu_background"); + menu_ndu = Draw_CachePic("gfx/menu/nacht_der_untoten"); + //menu_kn = Draw_CachePic("gfx/menu/kino_der_toten"); + menu_wh = Draw_CachePic("gfx/menu/nzp_warehouse"); + menu_wh2 = Draw_CachePic("gfx/menu/nzp_warehouse2"); + //menu_wn = Draw_CachePic("gfx/menu/wahnsinn"); + menu_ch = Draw_CachePic("gfx/menu/christmas_special"); + menu_custom = Draw_CachePic("gfx/menu/custom"); +} + +void M_Start_Menu_f () +{ + //Load_Achivements(); + M_Load_Menu_Pics(); + key_dest = key_menu; + m_state = m_start; + m_entersound = true; + //loadingScreen = 0; +} + +static void M_Start_Menu_Draw () +{ + // Background + menu_bk = Draw_CachePic("gfx/menu/menu_background"); + Draw_StretchPic(0, 0, menu_bk, 400, 240); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, vid.width, vid.height, 0, 0, 0, 102); + + Draw_ColoredStringCentered(vid.height - 64, "Press A to Start", 255, 0, 0, 255, 1); +} + +void M_Start_Key (int key) +{ + switch (key) + { + case K_AUX1: + S_LocalSound ("sounds/menu/enter.wav"); + //Cbuf_AddText("cd playstring tensioned_by_the_damned 1\n"); + Cbuf_AddText("togglemenu\n"); + break; + } +} + + +int m_save_demonum; + +/* +================ +M_ToggleMenu_f +================ +*/ +void M_ToggleMenu_f (void) +{ + m_entersound = true; + + if (key_dest == key_menu || key_dest == key_menu_pause) + { + if (m_state != m_main && m_state != m_paused_menu) + { + M_Menu_Main_f (); + return; + } + key_dest = key_game; + m_state = m_none; + return; + } + if (key_dest == key_console) + { + Con_ToggleConsole_f (); + } + else if (sv.active && (svs.maxclients > 1 || key_dest == key_game)) + { + M_Paused_Menu_f(); + } + else + { + M_Menu_Main_f (); + } +} + + +int M_Paused_Cusor; +#define Max_Paused_Iteams 5 + +void M_Paused_Menu_f () +{ + key_dest = key_menu_pause; + m_state = m_paused_menu; + m_entersound = true; + loadingScreen = 0; + loadscreeninit = false; + M_Paused_Cusor = 0; +} + +static void M_Paused_Menu_Draw () +{ + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, vid.width, vid.height, 0, 0, 0, 102); + + // Header + Draw_ColoredString(10, 10, "PAUSED", 255, 255, 255, 255, 2); + + if ((M_Paused_Cusor == 0)) + Draw_ColoredString(10, 135, "Resume", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 135, "Resume", 255, 255, 255, 255, 1); + + if ((M_Paused_Cusor == 1)) + Draw_ColoredString(10, 145, "Restart", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 145, "Restart", 255, 255, 255, 255, 1); + + if ((M_Paused_Cusor == 2)) + Draw_ColoredString(10, 155, "Settings", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 155, "Settings", 255, 255, 255, 255, 1); + + if (waypoint_mode.value) { + if ((M_Paused_Cusor == 3)) + Draw_ColoredString(10, 165, "Save Waypoints", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 165, "Save Waypoints", 255, 255, 255, 255, 1); + } else { + if ((M_Paused_Cusor == 3)) + Draw_ColoredString(10, 165, "Achievements", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 165, "Achievements", 255, 255, 255, 255, 1); + } + + if ((M_Paused_Cusor == 4)) + Draw_ColoredString(10, 175, "Main Menu", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 175, "Main Menu", 255, 255, 255, 255, 1); +} + +static void M_Paused_Menu_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + case K_AUX2: + S_LocalSound ("sounds/menu/enter.wav"); + Cbuf_AddText("togglemenu\n"); + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (++M_Paused_Cusor >= Max_Paused_Iteams) + M_Paused_Cusor = 0; + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (--M_Paused_Cusor < 0) + M_Paused_Cusor = Max_Paused_Iteams - 1; + break; + + case K_ENTER: + case K_AUX1: + m_entersound = true; + + switch (M_Paused_Cusor) + { + case 0: + key_dest = key_game; + m_state = m_none; + break; + case 1: + M_Menu_Restart_f(); + break; + case 2: + M_Menu_Options_f(); + key_dest = key_menu_pause; + break; + case 3: + if (waypoint_mode.value) { + Cbuf_AddText("impulse 101\n"); + } + /*else + M_Menu_Achievement_f(); + */ // naievil -- fixme: do not have achievements + key_dest = key_menu_pause; + break; + case 4: + M_Menu_Exit_f(); + break; + } + } +} + + +//============================================================================= +/* MAIN MENU */ + +int m_main_cursor; +#define MAIN_ITEMS 4 + + +void M_Menu_Main_f (void) +{ + if (key_dest != key_menu) + { + m_save_demonum = cls.demonum; + cls.demonum = -1; + } + key_dest = key_menu; + m_state = m_main; + m_entersound = true; +} + +void M_Main_Draw (void) +{ + // Background + menu_bk = Draw_CachePic("gfx/menu/menu_background"); + Draw_StretchPic(0, 0, menu_bk, 400, 240); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 400, 240, 0, 0, 0, 102); + + // Version String + Draw_ColoredString((vid.width - getTextWidth(game_build_date, 1)) + 4, 5, game_build_date, 255, 255, 255, 255, 1); + + // Header + Draw_ColoredString(5, 5, "MAIN MENU", 255, 255, 255, 255, 2); + + // Solo + if (m_main_cursor == 0) + Draw_ColoredString(5, 40, "Solo", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 40, "Solo", 255, 255, 255, 255, 1); + + + // Co-Op (Unfinished, so non-selectable) + Draw_ColoredString(5, 50, "Co-Op (Coming Soon!)", 128, 128, 128, 255, 1); + + // Divider + Draw_FillByColor(5, 63, 160, 2, 130, 130, 130, 255); + + if (m_main_cursor == 1) + Draw_ColoredString(5, 70, "Settings", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 70, "Settings", 255, 255, 255, 255, 1); + + Draw_ColoredString(5, 80, "Achievements", 128, 128, 128, 255, 1); + + // Divider + Draw_FillByColor(5, 93, 160, 2, 130, 130, 130, 255); + + if (m_main_cursor == 2) + Draw_ColoredString(5, 100, "Credits", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 100, "Credits", 255, 255, 255, 255, 1); + + // Divider + Draw_FillByColor(5, 113, 160, 2, 130, 130, 130, 255); + + if (m_main_cursor == 3) + Draw_ColoredString(5, 120, "Exit", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 120, "Exit", 255, 255, 255, 255, 1); + + // Descriptions + switch(m_main_cursor) { + case 0: // Solo + Draw_ColoredString(5, 220, "Take on the Hordes by yourself.", 255, 255, 255, 255, 1); + break; + case 1: // Settings + Draw_ColoredString(5, 220, "Adjust your Settings to Optimize your Experience.", 255, 255, 255, 255, 1); + break; + case 2: // Credits + Draw_ColoredString(5, 220, "See who made NZ:P possible.", 255, 255, 255, 255, 1); + break; + case 3: // Exit + Draw_ColoredString(5, 220, "Return to Home Menu.", 255, 255, 255, 255, 1); + break; + } +} + + +void M_Main_Key (int key) +{ + switch (key) + { + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (++m_main_cursor >= MAIN_ITEMS) + m_main_cursor = 0; + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (--m_main_cursor < 0) + m_main_cursor = MAIN_ITEMS - 1; + break; + + case K_ENTER: + case K_AUX1: + m_entersound = true; + + switch (m_main_cursor) + { + case 0: + M_Menu_SinglePlayer_f (); + break; + + case 1: + M_Menu_Options_f (); + break; + + case 2: + M_Menu_Credits_f (); + break; + + case 3: + M_Menu_Quit_f (); + break; + } + } +} + +//============================================================================= +/* CREDITS MENU */ + + +void M_Menu_Credits_f (void) +{ + key_dest = key_menu; + m_state = m_credits; + m_entersound = true; +} + +void M_Credits_Draw (void) +{ + // Background + menu_bk = Draw_CachePic("gfx/menu/menu_background"); + Draw_StretchPic(0, 0, menu_bk, 400, 240); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 400, 240, 0, 0, 0, 102); + + // Header + Draw_ColoredString(5, 5, "CREDITS", 255, 255, 255, 255, 2); + + Draw_ColoredString(5, 30, "Programming:", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 40, "Blubs, Jukki, DR_Mabuse1981, Naievil", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 50, "Cypress, ScatterBox", 255, 255, 255, 255, 1); + + Draw_ColoredString(5, 70, "Models:", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 80, "Blubs, Ju[s]tice, Derped_Crusader", 255, 255, 255, 255, 1); + + Draw_ColoredString(5, 100, "GFX:", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 110, "Blubs, Ju[s]tice, Cypress, Derped_Crusader", 255, 255, 255, 255, 1); + + Draw_ColoredString(5, 130, "Sounds/Music:", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 140, "Blubs, Biodude, Cypress, Marty P.", 255, 255, 255, 255, 1); + + Draw_ColoredString(5, 160, "Special Thanks:", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 170, "- Spike, Eukara: FTEQW", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 180, "- Shpuld: CleanQC4FTE", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 190, "- Crow_Bar, st1x51: dQuake(plus)", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 200, "- fgsfdsfgs: Quakespasm-NX", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 210, "- MasterFeizz: ctrQuake", 255, 255, 255, 255, 1); + Draw_ColoredString(5, 220, "- Rinnegatamante: Initial VITA Port & Updater", 255, 255, 255, 255, 1); + + Draw_ColoredString(5, 230, "Back", 255, 0, 0, 255, 1); +} + + +void M_Credits_Key (int key) +{ + switch (key) + { + case K_ENTER: + case K_AUX1: + case K_AUX2: + M_Menu_Main_f (); + break; + } +} + +//============================================================================= +/* RESTART MENU */ + +qboolean wasInMenus; + + +char *restartMessage [] = +{ + + " Are you sure you want", + " to restart this game? ", //msg:0 + " ", + " A :Yes B : No " +}; + + +void M_Menu_Restart_f (void) +{ + wasInMenus = (key_dest == key_menu_pause); + key_dest = key_menu_pause; + m_state = m_restart; + m_entersound = true; +} + + +void M_Restart_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + case K_AUX2: + case 'n': + case 'N': + m_state = m_paused_menu; + m_entersound = true; + break; + + case 'Y': + case 'y': + case K_ENTER: + case K_AUX1: + key_dest = key_game; + m_state = m_none; + // Cbuf_AddText ("restart\n"); // nai -- old, now do soft reset + PR_ExecuteProgram (pr_global_struct->Soft_Restart); + break; + + default: + break; + } + +} + + +void M_Restart_Draw (void) +{ + m_state = m_paused_menu; + m_recursiveDraw = true; + M_Draw (); + m_state = m_restart; + + M_DrawTextBox (56, 76, 24, 4); + M_Print (64, 84, restartMessage[0]); + M_Print (64, 92, restartMessage[1]); + M_Print (64, 100, restartMessage[2]); + M_Print (64, 108, restartMessage[3]); +} + + + + +//============================================================================= +/* EXIT MENU */ + + +char *exitMessage [] = +{ + + " Are you sure you want ", + "to quit to the Main Menu?", //msg:0 + " ", + " A :Yes B : No " +}; + + +void M_Menu_Exit_f (void) +{ + wasInMenus = (key_dest == key_menu_pause); + key_dest = key_menu_pause; + m_state = m_exit; + m_entersound = true; +} + + +void M_Exit_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + case K_AUX2: + case 'n': + case 'N': + m_state = m_paused_menu; + m_entersound = true; + break; + + case 'Y': + case 'y': + case K_ENTER: + case K_AUX1: + Cbuf_AddText("disconnect\n"); + CL_ClearState (); + M_Menu_Main_f(); + break; + + default: + break; + } + +} + + +void M_Exit_Draw (void) +{ + m_state = m_paused_menu; + m_recursiveDraw = true; + M_Draw (); + m_state = m_exit; + + M_DrawTextBox (56, 76, 24, 4); + M_Print (64, 84, exitMessage[0]); + M_Print (64, 92, exitMessage[1]); + M_Print (64, 100, exitMessage[2]); + M_Print (64, 108, exitMessage[3]); +} + + +//============================================================================= +/* SINGLE PLAYER MENU */ + +int m_singleplayer_cursor; +#define SINGLEPLAYER_ITEMS 6 + + +void M_Menu_SinglePlayer_f (void) +{ + key_dest = key_menu; + m_state = m_singleplayer; + m_entersound = true; +} + + + +void M_SinglePlayer_Draw (void) +{ + // Background + menu_bk = Draw_CachePic("gfx/menu/menu_background"); + Draw_StretchPic(0, 0, menu_bk, 400, 240); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 400, 240, 0, 0, 0, 102); + + // Header + Draw_ColoredString(5, 5, "SOLO", 255, 255, 255, 255, 2); + + // Nacht der Untoten + if (m_singleplayer_cursor == 0) + Draw_ColoredString(5, 40, "Nacht der Untoten", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 40, "Nacht der Untoten", 255, 255, 255, 255, 1); + + // Divider + Draw_FillByColor(5, 53, 160, 2, 130, 130, 130, 255); + + // Warehouse + if (m_singleplayer_cursor == 1) + Draw_ColoredString(5, 60, "Warehouse", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 60, "Warehouse", 255, 255, 255, 255, 1); + + // Warehouse (Classic) + if (m_singleplayer_cursor == 2) + Draw_ColoredString(5, 70, "Warehouse (Classic)", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 70, "Warehouse (Classic)", 255, 255, 255, 255, 1); + + // Christmas Special + if (m_singleplayer_cursor == 3) + Draw_ColoredString(5, 80, "Christmas Special", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 80, "Christmas Special", 255, 255, 255, 255, 1); + + // Divider + Draw_FillByColor(5, 93, 160, 2, 130, 130, 130, 255); + + // Custom Maps + if (m_singleplayer_cursor == 4) + Draw_ColoredString(5, 100, "Custom Maps", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 100, "Custom Maps", 255, 255, 255, 255, 1); + + // Back + if (m_singleplayer_cursor == 5) + Draw_ColoredString(5, 230, "Back", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 230, "Back", 255, 255, 255, 255, 1); + + // Map description & pic + switch(m_singleplayer_cursor) { + case 0: + menu_ndu = Draw_CachePic("gfx/menu/nacht_der_untoten"); + Draw_StretchPic(185, 40, menu_ndu, 175, 100); + Draw_ColoredString(180, 148, "Desolate bunker located on a Ge-", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 158, "rman airfield, stranded after a", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 168, "brutal plane crash surrounded by", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 178, "hordes of undead. Exploit myste-", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 188, "rious forces at play and hold o-", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 198, "ut against relentless waves. Der", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 208, "Anstieg ist jetzt. Will you fall", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 218, "to the overwhelming onslaught?", 255, 255, 255, 255, 1); + break; + case 1: + menu_wh2 = Draw_CachePic("gfx/menu/nzp_warehouse2"); + Draw_StretchPic(185, 40, menu_wh2, 175, 100); + Draw_ColoredString(180, 148, "Four nameless marines find them-", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 158, "selves at a forsaken warehouse,", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 168, "or is it something more? Fight", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 178, "your way to uncovering its sec-", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 188, "rets, though you may not like", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 198, "what you find..", 255, 255, 255, 255, 1); + break; + case 2: + menu_wh = Draw_CachePic("gfx/menu/nzp_warehouse"); + Draw_StretchPic(185, 40, menu_wh, 175, 100); + Draw_ColoredString(180, 148, "Old Warehouse full of Zombies!", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 158, "Fight your way to the Power", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 168, "Switch through the Hordes!", 255, 255, 255, 255, 1); + break; + case 3: + menu_ch = Draw_CachePic("gfx/menu/christmas_special"); + Draw_StretchPic(185, 40, menu_ch, 175, 100); + Draw_ColoredString(180, 148, "No Santa this year. Though we're", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 158, "sure you will get presents from", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 168, "the undead! Will you accept them?", 255, 255, 255, 255, 1); + break; + case 4: + menu_custom = Draw_CachePic("gfx/menu/custom"); + Draw_StretchPic(185, 40, menu_custom, 175, 100); + Draw_ColoredString(180, 148, "Custom Maps made by Community", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 158, "Members on GitHub and on the", 255, 255, 255, 255, 1); + Draw_ColoredString(180, 168, "NZ:P Forum!", 255, 255, 255, 255, 1); + break; + } +} + + +void M_SinglePlayer_Key (int key) +{ + switch (key) + { + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (++m_singleplayer_cursor >= SINGLEPLAYER_ITEMS) + m_singleplayer_cursor = 0; + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (--m_singleplayer_cursor < 0) + m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1; + break; + + case K_ENTER: + case K_AUX1: + m_entersound = true; + + switch (m_singleplayer_cursor) + { + case 0: + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText ("map ndu\n"); + loadingScreen = 1; + loadname2 = "ndu"; + loadnamespec = "Nacht der Untoten"; + break; + + case 1: + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText ("map nzp_warehouse2\n"); + loadingScreen = 1; + loadname2 = "nzp_warehouse2"; + loadnamespec = "Warehouse"; + break; + + case 2: + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText ("map nzp_warehouse\n"); + loadingScreen = 1; + loadname2 = "nzp_warehouse"; + loadnamespec = "Warehouse (Classic)"; + break; + + case 3: + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText ("map christmas_special\n"); + loadingScreen = 1; + loadname2 = "christmas_special"; + loadnamespec = "Christmas Special"; + break; + + case 4: + M_Menu_CustomMaps_f (); + break; + case 5: + M_Menu_Main_f (); + break; + } + break; + + // b button + case K_AUX2: + M_Menu_Main_f(); + break; + } +} + + +//============================================================================= +/* SINGLE PLAYER MENU */ + +int m_map_cursor; +int MAP_ITEMS; +int user_maps_num = 0; +int current_custom_map_page; +int custom_map_pages; +int multiplier; +char user_levels[256][MAX_QPATH]; + +// UGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +// fuck windows +char* remove_windows_newlines(const char* line) +{ + const char* p = line; + size_t len = strlen(line); + char* result = (char*)malloc(len + 1); + + if (result == NULL) { + return NULL; + } + + char* q = result; + + for (size_t i = 0; i < len; i++) { + if (p[i] == '\r') { + continue; + } + *q++ = p[i]; + } + + *q = '\0'; + + return result; +} + +void Map_Finder(void) +{ + struct dirent *dp; + DIR *dir = opendir(va("%s/maps", com_gamedir)); // Open the directory - dir contains a pointer to manage the dir + + if(dir < 0) + { + Sys_Error ("Map_Finder"); + return; + } + + for (int i = 0; i < 50; i++) { + custom_maps[i].occupied = false; + } + + while(dp=readdir(dir)) + { + + if(dp->d_name[0] == '.') + { + continue; + } + + if(!strcmp(COM_FileExtension(dp->d_name),"bsp")|| !strcmp(COM_FileExtension(dp->d_name),"BSP")) + { + char ntype[32]; + + COM_StripExtension(dp->d_name, ntype); + custom_maps[user_maps_num].occupied = true; + custom_maps[user_maps_num].map_name = malloc(sizeof(char)*32); + sprintf(custom_maps[user_maps_num].map_name, "%s", ntype); + + char* setting_path; + + setting_path = malloc(sizeof(char)*64); + custom_maps[user_maps_num].map_thumbnail_path = malloc(sizeof(char)*64); + strcpy(setting_path, va("%s/maps/", com_gamedir)); + strcpy(custom_maps[user_maps_num].map_thumbnail_path, "gfx/menu/custom/"); + strcat(setting_path, custom_maps[user_maps_num].map_name); + strcat(custom_maps[user_maps_num].map_thumbnail_path, custom_maps[user_maps_num].map_name); + strcat(setting_path, ".txt"); + + FILE *setting_file; + setting_file = fopen(setting_path, "rb"); + if (setting_file != NULL) { + + fseek(setting_file, 0L, SEEK_END); + size_t sz = ftell(setting_file); + fseek(setting_file, 0L, SEEK_SET); + + int state; + state = 0; + int value; + + custom_maps[user_maps_num].map_name_pretty = malloc(sizeof(char)*32); + custom_maps[user_maps_num].map_desc_1 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_2 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_3 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_4 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_5 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_6 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_7 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_8 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_author = malloc(sizeof(char)*40); + + char* buffer = (char*)calloc(sz+1, sizeof(char)); + fread(buffer, sz, 1, setting_file); + + strtok(buffer, "\n"); + while(buffer != NULL) { + switch(state) { + case 0: strcpy(custom_maps[user_maps_num].map_name_pretty, remove_windows_newlines(buffer)); break; + case 1: strcpy(custom_maps[user_maps_num].map_desc_1, remove_windows_newlines(buffer)); break; + case 2: strcpy(custom_maps[user_maps_num].map_desc_2, remove_windows_newlines(buffer)); break; + case 3: strcpy(custom_maps[user_maps_num].map_desc_3, remove_windows_newlines(buffer)); break; + case 4: strcpy(custom_maps[user_maps_num].map_desc_4, remove_windows_newlines(buffer)); break; + case 5: strcpy(custom_maps[user_maps_num].map_desc_5, remove_windows_newlines(buffer)); break; + case 6: strcpy(custom_maps[user_maps_num].map_desc_6, remove_windows_newlines(buffer)); break; + case 7: strcpy(custom_maps[user_maps_num].map_desc_7, remove_windows_newlines(buffer)); break; + case 8: strcpy(custom_maps[user_maps_num].map_desc_8, remove_windows_newlines(buffer)); break; + case 9: strcpy(custom_maps[user_maps_num].map_author, remove_windows_newlines(buffer)); break; + case 10: value = 0; sscanf(remove_windows_newlines(buffer), "%d", &value); custom_maps[user_maps_num].map_use_thumbnail = value; break; + case 11: value = 0; sscanf(remove_windows_newlines(buffer), "%d", &value); custom_maps[user_maps_num].map_allow_game_settings = value; break; + default: break; + } + state++; + buffer = strtok(NULL, "\n"); + } + free(buffer); + buffer = 0; + fclose(setting_file); + } + user_maps_num++; + } + } + closedir(dir); // close the handle (pointer) + custom_map_pages = (int)ceil((double)(user_maps_num)/15); +} + +void M_Menu_CustomMaps_f (void) +{ + key_dest = key_menu; + m_state = m_custommaps; + m_entersound = true; + MAP_ITEMS = 13; + current_custom_map_page = 1; +} + + +void M_Menu_CustomMaps_Draw (void) +{ + // Background + menu_bk = Draw_CachePic("gfx/menu/menu_background"); + Draw_StretchPic(0, 0, menu_bk, 400, 240); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 400, 240, 0, 0, 0, 102); + + // Header + Draw_ColoredString(5, 5, "CUSTOM MAPS", 255, 255, 255, 255, 2); + + int line_increment; + + line_increment = 0; + + if (current_custom_map_page > 1) + multiplier = (current_custom_map_page - 1) * 15; + else + multiplier = 0; + + for (int i = 0; i < 15; i++) { + if (custom_maps[i + multiplier].occupied == false) + continue; + + if (m_map_cursor == i) { + + if (custom_maps[i + multiplier].map_use_thumbnail == 1) { + menu_cuthum = Draw_CachePic(custom_maps[i + multiplier].map_thumbnail_path); + if (menu_cuthum != NULL) { + Draw_StretchPic(185, 40, menu_cuthum, 175, 100); + } + } + + if (custom_maps[i + multiplier].map_name_pretty != 0) + Draw_ColoredString(5, 40 + (10 * i), custom_maps[i + multiplier].map_name_pretty, 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 40 + (10 * i), custom_maps[i + multiplier].map_name, 255, 0, 0, 255, 1); + + if (custom_maps[i + multiplier].map_desc_1 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_1, " ") != 0) { + Draw_ColoredString(180, 148, custom_maps[i + multiplier].map_desc_1, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_2 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_2, " ") != 0) { + line_increment++; + Draw_ColoredString(180, 158, custom_maps[i + multiplier].map_desc_2, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_3 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_3, " ") != 0) { + line_increment++; + Draw_ColoredString(180, 168, custom_maps[i + multiplier].map_desc_3, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_4 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_4, " ") != 0) { + line_increment++; + Draw_ColoredString(180, 178, custom_maps[i + multiplier].map_desc_4, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_5 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_5, " ") != 0) { + line_increment++; + Draw_ColoredString(180, 188, custom_maps[i + multiplier].map_desc_5, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_6 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_6, " ") != 0) { + line_increment++; + Draw_ColoredString(180, 198, custom_maps[i + multiplier].map_desc_6, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_7 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_7, " ") != 0) { + line_increment++; + Draw_ColoredString(180, 208, custom_maps[i + multiplier].map_desc_7, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_8 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_8, " ") != 0) { + line_increment++; + Draw_ColoredString(180, 218, custom_maps[i + multiplier].map_desc_8, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_author != 0) { + if (strcmp(custom_maps[i + multiplier].map_author, " ") != 0) { + int y = 158 + (10 * line_increment); + Draw_ColoredString(180, y, custom_maps[i + multiplier].map_author, 255, 255, 0, 255, 1); + } + } + } else { + if (custom_maps[i + multiplier].map_name_pretty != 0) + Draw_ColoredString(5, 40 + (10 * i), custom_maps[i + multiplier].map_name_pretty, 255, 255, 255, 255, 1); + else + Draw_ColoredString(5, 40 + (10 * i), custom_maps[i + multiplier].map_name, 255, 255, 255, 255, 1); + } + } + + if (current_custom_map_page != custom_map_pages) { + if (m_map_cursor == 15) + Draw_ColoredString(5, 210, "Next Page", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 210, "Next Page", 255, 255, 255, 255, 1); + } else { + Draw_ColoredString(5, 210, "Next Page", 128, 128, 128, 255, 1); + } + + if (current_custom_map_page != 1) { + if (m_map_cursor == 16) + Draw_ColoredString(5, 220, "Previous Page", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 220, "Previous Page", 255, 255, 255, 255, 1); + } else { + Draw_ColoredString(5, 220, "Previous Page", 128, 128, 128, 255, 1); + } + + + + if (m_map_cursor == 17) + Draw_ColoredString(5, 230, "Back", 255, 0, 0, 255, 1); + else + Draw_ColoredString(5, 230, "Back", 255, 255, 255, 255, 1); +} + + +void M_Menu_CustomMaps_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + case K_AUX2: + M_Menu_SinglePlayer_f (); + break; + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + + m_map_cursor++; + + if (m_map_cursor < 14 && custom_maps[m_map_cursor + multiplier].occupied == false) { + m_map_cursor = 15; + } + + if (m_map_cursor == 15 && current_custom_map_page == custom_map_pages) + m_map_cursor = 16; + + if (m_map_cursor == 16 && current_custom_map_page == 1) + m_map_cursor = 17; + + if (m_map_cursor >= 18) + m_map_cursor = 0; + break; + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + + m_map_cursor--; + + if (m_map_cursor < 0) + m_map_cursor = 17; + + if (m_map_cursor == 16 && current_custom_map_page == 1) + m_map_cursor = 15; + + if (m_map_cursor == 15 && current_custom_map_page == custom_map_pages) + m_map_cursor = 14; + + if (m_map_cursor <= 14 && custom_maps[m_map_cursor + multiplier].occupied == false) { + for (int i = 14; i > -1; i--) { + if (custom_maps[i + multiplier].occupied == true) { + m_map_cursor = i; + break; + } + } + } + break; + case K_ENTER: + case K_AUX1: + m_entersound = true; + if (m_map_cursor == 17) { + M_Menu_SinglePlayer_f (); + } else if (m_map_cursor == 16) { + current_custom_map_page--; + m_map_cursor = 0; + } else if (m_map_cursor == 15) { + current_custom_map_page++; + m_map_cursor = 0; + } else + { + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText (va("map %s\n", custom_maps[m_map_cursor + multiplier].map_name)); + loadingScreen = 1; + loadname2 = custom_maps[m_map_cursor + multiplier].map_name; + if (custom_maps[m_map_cursor + multiplier].map_name_pretty != 0) + loadnamespec = custom_maps[m_map_cursor + multiplier].map_name_pretty; + else + loadnamespec = custom_maps[m_map_cursor + multiplier].map_name; + } + break; + } +} + +//============================================================================= +/* OPTIONS MENU */ + +#define OPTIONS_ITEMS 13 +#define SLIDER_RANGE 10 + +int options_cursor; + +void M_Menu_Options_f (void) +{ + m_state = m_options; + m_entersound = true; +} + + +void M_AdjustSliders (int dir) +{ + S_LocalSound ("misc/menu3.wav"); + + switch (options_cursor) + { + case 3: // screen size + scr_viewsize.value += dir * 10; + if (scr_viewsize.value < 30) + scr_viewsize.value = 30; + if (scr_viewsize.value > 120) + scr_viewsize.value = 120; + Cvar_SetValue ("viewsize", scr_viewsize.value); + break; + case 4: // gamma + v_gamma.value -= dir * 0.05; + if (v_gamma.value < 0.5) + v_gamma.value = 0.5; + if (v_gamma.value > 1) + v_gamma.value = 1; + Cvar_SetValue ("gamma", v_gamma.value); + break; + case 5: // mouse speed + sensitivity.value += dir * 0.5; + if (sensitivity.value < 1) + sensitivity.value = 1; + if (sensitivity.value > 11) + sensitivity.value = 11; + Cvar_SetValue ("sensitivity", sensitivity.value); + break; + case 6: // music volume + bgmvolume.value += dir * 0.1; + if (bgmvolume.value < 0) + bgmvolume.value = 0; + if (bgmvolume.value > 1) + bgmvolume.value = 1; + Cvar_SetValue ("bgmvolume", bgmvolume.value); + break; + case 7: // sfx volume + volume.value += dir * 0.1; + if (volume.value < 0) + volume.value = 0; + if (volume.value > 1) + volume.value = 1; + Cvar_SetValue ("volume", volume.value); + break; + + case 8: // allways run + if (cl_forwardspeed > 200) + { + cl_forwardspeed = 200; + Cvar_SetValue ("cl_backspeed", 200); + } + else + { + cl_forwardspeed = 400; + Cvar_SetValue ("cl_backspeed", 400); + } + break; + + case 9: // invert mouse + Cvar_SetValue ("m_pitch", -m_pitch.value); + break; + + case 10: // lookspring + Cvar_SetValue ("lookspring", !lookspring.value); + break; + + case 11: // lookstrafe + Cvar_SetValue ("lookstrafe", !lookstrafe.value); + break; + + case 12: // anub swap + Cvar_SetValue ("in_anub_mode", !in_anub_mode.value); + break; + } +} + + +void M_DrawSlider (int x, int y, float range) +{ + int i; + + if (range < 0) + range = 0; + if (range > 1) + range = 1; + M_DrawCharacter (x-8, y, 128); + for (i=0 ; i 200); + + M_Print (16, 104, " Invert Mouse"); + M_DrawCheckbox (220, 104, m_pitch.value < 0); + + M_Print (16, 112, " Lookspring"); + M_DrawCheckbox (220, 112, lookspring.value); + + M_Print (16, 120, " Lookstrafe"); + M_DrawCheckbox (220, 120, lookstrafe.value); + + M_Print (16, 128, "Swap C-Nub and C-Stick"); + M_DrawCheckbox (220, 128, in_anub_mode.value); + + if (vid_menudrawfn) + M_Print (16, 136, " Video Options"); + +// cursor + M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1)); +} + +extern qboolean console_enabled; +void M_Options_Key (int k) +{ + switch (k) + { + case K_ENTER: + case K_AUX1: + m_entersound = true; + switch (options_cursor) + { + case 0: + M_Menu_Keys_f (); + break; + case 1: + console_enabled = true; + m_state = m_none; + Con_Printf("\nPress SELECT to open keyboard.\n\n"); + Con_ToggleConsole_f (); + break; + case 2: + Cbuf_AddText ("exec default.cfg\n"); + break; + case 13: + M_Menu_Video_f (); + break; + default: + M_AdjustSliders (1); + break; + } + return; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + options_cursor--; + if (options_cursor < 0) + options_cursor = OPTIONS_ITEMS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + options_cursor++; + if (options_cursor >= OPTIONS_ITEMS) + options_cursor = 0; + break; + + case K_LEFTARROW: + M_AdjustSliders (-1); + break; + + case K_RIGHTARROW: + M_AdjustSliders (1); + break; + + case K_ESCAPE: + case K_AUX2: + if (key_dest == key_menu_pause) + M_Paused_Menu_f(); + else + M_Menu_Main_f (); + break; + } + + if (options_cursor == 13 && vid_menudrawfn == NULL) + { + if (k == K_UPARROW) + options_cursor = 12; + else + options_cursor = 0; + } +} + +//============================================================================= +/* KEYS MENU */ + +char *bindnames[][2] = +{ +{"+attack", "attack"}, +{"impulse 10", "change weapon"}, +{"+jump", "jump / swim up"}, +{"+forward", "walk forward"}, +{"+back", "backpedal"}, +{"+left", "turn left"}, +{"+right", "turn right"}, +{"+speed", "run"}, +{"+moveleft", "step left"}, +{"+moveright", "step right"}, +{"+strafe", "sidestep"}, +{"+lookup", "look up"}, +{"+lookdown", "look down"}, +{"centerview", "center view"}, +{"+mlook", "mouse look"}, +{"+klook", "keyboard look"}, +{"+moveup", "swim up"}, +{"+movedown", "swim down"} +}; + +#define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0])) + +int keys_cursor; +int bind_grab; + +void M_Menu_Keys_f (void) +{ + m_state = m_keys; + m_entersound = true; +} + + +void M_FindKeysForCommand (char *command, int *twokeys) +{ + int count; + int j; + int l; + char *b; + + twokeys[0] = twokeys[1] = -1; + l = strlen(command); + count = 0; + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, command, l) ) + { + twokeys[count] = j; + count++; + if (count == 2) + break; + } + } +} + +void M_UnbindCommand (char *command) +{ + int j; + int l; + char *b; + + l = strlen(command); + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, command, l) ) + Key_SetBinding (j, ""); + } +} + + +void M_Keys_Draw (void) +{ + int i; + int keys[2]; + char *name; + int x, y; + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + + if (bind_grab) + M_Print (12, 32, "Press a key or button for this action"); + else + M_Print (18, 32, "Enter to change, backspace to clear"); + +// search for known bindings + for (i=0 ; i= NUMCOMMANDS) + keys_cursor = 0; + break; + + case K_ENTER: // go into bind mode + case K_AUX1: + M_FindKeysForCommand (bindnames[keys_cursor][0], keys); + S_LocalSound ("misc/menu2.wav"); + if (keys[1] != -1) + M_UnbindCommand (bindnames[keys_cursor][0]); + bind_grab = true; + break; + + case K_BACKSPACE: // delete bindings + case K_DEL: // delete bindings + S_LocalSound ("misc/menu2.wav"); + M_UnbindCommand (bindnames[keys_cursor][0]); + break; + } +} + +//============================================================================= +/* VIDEO MENU */ + +void M_Menu_Video_f (void) +{ + key_dest = key_menu; + m_state = m_video; + m_entersound = true; +} + + +void M_Video_Draw (void) +{ + (*vid_menudrawfn) (); +} + + +void M_Video_Key (int key) +{ + (*vid_menukeyfn) (key); +} + +//============================================================================= +/* QUIT MENU */ + +int msgNumber; +int m_quit_prevstate; +qboolean wasInMenus; + +#ifndef _WIN32 +char *quitMessage [] = +{ +/* .........1.........2.... */ + " Hope you enjoyed ", + " this tech demo you ", + " pirate (press A) ", + " ", +}; +#endif + +void M_Menu_Quit_f (void) +{ + if (m_state == m_quit) + return; + wasInMenus = (key_dest == key_menu); + key_dest = key_menu; + m_quit_prevstate = m_state; + m_state = m_quit; + m_entersound = true; + msgNumber = 0; +} + +extern bool game_running; +void M_Quit_Key (int key) +{ + switch (key) + { + + case K_AUX2: + M_Menu_Main_f(); + break; + + case K_AUX1: + game_running = false; + break; + } +} + + +void M_Quit_Draw (void) +{ + if (wasInMenus) + { + m_state = m_quit_prevstate; + m_recursiveDraw = true; + M_Draw (); + m_state = m_quit; + } + + M_DrawTextBox (56, 76, 24, 4); + M_Print (64, 84, quitMessage[0]); + M_Print (64, 92, quitMessage[1]); + M_Print (64, 100, quitMessage[2]); + M_Print (64, 108, quitMessage[3]); +} + + +//============================================================================= +/* GAME OPTIONS MENU */ + +typedef struct +{ + char *name; + char *description; +} level_t; + +level_t levels[] = +{ + {"start", "Entrance"}, // 0 + + {"e1m1", "Slipgate Complex"}, // 1 + {"e1m2", "Castle of the Damned"}, + {"e1m3", "The Necropolis"}, + {"e1m4", "The Grisly Grotto"}, + {"e1m5", "Gloom Keep"}, + {"e1m6", "The Door To Chthon"}, + {"e1m7", "The House of Chthon"}, + {"e1m8", "Ziggurat Vertigo"}, + + {"e2m1", "The Installation"}, // 9 + {"e2m2", "Ogre Citadel"}, + {"e2m3", "Crypt of Decay"}, + {"e2m4", "The Ebon Fortress"}, + {"e2m5", "The Wizard's Manse"}, + {"e2m6", "The Dismal Oubliette"}, + {"e2m7", "Underearth"}, + + {"e3m1", "Termination Central"}, // 16 + {"e3m2", "The Vaults of Zin"}, + {"e3m3", "The Tomb of Terror"}, + {"e3m4", "Satan's Dark Delight"}, + {"e3m5", "Wind Tunnels"}, + {"e3m6", "Chambers of Torment"}, + {"e3m7", "The Haunted Halls"}, + + {"e4m1", "The Sewage System"}, // 23 + {"e4m2", "The Tower of Despair"}, + {"e4m3", "The Elder God Shrine"}, + {"e4m4", "The Palace of Hate"}, + {"e4m5", "Hell's Atrium"}, + {"e4m6", "The Pain Maze"}, + {"e4m7", "Azure Agony"}, + {"e4m8", "The Nameless City"}, + + {"end", "Shub-Niggurath's Pit"}, // 31 + + {"dm1", "Place of Two Deaths"}, // 32 + {"dm2", "Claustrophobopolis"}, + {"dm3", "The Abandoned Base"}, + {"dm4", "The Bad Place"}, + {"dm5", "The Cistern"}, + {"dm6", "The Dark Zone"} +}; + +//MED 01/06/97 added hipnotic levels +level_t hipnoticlevels[] = +{ + {"start", "Command HQ"}, // 0 + + {"hip1m1", "The Pumping Station"}, // 1 + {"hip1m2", "Storage Facility"}, + {"hip1m3", "The Lost Mine"}, + {"hip1m4", "Research Facility"}, + {"hip1m5", "Military Complex"}, + + {"hip2m1", "Ancient Realms"}, // 6 + {"hip2m2", "The Black Cathedral"}, + {"hip2m3", "The Catacombs"}, + {"hip2m4", "The Crypt"}, + {"hip2m5", "Mortum's Keep"}, + {"hip2m6", "The Gremlin's Domain"}, + + {"hip3m1", "Tur Torment"}, // 12 + {"hip3m2", "Pandemonium"}, + {"hip3m3", "Limbo"}, + {"hip3m4", "The Gauntlet"}, + + {"hipend", "Armagon's Lair"}, // 16 + + {"hipdm1", "The Edge of Oblivion"} // 17 +}; + +//PGM 01/07/97 added rogue levels +//PGM 03/02/97 added dmatch level +level_t roguelevels[] = +{ + {"start", "Split Decision"}, + {"r1m1", "Deviant's Domain"}, + {"r1m2", "Dread Portal"}, + {"r1m3", "Judgement Call"}, + {"r1m4", "Cave of Death"}, + {"r1m5", "Towers of Wrath"}, + {"r1m6", "Temple of Pain"}, + {"r1m7", "Tomb of the Overlord"}, + {"r2m1", "Tempus Fugit"}, + {"r2m2", "Elemental Fury I"}, + {"r2m3", "Elemental Fury II"}, + {"r2m4", "Curse of Osiris"}, + {"r2m5", "Wizard's Keep"}, + {"r2m6", "Blood Sacrifice"}, + {"r2m7", "Last Bastion"}, + {"r2m8", "Source of Evil"}, + {"ctf1", "Division of Change"} +}; + +typedef struct +{ + char *description; + int firstLevel; + int levels; +} episode_t; + +episode_t episodes[] = +{ + {"Welcome to Quake", 0, 1}, + {"Doomed Dimension", 1, 8}, + {"Realm of Black Magic", 9, 7}, + {"Netherworld", 16, 7}, + {"The Elder World", 23, 8}, + {"Final Level", 31, 1}, + {"Deathmatch Arena", 32, 6} +}; + +//MED 01/06/97 added hipnotic episodes +episode_t hipnoticepisodes[] = +{ + {"Scourge of Armagon", 0, 1}, + {"Fortress of the Dead", 1, 5}, + {"Dominion of Darkness", 6, 6}, + {"The Rift", 12, 4}, + {"Final Level", 16, 1}, + {"Deathmatch Arena", 17, 1} +}; + +//PGM 01/07/97 added rogue episodes +//PGM 03/02/97 added dmatch episode +episode_t rogueepisodes[] = +{ + {"Introduction", 0, 1}, + {"Hell's Fortress", 1, 7}, + {"Corridors of Time", 8, 8}, + {"Deathmatch Arena", 16, 1} +}; + +int startepisode; +int startlevel; +int maxplayers; +qboolean m_serverInfoMessage = false; +double m_serverInfoMessageTime; + +void M_Menu_GameOptions_f (void) +{ + key_dest = key_menu; + m_state = m_gameoptions; + m_entersound = true; + if (maxplayers == 0) + maxplayers = svs.maxclients; + if (maxplayers < 2) + maxplayers = svs.maxclientslimit; +} + + +int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120}; +#define NUM_GAMEOPTIONS 9 +int gameoptions_cursor; + +void M_GameOptions_Draw (void) +{ + int x; + + M_DrawTextBox (152, 32, 10, 1); + M_Print (160, 40, "begin game"); + + M_Print (0, 56, " Max players"); + M_Print (160, 56, va("%i", maxplayers) ); + + M_Print (0, 64, " Game Type"); + if (coop.value) + M_Print (160, 64, "Cooperative"); + else + M_Print (160, 64, "Deathmatch"); + + M_Print (0, 72, " Teamplay"); + { + char *msg; + + switch((int)teamplay.value) + { + case 1: msg = "No Friendly Fire"; break; + case 2: msg = "Friendly Fire"; break; + default: msg = "Off"; break; + } + M_Print (160, 72, msg); + } + + M_Print (0, 80, " Skill"); + if (skill.value == 0) + M_Print (160, 80, "Easy difficulty"); + else if (skill.value == 1) + M_Print (160, 80, "Normal difficulty"); + else if (skill.value == 2) + M_Print (160, 80, "Hard difficulty"); + else + M_Print (160, 80, "Nightmare difficulty"); + + M_Print (0, 88, " Frag Limit"); + if (fraglimit.value == 0) + M_Print (160, 88, "none"); + else + M_Print (160, 88, va("%i frags", (int)fraglimit.value)); + + M_Print (0, 96, " Time Limit"); + if (timelimit.value == 0) + M_Print (160, 96, "none"); + else + M_Print (160, 96, va("%i minutes", (int)timelimit.value)); + + M_Print (0, 112, " Episode"); + M_Print (160, 112, episodes[startepisode].description); + + M_Print (0, 120, " Level"); + M_Print (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description); + M_Print (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name); + +// line cursor + M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1)); + + if (m_serverInfoMessage) + { + if ((realtime - m_serverInfoMessageTime) < 5.0) + { + x = (320-26*8)/2; + M_DrawTextBox (x, 138, 24, 4); + x += 8; + M_Print (x, 146, " More than 4 players "); + M_Print (x, 154, " requires using command "); + M_Print (x, 162, "line parameters; please "); + M_Print (x, 170, " see techinfo.txt. "); + } + else + { + m_serverInfoMessage = false; + } + } +} + + +void M_NetStart_Change (int dir) +{ + int count; + + switch (gameoptions_cursor) + { + case 1: + maxplayers += dir; + if (maxplayers > svs.maxclientslimit) + { + maxplayers = svs.maxclientslimit; + m_serverInfoMessage = true; + m_serverInfoMessageTime = realtime; + } + if (maxplayers < 2) + maxplayers = 2; + break; + + case 2: + Cvar_SetValue ("coop", coop.value ? 0 : 1); + break; + + case 3: + count = 2; + + Cvar_SetValue ("teamplay", teamplay.value + dir); + if (teamplay.value > count) + Cvar_SetValue ("teamplay", 0); + else if (teamplay.value < 0) + Cvar_SetValue ("teamplay", count); + break; + + case 4: + Cvar_SetValue ("skill", skill.value + dir); + if (skill.value > 3) + Cvar_SetValue ("skill", 0); + if (skill.value < 0) + Cvar_SetValue ("skill", 3); + break; + + case 5: + Cvar_SetValue ("fraglimit", fraglimit.value + dir*10); + if (fraglimit.value > 100) + Cvar_SetValue ("fraglimit", 0); + if (fraglimit.value < 0) + Cvar_SetValue ("fraglimit", 100); + break; + + case 6: + Cvar_SetValue ("timelimit", timelimit.value + dir*5); + if (timelimit.value > 60) + Cvar_SetValue ("timelimit", 0); + if (timelimit.value < 0) + Cvar_SetValue ("timelimit", 60); + break; + + case 7: + startepisode += dir; + count = 2; + + if (startepisode < 0) + startepisode = count - 1; + + if (startepisode >= count) + startepisode = 0; + + startlevel = 0; + break; + + case 8: + startlevel += dir; + count = episodes[startepisode].levels; + + if (startlevel < 0) + startlevel = count - 1; + + if (startlevel >= count) + startlevel = 0; + break; + } +} + +void M_GameOptions_Key (int key) +{ + switch (key) + { + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + gameoptions_cursor--; + if (gameoptions_cursor < 0) + gameoptions_cursor = NUM_GAMEOPTIONS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + gameoptions_cursor++; + if (gameoptions_cursor >= NUM_GAMEOPTIONS) + gameoptions_cursor = 0; + break; + + case K_LEFTARROW: + if (gameoptions_cursor == 0) + break; + S_LocalSound ("misc/menu3.wav"); + M_NetStart_Change (-1); + break; + + case K_RIGHTARROW: + if (gameoptions_cursor == 0) + break; + S_LocalSound ("misc/menu3.wav"); + M_NetStart_Change (1); + break; + + case K_ENTER: + case K_AUX1: + S_LocalSound ("misc/menu2.wav"); + if (gameoptions_cursor == 0) + { + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("listen 0\n"); // so host_netport will be re-examined + Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) ); + SCR_BeginLoadingPlaque (); + Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) ); + + return; + } + + M_NetStart_Change (1); + break; + } +} + +//============================================================================= +/* Menu Subsystem */ + + +void M_Init (void) +{ + Cmd_AddCommand ("togglemenu", M_ToggleMenu_f); + + Cmd_AddCommand ("menu_main", M_Menu_Main_f); + Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f); + Cmd_AddCommand ("menu_options", M_Menu_Options_f); + Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); + Cmd_AddCommand ("menu_video", M_Menu_Video_f); + Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); + + // Snag the game version + long length; + FILE* f = fopen(va("%s/version.txt", com_gamedir), "rb"); + + if (f) + { + fseek (f, 0, SEEK_END); + length = ftell (f); + fseek (f, 0, SEEK_SET); + game_build_date = malloc(length); + + if (game_build_date) + fread (game_build_date, 1, length, f); + + fclose (f); + } else { + game_build_date = "version.txt not found."; + } + + Map_Finder(); +} + + +void M_Draw (void) +{ + if (m_state == m_none || key_dest != key_menu && key_dest != key_menu_pause) + return; + + if (!m_recursiveDraw) + { + scr_copyeverything = 1; + + if (scr_con_current) + { + Draw_ConsoleBackground (vid.height); + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); + } + else + Draw_FadeScreen (); + + scr_fullupdate = 0; + } + else + { + m_recursiveDraw = false; + } + + switch (m_state) + { + case m_none: + break; + + case m_start: + M_Start_Menu_Draw(); + break; + + case m_paused_menu: + M_Paused_Menu_Draw(); + break; + + case m_main: + M_Main_Draw (); + break; + + case m_singleplayer: + M_SinglePlayer_Draw (); + break; + + case m_options: + M_Options_Draw (); + break; + + case m_keys: + M_Keys_Draw (); + break; + + case m_video: + M_Video_Draw (); + break; + + case m_quit: + M_Quit_Draw (); + break; + + case m_restart: + M_Restart_Draw (); + break; + + case m_credits: + M_Credits_Draw (); + break; + + case m_exit: + M_Exit_Draw (); + break; + + case m_gameoptions: + M_GameOptions_Draw (); + break; + + case m_custommaps: + M_Menu_CustomMaps_Draw (); + return; + + default: + Con_Printf("Cannot identify menu for case %d\n", m_state); + } + + if (m_entersound) + { + S_LocalSound ("sounds/menu/enter.wav"); + m_entersound = false; + } + + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); +} + + +void M_Keydown (int key) +{ + switch (m_state) + { + case m_none: + return; + + case m_start: + M_Start_Key (key); + break; + + case m_paused_menu: + M_Paused_Menu_Key (key); + break; + + case m_main: + M_Main_Key (key); + return; + + case m_singleplayer: + M_SinglePlayer_Key (key); + return; + + case m_options: + M_Options_Key (key); + return; + + case m_keys: + M_Keys_Key (key); + return; + + case m_restart: + M_Restart_Key (key); + return; + + case m_credits: + M_Credits_Key (key); + return; + + case m_video: + M_Video_Key (key); + return; + + case m_quit: + M_Quit_Key (key); + return; + + case m_exit: + M_Exit_Key (key); + return; + + case m_gameoptions: + M_GameOptions_Key (key); + return; + + case m_custommaps: + M_Menu_CustomMaps_Key (key); + return; + + default: + Con_Printf("Cannot identify menu for case %d\n", m_state); + } +} diff --git a/source/ctr/menu.h b/source/ctr/menu.h new file mode 100644 index 0000000..616de3f --- /dev/null +++ b/source/ctr/menu.h @@ -0,0 +1,38 @@ +/* +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. + +*/ + +// +// 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); + + diff --git a/source/ctr/model.h b/source/ctr/model.h new file mode 100644 index 0000000..ea38349 --- /dev/null +++ b/source/ctr/model.h @@ -0,0 +1,377 @@ +/* +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__ +#define __MODEL__ + +#include "modelgen.h" +#include "spritegn.h" + +/* + +d*_t structures are on-disk representations +m*_t structures are in-memory + +*/ + +/* +============================================================================== + +BRUSH MODELS + +============================================================================== +*/ + + +// +// in memory representation +// +typedef struct +{ + vec3_t position; +} mvertex_t; + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + + +// plane_t structure +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 + +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; + int 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; + +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; + +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 +// + 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); + +mleaf_t *Mod_PointInLeaf (float *p, model_t *model); +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model); + +#endif // __MODEL__ diff --git a/source/ctr/modelgen.h b/source/ctr/modelgen.h new file mode 100644 index 0000000..806877f --- /dev/null +++ b/source/ctr/modelgen.h @@ -0,0 +1,134 @@ +/* +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. + +*/ +// +// 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. * +// ********************************************************* + +#ifdef INCLUDELIBS + +#include +#include +#include +#include + +#include "cmdlib.h" +#include "scriplib.h" +#include "trilib.h" +#include "lbmlib.h" +#include "mathlib.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 { + int type; +} daliasframetype_t; + +typedef struct { + int type; +} daliasskintype_t; + +#define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I') + // little-endian "IDPO" + diff --git a/source/ctr/net.h b/source/ctr/net.h new file mode 100644 index 0000000..e1f10bd --- /dev/null +++ b/source/ctr/net.h @@ -0,0 +1,337 @@ +/* +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.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 16384 +#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 + +typedef struct qsocket_s +{ + struct qsocket_s *next; + double connecttime; + double lastMessageTime; + double lastSendTime; + + qboolean disconnected; + qboolean canSend; + qboolean sendNext; + + int driver; + int 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; +extern int net_numsockets; + +typedef struct +{ + 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 +{ + 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 int net_driverlevel; +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; + int driver; + int ldriver; + struct qsockaddr addr; +} hostcache_t; + +extern int hostCacheCount; +extern hostcache_t hostcache[HOSTCACHESIZE]; + +#if !defined(_WIN32 ) && !defined (__linux__) && !defined (__sun__) +#ifndef htonl +extern unsigned long htonl (unsigned long hostlong); +#endif +#ifndef htons +extern unsigned short htons (unsigned short hostshort); +#endif +#ifndef ntohl +extern unsigned long ntohl (unsigned long netlong); +#endif +#ifndef ntohs +extern unsigned short ntohs (unsigned short netshort); +#endif +#endif + +#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); + +struct qsocket_s *NET_CheckNewConnections (void); +// returns a new connection number if there is one pending, else -1 + +struct qsocket_s *NET_Connect (char *host); +// called by client to connect to a host. Returns -1 if not able to + +qboolean NET_CanSendMessage (qsocket_t *sock); +// Returns true or false if the given qsocket can currently accept a +// message to be transmitted. + +int NET_GetMessage (struct qsocket_s *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_SendMessage (struct qsocket_s *sock, sizebuf_t *data); +int NET_SendUnreliableMessage (struct qsocket_s *sock, sizebuf_t *data); +// 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_SendToAll(sizebuf_t *data, int blocktime); +// This is a reliable *blocking* send to all attached clients. + + +void NET_Close (struct qsocket_s *sock); +// 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_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); diff --git a/source/ctr/net_bsd.c b/source/ctr/net_bsd.c new file mode 100644 index 0000000..d48751d --- /dev/null +++ b/source/ctr/net_bsd.c @@ -0,0 +1,93 @@ +/* +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" +#include "net_dgrm.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 + } + , + { + "Datagram", + false, + Datagram_Init, + Datagram_Listen, + Datagram_SearchForHosts, + Datagram_Connect, + Datagram_CheckNewConnections, + Datagram_GetMessage, + Datagram_SendMessage, + Datagram_SendUnreliableMessage, + Datagram_CanSendMessage, + Datagram_CanSendUnreliableMessage, + Datagram_Close, + Datagram_Shutdown + } +}; + +int net_numdrivers = 2; + +#include "net_udp.h" + +net_landriver_t net_landrivers[MAX_NET_DRIVERS] = +{ + { + "UDP", + false, + 0, + UDP_Init, + UDP_Shutdown, + UDP_Listen, + UDP_OpenSocket, + UDP_CloseSocket, + UDP_Connect, + UDP_CheckNewConnections, + UDP_Read, + UDP_Write, + UDP_Broadcast, + UDP_AddrToString, + UDP_StringToAddr, + UDP_GetSocketAddr, + UDP_GetNameFromAddr, + UDP_GetAddrFromName, + UDP_AddrCompare, + UDP_GetSocketPort, + UDP_SetSocketPort + } +}; + +int net_numlandrivers = 1; diff --git a/source/ctr/net_dgrm.c b/source/ctr/net_dgrm.c new file mode 100644 index 0000000..1f3524d --- /dev/null +++ b/source/ctr/net_dgrm.c @@ -0,0 +1,1373 @@ +/* +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_dgrm.c + +// This is enables a simple IP banning mechanism +#define BAN_TEST + +#ifdef BAN_TEST +#if defined(_WIN32) +#include +#elif defined (NeXT) +#include +#include +#else +#define AF_INET 2 /* internet */ +struct in_addr +{ + union + { + struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { unsigned short s_w1,s_w2; } S_un_w; + unsigned long S_addr; + } S_un; +}; +#define s_addr S_un.S_addr /* can be used for most tcp & ip code */ +struct sockaddr_in +{ + short sin_family; + unsigned short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; +char *inet_ntoa(struct in_addr in); +unsigned long inet_addr(const char *cp); +#endif +#endif // BAN_TEST + +#include "../quakedef.h" +#include "net_dgrm.h" + +// these two macros are to make the code more readable +#define sfunc net_landrivers[sock->landriver] +#define dfunc net_landrivers[net_landriverlevel] + +static int net_landriverlevel; + +/* statistic counters */ +int packetsSent = 0; +int packetsReSent = 0; +int packetsReceived = 0; +int receivedDuplicateCount = 0; +int shortPacketCount = 0; +int droppedDatagrams; + +static int myDriverLevel; + +struct +{ + unsigned int length; + unsigned int sequence; + byte data[MAX_DATAGRAM]; +} packetBuffer; + +extern int m_return_state; +extern int m_state; +extern qboolean m_return_onerror; +extern char m_return_reason[32]; + + +#ifdef DEBUG +char *StrAddr (struct qsockaddr *addr) +{ + static char buf[34]; + byte *p = (byte *)addr; + int n; + + for (n = 0; n < 16; n++) + sprintf (buf + n * 2, "%02x", *p++); + return buf; +} +#endif + + +#ifdef BAN_TEST +unsigned long banAddr = 0x00000000; +unsigned long banMask = 0xffffffff; + +void NET_Ban_f (void) +{ + char addrStr [32]; + char maskStr [32]; + void (*print) (char *fmt, ...); + + if (cmd_source == src_command) + { + if (!sv.active) + { + Cmd_ForwardToServer (); + return; + } + print = Con_Printf; + } + else + { + if (pr_global_struct->deathmatch && !host_client->privileged) + return; + print = SV_ClientPrintf; + } + + switch (Cmd_Argc ()) + { + case 1: + if (((struct in_addr *)&banAddr)->s_addr) + { + Q_strcpy(addrStr, inet_ntoa(*(struct in_addr *)&banAddr)); + Q_strcpy(maskStr, inet_ntoa(*(struct in_addr *)&banMask)); + print("Banning %s [%s]\n", addrStr, maskStr); + } + else + print("Banning not active\n"); + break; + + case 2: + if (Q_strcasecmp(Cmd_Argv(1), "off") == 0) + banAddr = 0x00000000; + else + banAddr = inet_addr(Cmd_Argv(1)); + banMask = 0xffffffff; + break; + + case 3: + banAddr = inet_addr(Cmd_Argv(1)); + banMask = inet_addr(Cmd_Argv(2)); + break; + + default: + print("BAN ip_address [mask]\n"); + break; + } +} +#endif + + +int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data) +{ + unsigned int packetLen; + unsigned int dataLen; + unsigned int eom; + +#ifdef DEBUG + if (data->cursize == 0) + Sys_Error("Datagram_SendMessage: zero length message\n"); + + if (data->cursize > NET_MAXMESSAGE) + Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize); + + if (sock->canSend == false) + Sys_Error("SendMessage: called with canSend == false\n"); +#endif + + Q_memcpy(sock->sendMessage, data->data, data->cursize); + sock->sendMessageLength = data->cursize; + + if (data->cursize <= MAX_DATAGRAM) + { + dataLen = data->cursize; + eom = NETFLAG_EOM; + } + else + { + dataLen = MAX_DATAGRAM; + eom = 0; + } + packetLen = NET_HEADERSIZE + dataLen; + + packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); + packetBuffer.sequence = BigLong(sock->sendSequence++); + Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen); + + sock->canSend = false; + + if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) + return -1; + + sock->lastSendTime = net_time; + packetsSent++; + return 1; +} + + +int SendMessageNext (qsocket_t *sock) +{ + unsigned int packetLen; + unsigned int dataLen; + unsigned int eom; + + if (sock->sendMessageLength <= MAX_DATAGRAM) + { + dataLen = sock->sendMessageLength; + eom = NETFLAG_EOM; + } + else + { + dataLen = MAX_DATAGRAM; + eom = 0; + } + packetLen = NET_HEADERSIZE + dataLen; + + packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); + packetBuffer.sequence = BigLong(sock->sendSequence++); + Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen); + + sock->sendNext = false; + + if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) + return -1; + + sock->lastSendTime = net_time; + packetsSent++; + return 1; +} + + +int ReSendMessage (qsocket_t *sock) +{ + unsigned int packetLen; + unsigned int dataLen; + unsigned int eom; + + if (sock->sendMessageLength <= MAX_DATAGRAM) + { + dataLen = sock->sendMessageLength; + eom = NETFLAG_EOM; + } + else + { + dataLen = MAX_DATAGRAM; + eom = 0; + } + packetLen = NET_HEADERSIZE + dataLen; + + packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); + packetBuffer.sequence = BigLong(sock->sendSequence - 1); + Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen); + + sock->sendNext = false; + + if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) + return -1; + + sock->lastSendTime = net_time; + packetsReSent++; + return 1; +} + + +qboolean Datagram_CanSendMessage (qsocket_t *sock) +{ + if (sock->sendNext) + SendMessageNext (sock); + + return sock->canSend; +} + + +qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock) +{ + return true; +} + + +int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) +{ + int packetLen; + +#ifdef DEBUG + if (data->cursize == 0) + Sys_Error("Datagram_SendUnreliableMessage: zero length message\n"); + + if (data->cursize > MAX_DATAGRAM) + Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize); +#endif + + packetLen = NET_HEADERSIZE + data->cursize; + + packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE); + packetBuffer.sequence = BigLong(sock->unreliableSendSequence++); + Q_memcpy (packetBuffer.data, data->data, data->cursize); + + if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) + return -1; + + packetsSent++; + return 1; +} + + +int Datagram_GetMessage (qsocket_t *sock) +{ + unsigned int length; + unsigned int flags; + int ret = 0; + struct qsockaddr readaddr; + unsigned int sequence; + unsigned int count; + + if (!sock->canSend) + if ((net_time - sock->lastSendTime) > 1.0) + ReSendMessage (sock); + + while(1) + { + length = sfunc.Read (sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); + +// if ((rand() & 255) > 220) +// continue; + + if (length == 0) + break; + + if (length == -1) + { + Con_Printf("Read error\n"); + return -1; + } + + if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0) + { +#ifdef DEBUG + Con_DPrintf("Forged packet received\n"); + Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr)); + Con_DPrintf("Received: %s\n", StrAddr (&readaddr)); +#endif + continue; + } + + if (length < NET_HEADERSIZE) + { + shortPacketCount++; + continue; + } + + length = BigLong(packetBuffer.length); + flags = length & (~NETFLAG_LENGTH_MASK); + length &= NETFLAG_LENGTH_MASK; + + if (flags & NETFLAG_CTL) + continue; + + sequence = BigLong(packetBuffer.sequence); + packetsReceived++; + + if (flags & NETFLAG_UNRELIABLE) + { + if (sequence < sock->unreliableReceiveSequence) + { + Con_DPrintf("Got a stale datagram\n"); + ret = 0; + break; + } + if (sequence != sock->unreliableReceiveSequence) + { + count = sequence - sock->unreliableReceiveSequence; + droppedDatagrams += count; + Con_DPrintf("Dropped %u datagram(s)\n", count); + } + sock->unreliableReceiveSequence = sequence + 1; + + length -= NET_HEADERSIZE; + + SZ_Clear (&net_message); + SZ_Write (&net_message, packetBuffer.data, length); + + ret = 2; + break; + } + + if (flags & NETFLAG_ACK) + { + if (sequence != (sock->sendSequence - 1)) + { + Con_DPrintf("Stale ACK received\n"); + continue; + } + if (sequence == sock->ackSequence) + { + sock->ackSequence++; + if (sock->ackSequence != sock->sendSequence) + Con_DPrintf("ack sequencing error\n"); + } + else + { + Con_DPrintf("Duplicate ACK received\n"); + continue; + } + sock->sendMessageLength -= MAX_DATAGRAM; + if (sock->sendMessageLength > 0) + { + Q_memcpy(sock->sendMessage, sock->sendMessage+MAX_DATAGRAM, sock->sendMessageLength); + sock->sendNext = true; + } + else + { + sock->sendMessageLength = 0; + sock->canSend = true; + } + continue; + } + + if (flags & NETFLAG_DATA) + { + packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK); + packetBuffer.sequence = BigLong(sequence); + sfunc.Write (sock->socket, (byte *)&packetBuffer, NET_HEADERSIZE, &readaddr); + + if (sequence != sock->receiveSequence) + { + receivedDuplicateCount++; + continue; + } + sock->receiveSequence++; + + length -= NET_HEADERSIZE; + + if (flags & NETFLAG_EOM) + { + SZ_Clear(&net_message); + SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength); + SZ_Write(&net_message, packetBuffer.data, length); + sock->receiveMessageLength = 0; + + ret = 1; + break; + } + + Q_memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length); + sock->receiveMessageLength += length; + continue; + } + } + + if (sock->sendNext) + SendMessageNext (sock); + + return ret; +} + + +void PrintStats(qsocket_t *s) +{ + Con_Printf("canSend = %4u \n", s->canSend); + Con_Printf("sendSeq = %4u ", s->sendSequence); + Con_Printf("recvSeq = %4u \n", s->receiveSequence); + Con_Printf("\n"); +} + +void NET_Stats_f (void) +{ + qsocket_t *s; + + if (Cmd_Argc () == 1) + { + Con_Printf("unreliable messages sent = %i\n", unreliableMessagesSent); + Con_Printf("unreliable messages recv = %i\n", unreliableMessagesReceived); + Con_Printf("reliable messages sent = %i\n", messagesSent); + Con_Printf("reliable messages received = %i\n", messagesReceived); + Con_Printf("packetsSent = %i\n", packetsSent); + Con_Printf("packetsReSent = %i\n", packetsReSent); + Con_Printf("packetsReceived = %i\n", packetsReceived); + Con_Printf("receivedDuplicateCount = %i\n", receivedDuplicateCount); + Con_Printf("shortPacketCount = %i\n", shortPacketCount); + Con_Printf("droppedDatagrams = %i\n", droppedDatagrams); + } + else if (strcmp(Cmd_Argv(1), "*") == 0) + { + for (s = net_activeSockets; s; s = s->next) + PrintStats(s); + for (s = net_freeSockets; s; s = s->next) + PrintStats(s); + } + else + { + for (s = net_activeSockets; s; s = s->next) + if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0) + break; + if (s == NULL) + for (s = net_freeSockets; s; s = s->next) + if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0) + break; + if (s == NULL) + return; + PrintStats(s); + } +} + + +static qboolean testInProgress = false; +static int testPollCount; +static int testDriver; +static int testSocket; + +static void Test_Poll(void); +PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll}; + +static void Test_Poll(void) +{ + struct qsockaddr clientaddr; + int control; + int len; + char name[32]; + char address[64]; + int colors; + int frags; + int connectTime; + byte playerNumber; + + net_landriverlevel = testDriver; + + while (1) + { + len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr); + if (len < sizeof(int)) + break; + + net_message.cursize = len; + + MSG_BeginReading (); + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + break; + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + break; + if ((control & NETFLAG_LENGTH_MASK) != len) + break; + + if (MSG_ReadByte() != CCREP_PLAYER_INFO) + Sys_Error("Unexpected repsonse to Player Info request\n"); + + playerNumber = MSG_ReadByte(); + Q_strcpy(name, MSG_ReadString()); + colors = MSG_ReadLong(); + frags = MSG_ReadLong(); + connectTime = MSG_ReadLong(); + Q_strcpy(address, MSG_ReadString()); + + Con_Printf("%s\n frags:%3i colors:%u %u time:%u\n %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address); + } + + testPollCount--; + if (testPollCount) + { + SchedulePollProcedure(&testPollProcedure, 0.1); + } + else + { + dfunc.CloseSocket(testSocket); + testInProgress = false; + } +} + +static void Test_f (void) +{ + char *host; + int n; + int max = MAX_SCOREBOARD; + struct qsockaddr sendaddr; + + if (testInProgress) + return; + + host = Cmd_Argv (1); + + if (host && hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (Q_strcasecmp (host, hostcache[n].name) == 0) + { + if (hostcache[n].driver != myDriverLevel) + continue; + net_landriverlevel = hostcache[n].ldriver; + max = hostcache[n].maxusers; + Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); + break; + } + if (n < hostCacheCount) + goto JustDoIt; + } + + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + { + if (!net_landrivers[net_landriverlevel].initialized) + continue; + + // see if we can resolve the host name + if (dfunc.GetAddrFromName(host, &sendaddr) != -1) + break; + } + if (net_landriverlevel == net_numlandrivers) + return; + +JustDoIt: + testSocket = dfunc.OpenSocket(0); + if (testSocket == -1) + return; + + testInProgress = true; + testPollCount = 20; + testDriver = net_landriverlevel; + + for (n = 0; n < max; n++) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO); + MSG_WriteByte(&net_message, n); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr); + } + SZ_Clear(&net_message); + SchedulePollProcedure(&testPollProcedure, 0.1); +} + + +static qboolean test2InProgress = false; +static int test2Driver; +static int test2Socket; + +static void Test2_Poll(void); +PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll}; + +static void Test2_Poll(void) +{ + struct qsockaddr clientaddr; + int control; + int len; + char name[256]; + char value[256]; + + net_landriverlevel = test2Driver; + name[0] = 0; + + len = dfunc.Read (test2Socket, net_message.data, net_message.maxsize, &clientaddr); + if (len < sizeof(int)) + goto Reschedule; + + net_message.cursize = len; + + MSG_BeginReading (); + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + goto Error; + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + goto Error; + if ((control & NETFLAG_LENGTH_MASK) != len) + goto Error; + + if (MSG_ReadByte() != CCREP_RULE_INFO) + goto Error; + + Q_strcpy(name, MSG_ReadString()); + if (name[0] == 0) + goto Done; + Q_strcpy(value, MSG_ReadString()); + + Con_Printf("%-16.16s %-16.16s\n", name, value); + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_RULE_INFO); + MSG_WriteString(&net_message, name); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (test2Socket, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + +Reschedule: + SchedulePollProcedure(&test2PollProcedure, 0.05); + return; + +Error: + Con_Printf("Unexpected repsonse to Rule Info request\n"); +Done: + dfunc.CloseSocket(test2Socket); + test2InProgress = false; + return; +} + +static void Test2_f (void) +{ + char *host; + int n; + struct qsockaddr sendaddr; + + if (test2InProgress) + return; + + host = Cmd_Argv (1); + + if (host && hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (Q_strcasecmp (host, hostcache[n].name) == 0) + { + if (hostcache[n].driver != myDriverLevel) + continue; + net_landriverlevel = hostcache[n].ldriver; + Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); + break; + } + if (n < hostCacheCount) + goto JustDoIt; + } + + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + { + if (!net_landrivers[net_landriverlevel].initialized) + continue; + + // see if we can resolve the host name + if (dfunc.GetAddrFromName(host, &sendaddr) != -1) + break; + } + if (net_landriverlevel == net_numlandrivers) + return; + +JustDoIt: + test2Socket = dfunc.OpenSocket(0); + if (test2Socket == -1) + return; + + test2InProgress = true; + test2Driver = net_landriverlevel; + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_RULE_INFO); + MSG_WriteString(&net_message, ""); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (test2Socket, net_message.data, net_message.cursize, &sendaddr); + SZ_Clear(&net_message); + SchedulePollProcedure(&test2PollProcedure, 0.05); +} + + +int Datagram_Init (void) +{ + int i; + int csock; + + myDriverLevel = net_driverlevel; + Cmd_AddCommand ("net_stats", NET_Stats_f); + + if (COM_CheckParm("-nolan")) + return -1; + + for (i = 0; i < net_numlandrivers; i++) + { + csock = net_landrivers[i].Init (); + if (csock == -1) + continue; + net_landrivers[i].initialized = true; + net_landrivers[i].controlSock = csock; + } + +#ifdef BAN_TEST + Cmd_AddCommand ("ban", NET_Ban_f); +#endif + Cmd_AddCommand ("test", Test_f); + Cmd_AddCommand ("test2", Test2_f); + + return 0; +} + + +void Datagram_Shutdown (void) +{ + int i; + +// +// shutdown the lan drivers +// + for (i = 0; i < net_numlandrivers; i++) + { + if (net_landrivers[i].initialized) + { + net_landrivers[i].Shutdown (); + net_landrivers[i].initialized = false; + } + } +} + + +void Datagram_Close (qsocket_t *sock) +{ + sfunc.CloseSocket(sock->socket); +} + + +void Datagram_Listen (qboolean state) +{ + int i; + + for (i = 0; i < net_numlandrivers; i++) + if (net_landrivers[i].initialized) + net_landrivers[i].Listen (state); +} + + +static qsocket_t *_Datagram_CheckNewConnections (void) +{ + struct qsockaddr clientaddr; + struct qsockaddr newaddr; + int newsock; + int acceptsock; + qsocket_t *sock; + qsocket_t *s; + int len; + int command; + int control; + int ret; + + acceptsock = dfunc.CheckNewConnections(); + if (acceptsock == -1) + return NULL; + + SZ_Clear(&net_message); + + len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr); + if (len < sizeof(int)) + return NULL; + net_message.cursize = len; + + MSG_BeginReading (); + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + return NULL; + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + return NULL; + if ((control & NETFLAG_LENGTH_MASK) != len) + return NULL; + + command = MSG_ReadByte(); + if (command == CCREQ_SERVER_INFO) + { + if (strcmp(MSG_ReadString(), "QUAKE") != 0) + return NULL; + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_SERVER_INFO); + dfunc.GetSocketAddr(acceptsock, &newaddr); + MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); + MSG_WriteString(&net_message, hostname.string); + MSG_WriteString(&net_message, sv.name); + MSG_WriteByte(&net_message, net_activeconnections); + MSG_WriteByte(&net_message, svs.maxclients); + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + + if (command == CCREQ_PLAYER_INFO) + { + int playerNumber; + int activeNumber; + int clientNumber; + client_t *client; + + playerNumber = MSG_ReadByte(); + activeNumber = -1; + for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++) + { + if (client->active) + { + activeNumber++; + if (activeNumber == playerNumber) + break; + } + } + if (clientNumber == svs.maxclients) + return NULL; + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_PLAYER_INFO); + MSG_WriteByte(&net_message, playerNumber); + MSG_WriteString(&net_message, client->name); + MSG_WriteLong(&net_message, client->colors); + MSG_WriteLong(&net_message, 0); + MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime)); + MSG_WriteString(&net_message, client->netconnection->address); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + + return NULL; + } + + if (command == CCREQ_RULE_INFO) + { + const char *prevCvarName; + cvar_t *var; + + // find the search start location + prevCvarName = MSG_ReadString(); + var = Cvar_FindVarAfter (prevCvarName, CVAR_SERVERINFO); + + // send the response + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_RULE_INFO); + if (var) + { + MSG_WriteString(&net_message, var->name); + MSG_WriteString(&net_message, var->string); + } + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + + return NULL; + } + + if (command != CCREQ_CONNECT) + return NULL; + + if (strcmp(MSG_ReadString(), "QUAKE") != 0) + return NULL; + + if (MSG_ReadByte() != NET_PROTOCOL_VERSION) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_REJECT); + MSG_WriteString(&net_message, "Incompatible version.\n"); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + +#ifdef BAN_TEST + // check for a ban + if (clientaddr.sa_family == AF_INET) + { + unsigned long testAddr; + testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; + if ((testAddr & banMask) == banAddr) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_REJECT); + MSG_WriteString(&net_message, "You have been banned.\n"); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + } +#endif + + // see if this guy is already connected + for (s = net_activeSockets; s; s = s->next) + { + if (s->driver != net_driverlevel) + continue; + ret = dfunc.AddrCompare(&clientaddr, &s->addr); + if (ret >= 0) + { + // is this a duplicate connection reqeust? + if (ret == 0 && net_time - s->connecttime < 2.0) + { + // yes, so send a duplicate reply + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_ACCEPT); + dfunc.GetSocketAddr(s->socket, &newaddr); + MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + // it's somebody coming back in from a crash/disconnect + // so close the old qsocket and let their retry get them back in + NET_Close(s); + return NULL; + } + } + + // allocate a QSocket + sock = NET_NewQSocket (); + if (sock == NULL) + { + // no room; try to let him know + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_REJECT); + MSG_WriteString(&net_message, "Server is full.\n"); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + + // allocate a network socket + newsock = dfunc.OpenSocket(0); + if (newsock == -1) + { + NET_FreeQSocket(sock); + return NULL; + } + + // connect to the client + if (dfunc.Connect (newsock, &clientaddr) == -1) + { + dfunc.CloseSocket(newsock); + NET_FreeQSocket(sock); + return NULL; + } + + // everything is allocated, just fill in the details + sock->socket = newsock; + sock->landriver = net_landriverlevel; + sock->addr = clientaddr; + Q_strcpy(sock->address, dfunc.AddrToString(&clientaddr)); + + // send him back the info about the server connection he has been allocated + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_ACCEPT); + dfunc.GetSocketAddr(newsock, &newaddr); + MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); +// MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + + return sock; +} + +qsocket_t *Datagram_CheckNewConnections (void) +{ + qsocket_t *ret = NULL; + + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + if (net_landrivers[net_landriverlevel].initialized) + if ((ret = _Datagram_CheckNewConnections ()) != NULL) + break; + return ret; +} + + +static void _Datagram_SearchForHosts (qboolean xmit) +{ + int ret; + int n; + int i; + struct qsockaddr readaddr; + struct qsockaddr myaddr; + int control; + + dfunc.GetSocketAddr (dfunc.controlSock, &myaddr); + if (xmit) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); + MSG_WriteString(&net_message, "QUAKE"); + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize); + SZ_Clear(&net_message); + } + + while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0) + { + if (ret < sizeof(int)) + continue; + net_message.cursize = ret; + + // don't answer our own query + if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0) + continue; + + // is the cache full? + if (hostCacheCount == HOSTCACHESIZE) + continue; + + MSG_BeginReading (); + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + continue; + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + continue; + if ((control & NETFLAG_LENGTH_MASK) != ret) + continue; + + if (MSG_ReadByte() != CCREP_SERVER_INFO) + continue; + + dfunc.GetAddrFromName(MSG_ReadString(), &readaddr); + // search the cache for this server + for (n = 0; n < hostCacheCount; n++) + if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0) + break; + + // is it already there? + if (n < hostCacheCount) + continue; + + // add it + hostCacheCount++; + Q_strcpy(hostcache[n].name, MSG_ReadString()); + Q_strcpy(hostcache[n].map, MSG_ReadString()); + hostcache[n].users = MSG_ReadByte(); + hostcache[n].maxusers = MSG_ReadByte(); + if (MSG_ReadByte() != NET_PROTOCOL_VERSION) + { + Q_strcpy(hostcache[n].cname, hostcache[n].name); + hostcache[n].cname[14] = 0; + Q_strcpy(hostcache[n].name, "*"); + Q_strcat(hostcache[n].name, hostcache[n].cname); + } + Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); + hostcache[n].driver = net_driverlevel; + hostcache[n].ldriver = net_landriverlevel; + Q_strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); + + // check for a name conflict + for (i = 0; i < hostCacheCount; i++) + { + if (i == n) + continue; + if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0) + { + i = Q_strlen(hostcache[n].name); + if (i < 15 && hostcache[n].name[i-1] > '8') + { + hostcache[n].name[i] = '0'; + hostcache[n].name[i+1] = 0; + } + else + hostcache[n].name[i-1]++; + i = -1; + } + } + } +} + +void Datagram_SearchForHosts (qboolean xmit) +{ + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + { + if (hostCacheCount == HOSTCACHESIZE) + break; + if (net_landrivers[net_landriverlevel].initialized) + _Datagram_SearchForHosts (xmit); + } +} + + +static qsocket_t *_Datagram_Connect (char *host) +{ + struct qsockaddr sendaddr; + struct qsockaddr readaddr; + qsocket_t *sock; + int newsock; + int ret; + int reps; + double start_time; + int control; + char *reason; + + // see if we can resolve the host name + if (dfunc.GetAddrFromName(host, &sendaddr) == -1) + return NULL; + + newsock = dfunc.OpenSocket (0); + if (newsock == -1) + return NULL; + + sock = NET_NewQSocket (); + if (sock == NULL) + goto ErrorReturn2; + sock->socket = newsock; + sock->landriver = net_landriverlevel; + + // connect to the host + if (dfunc.Connect (newsock, &sendaddr) == -1) + goto ErrorReturn; + + // send the connection request + Con_Printf("trying...\n"); SCR_UpdateScreen (); + start_time = net_time; + + for (reps = 0; reps < 3; reps++) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_CONNECT); + MSG_WriteString(&net_message, "QUAKE"); + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); + SZ_Clear(&net_message); + do + { + ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr); + // if we got something, validate it + if (ret > 0) + { + // is it from the right place? + if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) + { +#ifdef DEBUG + Con_Printf("wrong reply address\n"); + Con_Printf("Expected: %s\n", StrAddr (&sendaddr)); + Con_Printf("Received: %s\n", StrAddr (&readaddr)); + SCR_UpdateScreen (); +#endif + ret = 0; + continue; + } + + if (ret < sizeof(int)) + { + ret = 0; + continue; + } + + net_message.cursize = ret; + MSG_BeginReading (); + + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + { + ret = 0; + continue; + } + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + { + ret = 0; + continue; + } + if ((control & NETFLAG_LENGTH_MASK) != ret) + { + ret = 0; + continue; + } + } + } + while (ret == 0 && (SetNetTime() - start_time) < 2.5); + if (ret) + break; + Con_Printf("still trying...\n"); SCR_UpdateScreen (); + start_time = SetNetTime(); + } + + if (ret == 0) + { + reason = "No Response"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + if (ret == -1) + { + reason = "Network Error"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + ret = MSG_ReadByte(); + if (ret == CCREP_REJECT) + { + reason = MSG_ReadString(); + Con_Printf(reason); + Q_strncpy(m_return_reason, reason, 31); + goto ErrorReturn; + } + + if (ret == CCREP_ACCEPT) + { + Q_memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); + dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); + } + else + { + reason = "Bad Response"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + dfunc.GetNameFromAddr (&sendaddr, sock->address); + + Con_Printf ("Connection accepted\n"); + sock->lastMessageTime = SetNetTime(); + + // switch the connection to the specified address + if (dfunc.Connect (newsock, &sock->addr) == -1) + { + reason = "Connect to Game failed"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + m_return_onerror = false; + return sock; + +ErrorReturn: + NET_FreeQSocket(sock); +ErrorReturn2: + dfunc.CloseSocket(newsock); + if (m_return_onerror) + { + key_dest = key_menu; + m_state = m_return_state; + m_return_onerror = false; + } + return NULL; +} + +qsocket_t *Datagram_Connect (char *host) +{ + qsocket_t *ret = NULL; + + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + if (net_landrivers[net_landriverlevel].initialized) + if ((ret = _Datagram_Connect (host)) != NULL) + break; + return ret; +} diff --git a/source/ctr/net_dgrm.h b/source/ctr/net_dgrm.h new file mode 100644 index 0000000..da052e7 --- /dev/null +++ b/source/ctr/net_dgrm.h @@ -0,0 +1,34 @@ +/* +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_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); diff --git a/source/ctr/net_main.c b/source/ctr/net_main.c new file mode 100644 index 0000000..3d659c8 --- /dev/null +++ b/source/ctr/net_main.c @@ -0,0 +1,997 @@ +/* +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_main.c + +#include "../quakedef.h" +#include "../net_vcr.h" + +qsocket_t *net_activeSockets = NULL; +qsocket_t *net_freeSockets = NULL; +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; +static double slistStartTime; +static int slistLastShown; + +static void Slist_Send(void); +static void Slist_Poll(void); +PollProcedure slistSendProcedure = {NULL, 0.0, Slist_Send}; +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 + +int vcrFile = -1; +qboolean recording = false; + +// these two macros are to make the code more readable +#define sfunc net_drivers[sock->driver] +#define dfunc net_drivers[net_driverlevel] + +int net_driverlevel; + + +double net_time; + +double SetNetTime(void) +{ + net_time = Sys_FloatTime(); + 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 == NULL) + 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; + Q_strcpy (sock->address,"UNSET ADDRESS"); + sock->driver = net_driverlevel; + 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 ("NET_FreeQSocket: not active\n"); + } + + // add it to free list + sock->next = net_freeSockets; + net_freeSockets = sock; + sock->disconnected = true; +} + + +static void NET_Listen_f (void) +{ + if (Cmd_Argc () != 2) + { + Con_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0); + return; + } + + listening = Q_atoi(Cmd_Argv(1)) ? true : false; + + for (net_driverlevel=0 ; net_driverlevel 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"); + 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_FloatTime(); + + SchedulePollProcedure(&slistSendProcedure, 0.0); + SchedulePollProcedure(&slistPollProcedure, 0.1); + + hostCacheCount = 0; +} + + +static void Slist_Send(void) +{ + for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++) + { + if (!slistLocal && net_driverlevel == 0) + continue; + if (net_drivers[net_driverlevel].initialized == false) + continue; + dfunc.SearchForHosts (true); + } + + if ((Sys_FloatTime() - slistStartTime) < 0.5) + SchedulePollProcedure(&slistSendProcedure, 0.75); +} + + +static void Slist_Poll(void) +{ + for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++) + { + if (!slistLocal && net_driverlevel == 0) + continue; + if (net_drivers[net_driverlevel].initialized == false) + continue; + dfunc.SearchForHosts (false); + } + + if (! slistSilent) + PrintSlist(); + + if ((Sys_FloatTime() - 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 n; + int numdrivers = net_numdrivers; + + SetNetTime(); + + if (host && *host == 0) + host = NULL; + + if (host) + { + if (Q_strcasecmp (host, "local") == 0) + { + numdrivers = 1; + goto JustDoIt; + } + + if (hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (Q_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 (Q_strcasecmp (host, hostcache[n].name) == 0) + { + host = hostcache[n].cname; + break; + } + +JustDoIt: + for (net_driverlevel=0 ; net_driverleveladdress, 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 + sfunc.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 +================= +*/ + +struct +{ + double time; + int op; + long session; + int ret; + int len; +} vcrGetMessage; + +extern void PrintStats(qsocket_t *s); + +int NET_GetMessage (qsocket_t *sock) +{ + int ret; + + if (!sock) + return -1; + + if (sock->disconnected) + { + Con_Printf("NET_GetMessage: disconnected socket\n"); + return -1; + } + + SetNetTime(); + + ret = sfunc.QGetMessage(sock); + + // see if this connection has timed out + if (ret == 0 && sock->driver) + { + if (net_time - sock->lastMessageTime > net_messagetimeout.value) + { + NET_Close(sock); + return -1; + } + } + + + if (ret > 0) + { + if (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 +================== +*/ +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("NET_SendMessage: disconnected socket\n"); + return -1; + } + + SetNetTime(); + r = sfunc.QSendMessage(sock, data); + if (r == 1 && 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 = sfunc.SendUnreliableMessage(sock, data); + if (r == 1 && 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 = sfunc.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 ; inetconnection) + continue; + if (host_client->active) + { + if (host_client->netconnection->driver == 0) + { + 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_FloatTime(); + while (count) + { + count = 0; + for (i=0, host_client = svs.clients ; inetconnection)) + { + 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_FloatTime() - 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 ("NET_Init: you must specify a number after -port"); + } + 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 (net_driverlevel=0 ; net_driverlevelnext) + NET_Close(sock); + +// +// shutdown the drivers +// + for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) + { + if (net_drivers[net_driverlevel].initialized == true) + { + net_drivers[net_driverlevel].Shutdown (); + net_drivers[net_driverlevel].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(); + + 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_FloatTime() + 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 diff --git a/source/ctr/net_udp.h b/source/ctr/net_udp.h new file mode 100644 index 0000000..7530301 --- /dev/null +++ b/source/ctr/net_udp.h @@ -0,0 +1,39 @@ +/* +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_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); diff --git a/source/ctr/net_udpctr.c b/source/ctr/net_udpctr.c new file mode 100644 index 0000000..2b55600 --- /dev/null +++ b/source/ctr/net_udpctr.c @@ -0,0 +1,445 @@ +/* +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_udp.c + +#include "../quakedef.h" +#include "net_udp.h" + +#include +#include +#include + +#include <3ds.h> +#include + +inline uint32_t htonl(uint32_t hostshort) +{ + return __builtin_bswap32(hostshort); +} + +inline uint16_t htons(uint16_t hostshort) +{ + return __builtin_bswap16(hostshort); +} + +inline uint32_t ntohl(uint32_t netlong) +{ + return __builtin_bswap32(netlong); +} + +inline uint16_t ntohs(uint16_t netshort) +{ + return __builtin_bswap16(netshort); +} + +#define SOC_BUFFERSIZE 0x100000 +#define SOC_ALIGN 0x1000 + +static u32 *SOC_buffer = NULL; + +extern int close (int); + +extern cvar_t hostname; + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket; +static int net_broadcastsocket = 0; +static struct qsockaddr broadcastaddr; + +static unsigned long myAddr; + +#include "net_udp.h" + +//============================================================================= + +int UDP_Init (void) +{ + struct hostent *local; + char buff[15]; + struct qsockaddr addr; + char *colon; + int ret; + + if (COM_CheckParm ("-noudp")) + return -1; + + SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE); + + if(SOC_buffer == NULL) + { + Sys_Error("Failed to allocate SOC_Buffer\n"); + } + ret = socInit(SOC_buffer, SOC_BUFFERSIZE); + + if(ret != 0) + { + + free(SOC_buffer); + return -1; + } + myAddr = gethostid(); + + // if the quake hostname isn't set, set it to the machine name + if (strcmp(hostname.string, "UNNAMED") == 0) + { + Cvar_Set ("hostname", "3ds"); + } + + if ((net_controlsocket = UDP_OpenSocket (5000)) == -1) //Passing 0 causes function to fail on 3DS + { + socExit(); + free(SOC_buffer); + 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(net_hostport); + + UDP_GetSocketAddr (net_controlsocket, &addr); + Q_strcpy(my_tcpip_address, UDP_AddrToString (&addr)); + colon = Q_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); + socExit(); +} + +//============================================================================= + +void UDP_Listen (qboolean state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + if ((net_acceptsocket = UDP_OpenSocket (net_hostport)) == -1) + Sys_Error ("UDP_Listen: Unable to open accept socket\n"); + 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; + int yes = 1; + int rc; + + if ((newsocket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + return -1; + + int flags = fcntl(newsocket, F_GETFL, 0); + if ( fcntl(newsocket, F_SETFL, flags | O_NONBLOCK) == -1) + goto ErrorReturn; + + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(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) +{ + char buf[4096]; + + if (net_acceptsocket == -1) + return -1; + + if (recvfrom(net_acceptsocket, buf, 4096, MSG_PEEK, NULL, NULL) > 0) + 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 ) + return 0; + return ret; +} + +//============================================================================= + +int UDP_MakeSocketBroadcastCapable (int socket) +{ + return -1; +} + + +//============================================================================= + +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\n"); + 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 ) + 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; + + Q_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) + { + Q_strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); + return 0; + } + + Q_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; +} + +//============================================================================= \ No newline at end of file diff --git a/source/ctr/r_local.h b/source/ctr/r_local.h new file mode 100644 index 0000000..a7cb959 --- /dev/null +++ b/source/ctr/r_local.h @@ -0,0 +1,314 @@ +/* +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_local.h -- private refresh defs + +#ifndef GLQUAKE +#include "r_shared.h" + +#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) + // normalizing factor so player model works out to about + // 1 pixel per triangle + +#define BMODEL_FULLY_CLIPPED 0x10 // value returned by R_BmodelCheckBBox () + // if bbox is trivially rejected + +//=========================================================================== +// viewmodel lighting + +typedef struct { + int ambientlight; + int shadelight; + float *plightvec; +} alight_t; + +//=========================================================================== +// clipped bmodel edges + +typedef struct bedge_s +{ + mvertex_t *v[2]; + struct bedge_s *pnext; +} bedge_t; + +typedef struct { + float fv[3]; // viewspace x, y +} auxvert_t; + +//=========================================================================== + +extern cvar_t r_draworder; +extern cvar_t r_speeds; +extern cvar_t r_timegraph; +extern cvar_t r_graphheight; +extern cvar_t r_clearcolor; +extern cvar_t r_waterwarp; +extern cvar_t r_fullbright; +extern cvar_t r_drawentities; +extern cvar_t r_aliasstats; +extern cvar_t r_dspeeds; +extern cvar_t r_drawflat; +extern cvar_t r_ambient; +extern cvar_t r_reportsurfout; +extern cvar_t r_maxsurfs; +extern cvar_t r_numsurfs; +extern cvar_t r_reportedgeout; +extern cvar_t r_maxedges; +extern cvar_t r_numedges; + +#define XCENTERING (1.0 / 2.0) +#define YCENTERING (1.0 / 2.0) + +#define CLIP_EPSILON 0.001 + +#define BACKFACE_EPSILON 0.01 + +//=========================================================================== + +#define DIST_NOT_SET 98765 + +typedef struct clipplane_s +{ + vec3_t normal; + float dist; + struct clipplane_s *next; + byte leftedge; + byte rightedge; + byte reserved[2]; +} clipplane_t; + +extern clipplane_t view_clipplanes[4]; + +//============================================================================= + +void R_RenderWorld (void); + +//============================================================================= + +extern mplane_t screenedge[4]; + +extern vec3_t r_origin; + +extern vec3_t r_entorigin; + +extern float screenAspect; +extern float verticalFieldOfView; +extern float xOrigin, yOrigin; + +extern int r_visframecount; + +//============================================================================= + +extern int vstartscan; + + +void R_ClearPolyList (void); +void R_DrawPolyList (void); + +// +// current entity info +// +extern qboolean insubmodel; +extern vec3_t r_worldmodelorg; + + +void R_DrawSprite (void); +void R_RenderFace (msurface_t *fa, int clipflags); +void R_RenderPoly (msurface_t *fa, int clipflags); +void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf); +void R_TransformPlane (mplane_t *p, float *normal, float *dist); +void R_TransformFrustum (void); +void R_SetSkyFrame (void); +void R_DrawSurfaceBlock16 (void); +void R_DrawSurfaceBlock8 (void); +texture_t *R_TextureAnimation (texture_t *base); + +#if id386 + +void R_DrawSurfaceBlock8_mip0 (void); +void R_DrawSurfaceBlock8_mip1 (void); +void R_DrawSurfaceBlock8_mip2 (void); +void R_DrawSurfaceBlock8_mip3 (void); + +#endif + +void R_GenSkyTile (void *pdest); +void R_GenSkyTile16 (void *pdest); +void R_Surf8Patch (void); +void R_Surf16Patch (void); +void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags); +void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel); + +void R_AddPolygonEdges (emitpoint_t *pverts, int numverts, int miplevel); +surf_t *R_GetSurf (void); +void R_AliasDrawModel (alight_t *plighting); +void R_BeginEdgeFrame (void); +void R_ScanEdges (void); +void D_DrawSurfaces (void); +void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist); +void R_StepActiveU (edge_t *pedge); +void R_RemoveEdges (edge_t *pedge); + +extern void R_Surf8Start (void); +extern void R_Surf8End (void); +extern void R_Surf16Start (void); +extern void R_Surf16End (void); +extern void R_EdgeCodeStart (void); +extern void R_EdgeCodeEnd (void); + +extern void R_RotateBmodel (void); + +extern int c_faceclip; +extern int r_polycount; +extern int r_wholepolycount; + +extern model_t *cl_worldmodel; + +extern int *pfrustum_indexes[4]; + +#define NEAR_CLIP 0.01 + +extern int ubasestep, errorterm, erroradjustup, erroradjustdown; +extern int vstartscan; + +extern fixed16_t sadjust, tadjust; +extern fixed16_t bbextents, bbextentt; + +#define MAXBVERTINDEXES 1000 // new clipped vertices when clipping bmodels + // to the world BSP +extern mvertex_t *r_ptverts, *r_ptvertsmax; + +extern vec3_t sbaseaxis[3], tbaseaxis[3]; +extern float entity_rotation[3][3]; + +extern int reinit_surfcache; + +extern int r_currentkey; +extern int r_currentbkey; + +typedef struct btofpoly_s { + int clipflags; + msurface_t *psurf; +} btofpoly_t; + +#define MAX_BTOFPOLYS 5000 // FIXME: tune this + +extern int numbtofpolys; +extern btofpoly_t *pbtofpolys; + +void R_InitTurb (void); +void R_ZDrawSubmodelPolys (model_t *clmodel); + +//========================================================= +// Alias models +//========================================================= + +#define MAXALIASVERTS 2000 // TODO: tune this +#define ALIAS_Z_CLIP_PLANE 5 + +extern int numverts; +extern int a_skinwidth; +extern mtriangle_t *ptriangles; +extern int numtriangles; +extern aliashdr_t *paliashdr; +extern mdl_t *pmdl; +extern float leftclip, topclip, rightclip, bottomclip; +extern int r_acliptype; +extern finalvert_t *pfinalverts; +extern auxvert_t *pauxverts; + +qboolean R_AliasCheckBBox (void); + +//========================================================= +// turbulence stuff + +#define AMP 8*0x10000 +#define AMP2 3 +#define SPEED 20 + +//========================================================= +// particle stuff + +void R_DrawParticles (void); +void R_InitParticles (void); +void R_ClearParticles (void); +void R_ReadPointFile_f (void); +void R_SurfacePatch (void); + +extern int r_amodels_drawn; +extern edge_t *auxedges; +extern int r_numallocatededges; +extern edge_t *r_edges, *edge_p, *edge_max; + +extern edge_t *newedges[MAXHEIGHT]; +extern edge_t *removeedges[MAXHEIGHT]; + +extern int screenwidth; + +// FIXME: make stack vars when debugging done +extern edge_t edge_head; +extern edge_t edge_tail; +extern edge_t edge_aftertail; +extern int r_bmodelactive; +extern vrect_t *pconupdate; + +extern float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; +extern float r_aliastransition, r_resfudge; + +extern int r_outofsurfaces; +extern int r_outofedges; + +extern mvertex_t *r_pcurrentvertbase; +extern int r_maxvalidedgeoffset; + +void R_AliasClipTriangle (mtriangle_t *ptri); + +extern float r_time1; +extern float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; +extern float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; +extern int r_frustum_indexes[4*6]; +extern int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +extern qboolean r_surfsonstack; +extern cshift_t cshift_water; +extern qboolean r_dowarpold, r_viewchanged; + +extern mleaf_t *r_viewleaf, *r_oldviewleaf; + +extern vec3_t r_emins, r_emaxs; +extern mnode_t *r_pefragtopnode; +extern int r_clipflags; +extern int r_dlightframecount; +extern qboolean r_fov_greater_than_90; + +void R_StoreEfrags (efrag_t **ppefrag); +void R_TimeRefresh_f (void); +void R_TimeGraph (void); +void R_PrintAliasStats (void); +void R_PrintTimes (void); +void R_PrintDSpeeds (void); +void R_AnimateLight (void); +int R_LightPoint (vec3_t p); +void R_SetupFrame (void); +void R_cshift_f (void); +void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1); +void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip); +void R_SplitEntityOnNode2 (mnode_t *node); +void R_MarkLights (dlight_t *light, int bit, mnode_t *node); + +#endif \ No newline at end of file diff --git a/source/ctr/r_part.c b/source/ctr/r_part.c new file mode 100644 index 0000000..942e670 --- /dev/null +++ b/source/ctr/r_part.c @@ -0,0 +1,851 @@ +/* +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" + +#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"); + + QMB_InitParticles(); +} + +void R_DarkFieldParticles (entity_t *ent) +{ + int i, j, k; + particle_t *p; + float vel; + vec3_t dir; + vec3_t org; + + org[0] = ent->origin[0]; + org[1] = ent->origin[1]; + org[2] = ent->origin[2]; + for (i=-16 ; i<16 ; i+=8) + for (j=-16 ; j<16 ; j+=8) + for (k=0 ; k<32 ; k+=8) + { + 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[0] = p->color[1] = p->color[2] = 150 + rand()%6; + 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); + } +} + + +/* +=============== +R_EntityParticles +=============== +*/ + +#define NUMVERTEXNORMALS 162 +extern float r_avertexnormals[NUMVERTEXNORMALS][3]; +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 ; inext; + p->next = active_particles; + active_particles = p; + + p->die = cl.time + 0.01; + p->color[0] = p->color[1] = p->color[2] = 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_Clear_Classic_Particles (void) +{ + int i; + + free_particles = &particles[0]; + active_particles = NULL; + + for (i=0 ;inext; + p->next = active_particles; + active_particles = p; + + p->die = 99999; + p->color[0] = p->color[1] = p->color[2] = (-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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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 + +=============== +*/ + +#define RunParticleEffect(org, dir, color, count) \ + if (qmb_initialized) \ + QMB_RunParticleEffect (org, dir, color, count); \ + else \ + Run_Classic_ParticleEffect (org, dir, color, count); + +void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) +{ + if (color == 73 || color == 225) + { + RunParticleEffect(org, dir, color, count); + return; + } + + switch (count) + { + case 10: + case 20: + case 30: + RunParticleEffect(org, dir, color, count); + break; + default: + RunParticleEffect(org, dir, color, count); + } +} + + +/* +=============== +Run_Classic_ParticleEffect +=============== +*/ + +void Run_Classic_ParticleEffect (vec3_t org, vec3_t dir, int color, int count) +{ + int i, j; + particle_t *p; + + for (i=0 ; inext; + p->next = active_particles; + active_particles = p; + + if (count == 1024) + { // rocket explosion + p->die = cl.time + 5; + p->color[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = (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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 52 + ((tracercount&4)<<1); + else + p->color[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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_Classic_DrawParticles +=============== +*/ +extern cvar_t sv_gravity; + +void R_Classic_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; + + 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; + + glColor4ubv (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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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[0] = p->color[1] = p->color[2] = 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: +#ifdef QUAKE2 + p->vel[2] -= grav * 20; + break; +#endif + 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 +} + +/* +=============== +R_DrawParticles +=============== +*/ + +void R_DrawParticles (void) +{ + R_Classic_DrawParticles (); + QMB_DrawParticles (); +} diff --git a/source/ctr/r_shared.h b/source/ctr/r_shared.h new file mode 100644 index 0000000..73ea1db --- /dev/null +++ b/source/ctr/r_shared.h @@ -0,0 +1,155 @@ +/* +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 GLQUAKE +// r_shared.h: general refresh-related stuff shared between the refresh and the +// driver + +// FIXME: clean up and move into d_iface.h + +#ifndef _R_SHARED_H_ +#define _R_SHARED_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 MAXDIMENSION ((MAXHEIGHT > MAXWIDTH) ? MAXHEIGHT : MAXWIDTH) + +#define SIN_BUFFER_SIZE (MAXDIMENSION+CYCLE) + +#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; + +extern int sintable[SIN_BUFFER_SIZE]; +extern int intsintable[SIN_BUFFER_SIZE]; + +extern vec3_t vup, base_vup; +extern vec3_t vpn, base_vpn; +extern vec3_t vright, base_vright; +extern entity_t *currententity; + +#define NUMSTACKEDGES 2400 +#define MINEDGES NUMSTACKEDGES +#define NUMSTACKSURFACES 800 +#define MINSURFACES NUMSTACKSURFACES +#define MAXSPANS 3000 + +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 + +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 // _R_SHARED_H_ + +#endif // GLQUAKE diff --git a/source/ctr/render.h b/source/ctr/render.h new file mode 100644 index 0000000..560c144 --- /dev/null +++ b/source/ctr/render.h @@ -0,0 +1,199 @@ +/* +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. + +*/ + +// refresh.h -- public interface to refresh functions + +#define MAXCLIPPLANES 11 + +#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 origin; + vec3_t msg_angles[2]; // last two updates (0 is newest) + vec3_t angles; + + // Tomaz - QC Alpha Scale Glow Begin + float renderamt; + float rendermode; + float rendercolor[3]; + //Crow_bar + + unsigned char scale; + struct model_s *model; // NULL = no model + char old_model[128]; // NULL = no model + struct efrag_s *efrag; // linked list of efrags + int frame; + float syncbase; // for client-side animations + byte *colormap; + int effects; // light, particals, etc + int skinnum; // for Alias models + int visframe; // last frame this entity was + // found in an active leaf + + // fenix@io.com: model transform interpolation + float translate_start_time; + vec3_t origin1; + vec3_t origin2; + + float rotate_start_time; + vec3_t angles1; + vec3_t angles2; + + 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 + + float frame_start_time; + float frame_interval; + int pose1; + int pose2; + + int modelindex; + + int z_head; + int z_larm; + int z_rarm; + +} entity_t; + +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; + + float fog_start; + float fog_end; + float fog_red; + float fog_green; + float fog_blue; +} refdef_t; + + +// +// refresh +// +extern int reinit_surfcache; + + +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 miptex_s *mt); // called at level load + +void R_AddEfrags (entity_t *ent); +void R_RemoveEfrags (entity_t *ent); + +void R_NewMap (void); + +// particles + +typedef enum trail_type_s +{ + ROCKET_TRAIL, GRENADE_TRAIL, BLOOD_TRAIL, TRACER1_TRAIL, SLIGHT_BLOOD_TRAIL,NAIL_TRAIL, + TRACER2_TRAIL, VOOR_TRAIL, ALT_ROCKET_TRAIL, LAVA_TRAIL, BUBBLE_TRAIL, NEHAHRA_SMOKE, + RAYGREEN_TRAIL, RAYRED_TRAIL +} trail_type_t; + +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); + +#ifdef QUAKE2 +void R_DarkFieldParticles (entity_t *ent); +#endif +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); + + +// +// surface cache related +// +extern int reinit_surfcache; // if 1, surface cache is currently empty and +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); diff --git a/source/ctr/sbar.c b/source/ctr/sbar.c new file mode 100644 index 0000000..ca32292 --- /dev/null +++ b/source/ctr/sbar.c @@ -0,0 +1,335 @@ +/* +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. + +*/ +// sbar.c -- status bar code + +#include "../quakedef.h" + + +int sb_updates; // if >= vid.numpages, no update needed + +#define STAT_MINUS 10 // num frame for '-' stats digit +qpic_t *sb_nums[2][11]; +qpic_t *sb_colon, *sb_slash; +qpic_t *sb_ibar; +qpic_t *sb_sbar; +qpic_t *sb_scorebar; + +qpic_t *sb_weapons[7][8]; // 0 is active, 1 is owned, 2-5 are flashes +qpic_t *sb_ammo[4]; +qpic_t *sb_sigil[4]; +qpic_t *sb_armor[3]; +qpic_t *sb_items[32]; + +qpic_t *sb_faces[7][2]; // 0 is gibbed, 1 is dead, 2-6 are alive + // 0 is static, 1 is temporary animation +qpic_t *sb_face_invis; +qpic_t *sb_face_quad; +qpic_t *sb_face_invuln; +qpic_t *sb_face_invis_invuln; + +extern qboolean sb_showscores; + +extern int sb_lines; // scan lines to draw + +qpic_t *rsb_invbar[2]; +qpic_t *rsb_weapons[5]; +qpic_t *rsb_items[2]; +qpic_t *rsb_ammo[3]; +qpic_t *rsb_teambord; // PGM 01/19/97 - team color border + +//MED 01/04/97 added two more weapons + 3 alternates for grenade launcher +qpic_t *hsb_weapons[7][5]; // 0 is active, 1 is owned, 2-5 are flashes +//MED 01/04/97 added array to simplify weapon parsing +//int hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT}; +//MED 01/04/97 added hipnotic items array +qpic_t *hsb_items[2]; + +void Sbar_MiniDeathmatchOverlay (void); +void Sbar_DeathmatchOverlay (void); +void M_DrawPic (int x, int y, qpic_t *pic); + +/* +=============== +Sbar_ShowScores + +Tab key down +=============== +*/ +void Sbar_ShowScores (void) +{ + +} + +/* +=============== +Sbar_DontShowScores + +Tab key up +=============== +*/ +void Sbar_DontShowScores (void) +{ + +} + +/* +=============== +Sbar_Changed +=============== +*/ +void Sbar_Changed (void) +{ + +} + +/* +=============== +Sbar_Init +=============== +*/ +void Sbar_Init (void) +{ + +} + + +//============================================================================= + +// drawing routines are relative to the status bar location + +/* +============= +Sbar_DrawPic +============= +*/ +void Sbar_DrawPic (int x, int y, qpic_t *pic) +{ + +} + +/* +============= +Sbar_DrawTransPic +============= +*/ +void Sbar_DrawTransPic (int x, int y, qpic_t *pic) +{ + +} + +/* +================ +Sbar_DrawCharacter + +Draws one solid graphics character +================ +*/ +void Sbar_DrawCharacter (int x, int y, int num) +{ + +} + +/* +================ +Sbar_DrawString +================ +*/ +void Sbar_DrawString (int x, int y, char *str) +{ + +} + +/* +============= +Sbar_itoa +============= +*/ +int Sbar_itoa (int num, char *buf) +{ + +} + + +/* +============= +Sbar_DrawNum +============= +*/ +void Sbar_DrawNum (int x, int y, int num, int digits, int color) +{ + +} + +//============================================================================= + +int fragsort[MAX_SCOREBOARD]; + + +/* +=============== +Sbar_SortFrags +=============== +*/ +void Sbar_SortFrags (void) +{ + +} + +int Sbar_ColorForMap (int m) +{ + return m < 128 ? m + 8 : m + 8; +} + +/* +=============== +Sbar_UpdateScoreboard +=============== +*/ +void Sbar_UpdateScoreboard (void) +{ + +} + + + +/* +=============== +Sbar_SoloScoreboard +=============== +*/ +void Sbar_SoloScoreboard (void) +{ + +} + +/* +=============== +Sbar_DrawScoreboard +=============== +*/ +void Sbar_DrawScoreboard (void) +{ + +} + +//============================================================================= + +/* +=============== +Sbar_DrawInventory +=============== +*/ +void Sbar_DrawInventory (void) +{ + +} + +//============================================================================= + +/* +=============== +Sbar_DrawFrags +=============== +*/ +void Sbar_DrawFrags (void) +{ + +} + +//============================================================================= + + +/* +=============== +Sbar_DrawFace +=============== +*/ +void Sbar_DrawFace (void) +{ + +} + +/* +=============== +Sbar_Draw +=============== +*/ +void Sbar_Draw (void) +{ + +} + +//============================================================================= + +/* +================== +Sbar_IntermissionNumber + +================== +*/ +void Sbar_IntermissionNumber (int x, int y, int num, int digits, int color) +{ + +} + +/* +================== +Sbar_DeathmatchOverlay + +================== +*/ +void Sbar_DeathmatchOverlay (void) +{ + +} + +/* +================== +Sbar_DeathmatchOverlay + +================== +*/ +void Sbar_MiniDeathmatchOverlay (void) +{ + +} + +/* +================== +Sbar_IntermissionOverlay + +================== +*/ +void Sbar_IntermissionOverlay (void) +{ + +} + + +/* +================== +Sbar_FinaleOverlay + +================== +*/ +void Sbar_FinaleOverlay (void) +{ + +} diff --git a/source/ctr/sbar.h b/source/ctr/sbar.h new file mode 100644 index 0000000..286b3b6 --- /dev/null +++ b/source/ctr/sbar.h @@ -0,0 +1,39 @@ +/* +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. + +*/ + +// the status bar is only redrawn if something has changed, but if anything +// does, the entire thing will be redrawn for the next vid.numpages frames. + +#define SBAR_HEIGHT 24 + +extern int sb_lines; // scan lines to draw + +void Sbar_Init (void); + +void Sbar_Changed (void); +// call whenever any of the client stats represented on the sbar changes + +void Sbar_Draw (void); +// called every frame by screen + +void Sbar_IntermissionOverlay (void); +// called each frame after the level has been completed + +void Sbar_FinaleOverlay (void); diff --git a/source/ctr/screen.h b/source/ctr/screen.h new file mode 100644 index 0000000..390e9ca --- /dev/null +++ b/source/ctr/screen.h @@ -0,0 +1,62 @@ +/* +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.h + +void SCR_Init (void); + +void SCR_UpdateScreen (void); + + +void SCR_SizeUp (void); +void SCR_SizeDown (void); +void SCR_BringDownConsole (void); +void SCR_CenterPrint (char *str); +void SCR_UsePrint (int type, int cost, int weapon); +qpic_t *GetButtonIcon (char *buttonname); +char *GetGrenadeButtonL(); + +void SCR_BeginLoadingPlaque (void); +void SCR_EndLoadingPlaque (void); + +int SCR_ModalMessage (char *text); + +extern float scr_con_current; +extern float scr_conlines; // lines of console to display + +extern int scr_fullupdate; // set to 0 to force full redraw +extern int sb_lines; + +extern int clearnotify; // set to 0 whenever notify text is drawn +extern qboolean scr_disabled_for_loading; +extern qboolean scr_skipupdate; + +extern cvar_t scr_viewsize; + +extern cvar_t scr_viewsize; + +// only the refresh window will be updated unless these variables are flagged +extern int scr_copytop; +extern int scr_copyeverything; + +extern qboolean block_drawing; + +void SCR_UpdateWholeScreen (void); + +extern cvar_t scr_fov; \ No newline at end of file diff --git a/source/ctr/server.h b/source/ctr/server.h new file mode 100644 index 0000000..ec856b8 --- /dev/null +++ b/source/ctr/server.h @@ -0,0 +1,267 @@ +/* +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. + +*/ +// server.h + +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; + +//============================================================================= + +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 +#ifdef QUAKE2 + char startspot[64]; +#endif + char modelname[64]; // maps/.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; + + int old_points; + int old_kills; +// joe, from ProQuake: allow clients to connect if they don't have the map + qboolean nomap; +} client_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 +#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity +#define MOVETYPE_FOLLOW 12 // track movement of aiment + +// 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 +#define SOLID_CORPSE 5 // 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 +#ifdef QUAKE2 +#define FL_FLASHLIGHT 8192 +#define FL_ARCHIVE_OVERRIDE 1048576 +#endif + +// entity effects + +#define EF_BLUELIGHT 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_REDLIGHT 8 +#define EF_ORANGELIGHT 16 +#define EF_GREENLIGHT 32 +#define EF_PINKLIGHT 64 // formerly EF_LIGHT +#define EF_NODRAW 128 +#define EF_LIMELIGHT 256 // formerly EF_BRIGHTFIELD +#define EF_FULLBRIGHT 512 +#define EF_CYANLIGHT 1024 // formerly EF_DARKLIGHT +#define EF_YELLOWLIGHT 2048 // formerly EF_DARKFIELD +#define EF_PURPLELIGHT 4096 +#define EF_RAYRED 8196 // red trail for porter x2 +#define EF_RAYGREEN 16384 // green trail for ray gun + +#define SPAWNFLAG_NOT_EASY 256 +#define SPAWNFLAG_NOT_MEDIUM 512 +#define SPAWNFLAG_NOT_HARD 1024 +#define SPAWNFLAG_NOT_DEATHMATCH 2048 + +#ifdef QUAKE2 +// server flags +#define SFL_EPISODE_1 1 +#define SFL_EPISODE_2 2 +#define SFL_EPISODE_3 4 +#define SFL_EPISODE_4 8 +#define SFL_NEW_UNIT 16 +#define SFL_NEW_EPISODE 32 +#define SFL_CROSS_TRIGGERS 65280 +#endif + +//============================================================================ + +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 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_MoveToOrigin (void); + +void SV_CheckForNewClients (void); +void SV_RunClients (void); +void SV_SaveSpawnparms (); +#ifdef QUAKE2 +void SV_SpawnServer (char *server, char *startspot); +#else +void SV_SpawnServer (char *server); +#endif diff --git a/source/ctr/snd_ctr.c b/source/ctr/snd_ctr.c new file mode 100644 index 0000000..76981d6 --- /dev/null +++ b/source/ctr/snd_ctr.c @@ -0,0 +1,97 @@ +/* +Copyright (C) 2017 Felipe Izzo +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 +#include <3ds.h> +#include "../quakedef.h" + +#define SAMPLE_RATE 22050 +#define NUM_SAMPLES 2048 +#define SAMPLE_SIZE 4 +#define BUFFER_SIZE NUM_SAMPLES*SAMPLE_SIZE + +static int sound_initialized = 0; +static byte *audio_buffer; +static ndspWaveBuf wave_buf; + +qboolean SNDDMA_Init(void) +{ + sound_initialized = 0; + + if(ndspInit() != 0) { + return false; + } + + audio_buffer = linearAlloc(BUFFER_SIZE); + + ndspSetOutputMode(NDSP_OUTPUT_STEREO); + ndspChnReset(0); + ndspChnWaveBufClear(0); + ndspChnSetInterp(0, NDSP_INTERP_LINEAR); + ndspChnSetRate(0, (float)SAMPLE_RATE); + ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); + + memset(&wave_buf, 0, sizeof(wave_buf)); + wave_buf.data_vaddr = audio_buffer; + wave_buf.nsamples = BUFFER_SIZE / 4; + wave_buf.looping = 1; + + shm = &sn; + shm->splitbuffer = 0; + shm->samplebits = 16; + shm->speed = SAMPLE_RATE; + shm->channels = 2; + shm->samples = BUFFER_SIZE / 2; + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = audio_buffer; + + ndspChnWaveBufAdd(0, &wave_buf); + + sound_initialized = 1; + + return true; +} + +int SNDDMA_GetDMAPos(void) +{ + if(!sound_initialized) + return 0; + + shm->samplepos = ndspChnGetSamplePos(0) / (shm->samplebits / 8); + return shm->samplepos; +} + +void SNDDMA_Shutdown(void) +{ + if(!sound_initialized) + return; + + ndspChnWaveBufClear(0); + ndspExit(); + linearFree(audio_buffer); + + sound_initialized = 0; +} + +void SNDDMA_Submit(void) +{ +} \ No newline at end of file diff --git a/source/ctr/strl_fn.h b/source/ctr/strl_fn.h new file mode 100644 index 0000000..30df359 --- /dev/null +++ b/source/ctr/strl_fn.h @@ -0,0 +1,10 @@ +/* header file for BSD strlcat and strlcpy */ + +#ifndef __STRLFUNCS_H +#define __STRLFUNCS_H + +/* use our own copies of strlcpy and strlcat taken from OpenBSD */ +extern size_t q_strlcpy (char *dst, const char *src, size_t size); +extern size_t q_strlcat (char *dst, const char *src, size_t size); + +#endif /* __STRLFUNCS_H */ \ No newline at end of file diff --git a/source/ctr/sys.h b/source/ctr/sys.h new file mode 100644 index 0000000..c5d61a7 --- /dev/null +++ b/source/ctr/sys.h @@ -0,0 +1,71 @@ +/* +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.h -- non-portable functions + +// +// file IO +// + +// returns the file size +// return -1 if file is not present +// the file should be in BINARY mode for stupid OSs that care +int Sys_FileOpenRead (char *path, int *hndl); + +int Sys_FileOpenWrite (char *path); +void Sys_FileClose (int handle); +void Sys_FileSeek (int handle, int position); +int Sys_FileRead (int handle, void *dest, int count); +int Sys_FileWrite (int handle, void *data, int count); +int Sys_FileTime (char *path); +void Sys_mkdir (char *path); + +// +// memory protection +// +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length); + +// +// system IO +// +void Sys_DebugLog(char *file, char *fmt, ...); + +void Sys_Error (char *error, ...); +// an error will cause the entire program to exit + +void Sys_Printf (char *fmt, ...); +// send text to the console + +void Sys_Quit (void); + +double Sys_FloatTime (void); + +char *Sys_ConsoleInput (void); + +void Sys_Sleep (void); +// called to yield for a little bit so as +// not to hog cpu when paused or debugging + +void Sys_SendKeyEvents (void); +// Perform Key_Event () callbacks until the input que is empty + +void Sys_LowFPPrecision (void); +void Sys_HighFPPrecision (void); +void Sys_SetFPCW (void); + diff --git a/source/ctr/sys_ctr.c b/source/ctr/sys_ctr.c new file mode 100644 index 0000000..82bad33 --- /dev/null +++ b/source/ctr/sys_ctr.c @@ -0,0 +1,340 @@ +/* +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 "errno.h" +#include "touch_ctr.h" + +#include <3ds.h> + +#define TICKS_PER_SEC 268123480.0 + +int __stacksize__ = 4 * 1024 * 1024; +u32 __ctru_linear_heap_size = 28 * 1024 * 1024; +bool new3ds_flag; + +extern void Touch_Init(); +extern void Touch_Update(); + +qboolean isDedicated; + +/* +=============================================================================== + +FILE IO + +=============================================================================== +*/ + +#define MAX_HANDLES 10 +FILE *sys_handles[MAX_HANDLES]; + +int findhandle (void) +{ + int i; + + for (i=1 ; i +#include "touch_ctr.h" + +u16* touchOverlay; +char lastKey = 0; +int tmode; +u16* tfb; +touchPosition oldtouch, touch; +u64 tick; + +u64 lastTap = 0; + +int shiftToggle = 0; + +void Touch_Init(){ + tmode = TMODE_TOUCHPAD; //Start in touchpad Mode + tfb = (u16*)gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL); + + //Load overlay files from sdmc for easier testing + FILE *texture = fopen("touchOverlay.bin", "rb"); + if(!texture) + Sys_Error("Could not open touchpadOverlay.bin\n"); + fseek(texture, 0, SEEK_END); + int size = ftell(texture); + fseek(texture, 0, SEEK_SET); + touchOverlay = malloc(size); + fread(touchOverlay, 1, size, texture); + fclose(texture); +} + +void Touch_DrawOverlay() +{ + int x, y; + for(x=0; x<320; x++){ + for(y=0; y<240;y++){ + tfb[(x*240 + (239 - y))] = touchOverlay[(y*320 + x)]; + } + } +} + +void Touch_Update(){ + if(lastKey){ + Key_Event(lastKey, false); + lastKey = 0; + } + + if(hidKeysDown() & KEY_TOUCH){ + hidTouchRead(&touch); + tick = Sys_FloatTime(); + } + + if(hidKeysUp() & KEY_TOUCH){ + Touch_ProcessTap(); + } +} + +void Touch_ProcessTap() +{ + if(touch.px > 268 && touch.py > 14 && touch.py < 226) + Touch_SideBarTap(); +} + +void Touch_SideBarTap() +{ + uint16_t y = (touch.py - 14)/42; + lastKey = K_AUX9 + y; + Key_Event(lastKey, true); +} \ No newline at end of file diff --git a/source/ctr/touch_ctr.h b/source/ctr/touch_ctr.h new file mode 100644 index 0000000..91b7bd0 --- /dev/null +++ b/source/ctr/touch_ctr.h @@ -0,0 +1,14 @@ +#ifndef __TOUCH__ +#define __TOUCH__ + +//Touchscreen mode identifiers +#define TMODE_TOUCHPAD 1 +#define TMODE_SETTINGS 2 + +void Touch_TouchpadTap(); +void Touch_ProcessTap(); +void Touch_DrawOverlay(); +void Touch_Init(); +void Touch_Update(); + +#endif diff --git a/source/ctr/vid.h b/source/ctr/vid.h new file mode 100644 index 0000000..1708ba8 --- /dev/null +++ b/source/ctr/vid.h @@ -0,0 +1,85 @@ +/* +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. + +*/ +// vid.h -- video driver defs + +#define VID_CBITS 6 +#define VID_GRADES (1 << VID_CBITS) + +// a pixel can be one, two, or four bytes +typedef byte pixel_t; + +typedef struct vrect_s +{ + int x,y,width,height; + struct vrect_s *pnext; +} vrect_t; + +typedef struct +{ + pixel_t *buffer; // invisible buffer + pixel_t *colormap; // 256 * VID_GRADES size + unsigned short *colormap16; // 256 * VID_GRADES size + int fullbright; // index of first fullbright color + unsigned rowbytes; // may be > width if displayed in a window + unsigned width; + unsigned height; + float aspect; // width / height -- < 0 is taller than wide + int numpages; + int recalc_refdef; // if true, recalc vid-based stuff + pixel_t *conbuffer; + int conrowbytes; + unsigned conwidth; + unsigned conheight; + int maxwarpwidth; + int maxwarpheight; + pixel_t *direct; // direct drawing to framebuffer, if not + // NULL +} viddef_t; + +extern viddef_t vid; // global video state +extern unsigned short d_8to16table[256]; +extern unsigned d_8to24table[256]; +extern void (*vid_menudrawfn)(void); +extern void (*vid_menukeyfn)(int key); + +void VID_SetPalette (unsigned char *palette); +// called at startup and after any gamma correction + +void VID_ShiftPalette (unsigned char *palette); +// called for bonus and pain flashes, and for underwater color changes + +void VID_Init (unsigned char *palette); +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again + +void VID_Shutdown (void); +// Called at shutdown + +void VID_Update (vrect_t *rects); +// flushes the given rectangles from the view buffer to the screen + +int VID_SetMode (int modenum, unsigned char *palette); +// sets the mode; only used by the Quake engine for resetting to mode 0 (the +// base mode) on memory allocation failures + +void VID_HandlePause (qboolean pause); +// called only on Win32, when pause happens, so the mouse can be released +