From 4db1c33265cdfcf56deae3a9bce5f31e6856331a Mon Sep 17 00:00:00 2001 From: Marco Cawthorne Date: Thu, 23 Jan 2025 22:15:59 -0800 Subject: [PATCH] Dump more tools in, plus add ncAnimationManager object. --- Makefile | 120 +++++ Tools/Source/cmdlib.c | 869 +++++++++++++++++++++++++++++++ Tools/Source/cmdlib.h | 97 ++++ Tools/Source/pal2colormap.c | 131 +++++ Tools/Source/pal2pal.c | 91 ++++ Tools/Source/qfiles.c | 179 +++++++ Tools/Source/tga2lmp.c | 262 ++++++++++ Tools/Source/tga2pal.c | 109 ++++ Tools/build_filesdat.sh | 39 ++ Tools/build_gfx.sh | 17 + Tools/build_maps.sh | 32 ++ Tools/build_models.sh | 21 + Tools/build_wads.sh | 27 + Tools/make_map.sh | 24 + data1/src/files.qc | 15 + data1/src/init.qc | 2 +- data1/src/progs.src | 3 +- data1/src/rules.qc | 16 + id1/gfxsrc/palette.png | Bin 0 -> 1287 bytes id1/src/defs.qh | 2 + id1/src/files.qc | 70 +++ id1/src/init.qc | 2 +- id1/src/player.qh | 146 ++++++ id1/src/progs.src | 1 + id1/src/weapons/shotgun.qc | 23 +- qw/src/files.qc | 69 +++ qw/src/progs.src | 3 +- src/entry.qc | 88 ---- src/system/headers.src | 2 + src/system/ncAnimationManager.qc | 203 ++++++++ src/system/ncAnimationManager.qh | 14 + src/system/ncEntity.qc | 50 +- src/system/ncEntity.qh | 10 +- src/system/ncFX.qc | 6 +- src/system/ncItem.qc | 8 +- src/system/ncItem.qh | 4 +- src/system/ncMath.qc | 7 +- src/system/ncMath.qh | 3 +- src/system/ncProjectile.qc | 19 + src/system/ncProjectile.qh | 6 + src/system/sources.src | 2 + 41 files changed, 2682 insertions(+), 110 deletions(-) create mode 100644 Makefile create mode 100644 Tools/Source/cmdlib.c create mode 100644 Tools/Source/cmdlib.h create mode 100644 Tools/Source/pal2colormap.c create mode 100644 Tools/Source/pal2pal.c create mode 100644 Tools/Source/qfiles.c create mode 100644 Tools/Source/tga2lmp.c create mode 100644 Tools/Source/tga2pal.c create mode 100755 Tools/build_filesdat.sh create mode 100644 Tools/build_gfx.sh create mode 100755 Tools/build_maps.sh create mode 100755 Tools/build_models.sh create mode 100755 Tools/build_wads.sh create mode 100755 Tools/make_map.sh create mode 100644 data1/src/files.qc create mode 100644 data1/src/rules.qc create mode 100644 id1/gfxsrc/palette.png create mode 100644 id1/src/files.qc create mode 100644 id1/src/player.qh create mode 100644 qw/src/files.qc create mode 100644 src/system/ncAnimationManager.qc create mode 100644 src/system/ncAnimationManager.qh create mode 100644 src/system/ncProjectile.qc create mode 100644 src/system/ncProjectile.qh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7f8c4ad --- /dev/null +++ b/Makefile @@ -0,0 +1,120 @@ +# Nuclide-Lite Makefile +# 2025-01-22 + +# id1 = Quake; data1 = Hexen 2; qw = QuakeWorld +GAME=id1 +ENGINE_CLBUILD=m-dbg +EDITOR=radiant +ENGINE_URL=https://www.github.com/fte-team/fteqw +NAME=`./Tools/get-name.sh $(GAME)` +NUCLIDE_DIR=`pwd` +QCC_DIR=$(NUCLIDE_DIR) +BUILD_DIR=$(NUCLIDE_DIR)/build + +help: + @printf "Available targets:\n" + @printf "\thelp\n" + @printf "\tgame\n" + @printf "\tfteqcc\n" + @printf "\tfteqw\n" + +all: models maps game gfx + +game: + cd $(GAME)/src/ && ../../fteqcc + +pak: game qfiles + ./Tools/build_filesdat.sh id1 + cd $(GAME)/src && ../../qfiles -pak 0 + cd $(GAME)/src && ../../qfiles -pak 1 + +run: fteqw game + ./fteqw -basedir `pwd` -netquake -game $(GAME) + +wad: imgtool palette + ./Tools/build_wads.sh $(GAME) + +maps: wad + ./Tools/build_maps.sh $(GAME) + +$(GAME)/maps/%.bsp: qbsp qlight qvis + Tools/make_map.sh $(GAME) "$@" + +models: iqmtool palette + ./Tools/build_models.sh $(GAME) + +gfx: imgtool palette + cd $(GAME)/gfxsrc/ && ls | grep -v palette\.lmp | grep \.xcf | xargs ../../imgtool --rgb8 --ext png + cd $(GAME)/gfxsrc/ && ls | grep -v palette\.png | grep \.png | xargs mogrify -type TrueColor -compress None -flip -format tga + cp $(GAME)/gfx/palette.lmp $(GAME)/gfxsrc/palette.lmp + cd $(GAME)/gfxsrc/ && ls | grep -v palette\.tga | grep \.tga | xargs ../../tga2lmp + rm $(GAME)/gfxsrc/*.tga + cp $(GAME)/gfx/palette.lmp $(GAME)/gfxsrc/palette.lmp + rsync -va $(GAME)/gfxsrc/*.lmp $(GAME)/gfx/ + rm $(GAME)/gfxsrc/*.lmp + +# various palette utilities needed +tga2pal: + gcc -o tga2pal Tools/Source/tga2pal.c + +tga2lmp: + gcc -o tga2lmp Tools/Source/tga2lmp.c + +pal2pal: + gcc -o pal2pal Tools/Source/pal2pal.c + +pal2colormap: + gcc -o pal2colormap Tools/Source/pal2colormap.c + +qfiles: + gcc -o qfiles Tools/Source/qfiles.c Tools/Source/cmdlib.c + +palette: pal2colormap pal2pal tga2pal + if [ ! -d $(GAME)/gfx ];then mkdir $(GAME)/gfx; fi + mogrify -type TrueColor -compress None -flip -format tga $(GAME)/gfxsrc/palette.png + cd $(GAME)/gfxsrc/ && ../../tga2pal palette.tga + cd $(GAME)/gfxsrc/ && ../../pal2pal palette.lmp + cd $(GAME)/gfxsrc/ && ../../pal2colormap palette.lmp + mv $(GAME)/gfxsrc/palette.lmp $(GAME)/gfx/palette.lmp + mv $(GAME)/gfxsrc/colormap.lmp $(GAME)/gfx/colormap.lmp + mv $(GAME)/gfxsrc/palette.pal $(GAME)/gfx/palette.pal + -mkdir -p $(GAME)/gfxsrc + -mkdir -p $(GAME)/textures # needed by gtkradiant + cp $(GAME)/gfx/palette.lmp $(GAME)/textures/palette.lmp + rm $(GAME)/gfxsrc/palette.tga + +# fteqw repo +update: + if [ ! -d ThirdParty/fteqw ];then mkdir ThirdParty && git clone $(ENGINE_URL) ThirdParty/fteqw;else cd ./ThirdParty/fteqw && git pull;fi + if [ ! -d ThirdParty/gtkradiant ];then git clone https://github.com/TTimo/gtkradiant ThirdParty/gtkradiant;else cd ./ThirdParty/gtkradiant && git pull;fi + +fteqcc: update + cd ThirdParty/fteqw/engine && $(MAKE) qcc-rel + install -m 0777 ./ThirdParty/fteqw/engine/release/fteqcc ./ + +fteqw: update + cd ThirdParty/fteqw/engine && $(MAKE) makelibs ARCH=x86_64 + cd ThirdParty/fteqw/engine && $(MAKE) $(ENGINE_CLBUILD) ARCH=x86_64 + install -m 0777 ./ThirdParty/fteqw/engine/debug/fteqw ./ + +imgtool: update + cd ThirdParty/fteqw/engine && $(MAKE) imgtool ARCH=x86_64 + install -m 0777 ./ThirdParty/fteqw/engine/release/imgtool ./ + +iqmtool: update + cd ThirdParty/fteqw/engine && $(MAKE) iqmtool ARCH=x86_64 + install -m 0777 ./ThirdParty/fteqw/engine/release/iqmtool ./ + +qbsp: + printf "qbsp build\n" + +qvis: + printf "qvis build\n" + +qlight: + printf "qlight build\n" + +radiant: update + if [ ! -d ThirdParty/gtkradiant ];then git clone https://github.com/TTimo/gtkradiant ThirdParty/gtkradiant;fi + cd ThirdParty/gtkradiant && scons + Tools/make_launcher.sh ./ThirdParty/gtkradiant/install/ ./radiant.bin radiant diff --git a/Tools/Source/cmdlib.c b/Tools/Source/cmdlib.c new file mode 100644 index 0000000..0dc9242 --- /dev/null +++ b/Tools/Source/cmdlib.c @@ -0,0 +1,869 @@ +// cmdlib.c + +#include "cmdlib.h" +#include +#include + +#ifdef WIN32 +#include +#else +#include +#endif + +#ifdef NeXT +#include +#endif + +#define PATHSEPERATOR '/' + +// set these before calling CheckParm +int myargc; +char **myargv; + +char com_token[1024]; +qboolean com_eof; + +qboolean archive; +char archivedir[1024]; + + +/* +================= +Error + +For abnormal program terminations +================= +*/ +void Error (char *error, ...) +{ + va_list argptr; + + printf ("************ ERROR ************\n"); + + va_start (argptr,error); + vprintf (error,argptr); + va_end (argptr); + printf ("\n"); + exit (1); +} + + +/* + +qdir will hold the path up to the quake directory, including the slash + + f:\quake\ + /raid/quake/ + +gamedir will hold qdir + the game directory (id1, id2, etc) + + */ + +char qdir[1024]; +char gamedir[1024]; + +void SetQdirFromPath (char *path) +{ + char temp[1024]; + char *c; + + if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) + { // path is partial + Q_getwd (temp); + strcat (temp, path); + path = temp; + } + + // search for "quake" in path + + for (c=path ; *c ; c++) + if (!Q_strncasecmp (c, "Nuclide-Lite", 12)) + { + strncpy (qdir, path, c+13-path); + printf ("qdir: %s\n", qdir); + c += 13; + while (*c) + { + if (*c == '/' || *c == '\\') + { + strncpy (gamedir, path, c+1-path); + printf ("gamedir: %s\n", gamedir); + return; + } + c++; + } + Error ("No gamedir in %s", path); + return; + } + Error ("SeetQdirFromPath: no 'Nuclide' in %s", path); +} + +char *ExpandPath (char *path) +{ + static char full[1024]; + if (!qdir) + Error ("ExpandPath called without qdir set"); + if (path[0] == '/' || path[0] == '\\' || path[1] == ':') + return path; + sprintf (full, "%s%s", qdir, path); + return full; +} + +char *ExpandPathAndArchive (char *path) +{ + char *expanded; + char archivename[1024]; + + expanded = ExpandPath (path); + + if (archive) + { + sprintf (archivename, "%s/%s", archivedir, path); + CopyFile (expanded, archivename); + } + return expanded; +} + + +char *copystring(char *s) +{ + char *b; + b = malloc(strlen(s)+1); + strcpy (b, s); + return b; +} + + + +/* +================ +I_FloatTime +================ +*/ +double I_FloatTime (void) +{ + time_t t; + + time (&t); + + return t; +#if 0 +// more precise, less portable + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +#endif +} + +void Q_getwd (char *out) +{ +#ifdef WIN32 + _getcwd (out, 256); + strcat (out, "\\"); +#else + getcwd (out, 256); +#endif +} + + +void Q_mkdir (char *path) +{ +#ifdef WIN32 + if (_mkdir (path) != -1) + return; +#else + if (mkdir (path, 0777) != -1) + return; +#endif + if (errno != EEXIST) + Error ("mkdir %s: %s",path, strerror(errno)); +} + +/* +============ +FileTime + +returns -1 if not present +============ +*/ +int FileTime (char *path) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return -1; + + return buf.st_mtime; +} + + + +/* +============== +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) + { + com_eof = true; + 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++; + do + { + c = *data++; + if (c=='\"') + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } while (1); + } + +// 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; +} + + +int Q_strncasecmp (char *s1, char *s2, int n) +{ + int c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= '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 + } + + return -1; +} + +int Q_strcasecmp (char *s1, char *s2) +{ + return Q_strncasecmp (s1, s2, 99999); +} + + +char *strupr (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = toupper(*in); + in++; + } + return start; +} + +char *strlower (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = tolower(*in); + in++; + } + return start; +} + + +/* +============================================================================= + + MISC FUNCTIONS + +============================================================================= +*/ + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int CheckParm (char *check) +{ + int i; + + for (i = 1;i 0 && path[length] != PATHSEPERATOR) + length--; + path[length] = 0; +} + +void StripExtension (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != '.') + { + length--; + if (path[length] == '/') + return; // no extension + } + if (length) + path[length] = 0; +} + + +/* +==================== +Extract file parts +==================== +*/ +void ExtractFilePath (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != PATHSEPERATOR) + src--; + + memcpy (dest, path, src-path); + dest[src-path] = 0; +} + +void ExtractFileBase (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != PATHSEPERATOR) + src--; + + while (*src && *src != '.') + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileExtension (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && *(src-1) != '.') + src--; + if (src == path) + { + *dest = 0; // no extension + return; + } + + strcpy (dest,src); +} + + +/* +============== +ParseNum / ParseHex +============== +*/ +int ParseHex (char *hex) +{ + char *str; + int num; + + num = 0; + str = hex; + + while (*str) + { + num <<= 4; + if (*str >= '0' && *str <= '9') + num += *str-'0'; + else if (*str >= 'a' && *str <= 'f') + num += 10 + *str-'a'; + else if (*str >= 'A' && *str <= 'F') + num += 10 + *str-'A'; + else + Error ("Bad hex number: %s",hex); + str++; + } + + return num; +} + + +int ParseNum (char *str) +{ + if (str[0] == '$') + return ParseHex (str+1); + if (str[0] == '0' && str[1] == 'x') + return ParseHex (str+2); + return atol (str); +} + + + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +#ifdef _SGI_SOURCE +#define __BIG_ENDIAN__ +#endif + +#ifdef __BIG_ENDIAN__ + +short LittleShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short BigShort (short l) +{ + return l; +} + + +int LittleLong (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; +} + +int BigLong (int l) +{ + return l; +} + + +float LittleFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float BigFloat (float l) +{ + return l; +} + + +#else + + +short BigShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short LittleShort (short l) +{ + return l; +} + + +int BigLong (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; +} + +int LittleLong (int l) +{ + return l; +} + +float BigFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float LittleFloat (float l) +{ + return l; +} + + +#endif + + +//======================================================= + + +// FIXME: byte swap? + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + +#define CRC_INIT_VALUE 0xffff +#define CRC_XOR_VALUE 0x0000 + +static unsigned short crctable[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +void CRC_Init(unsigned short *crcvalue) +{ + *crcvalue = CRC_INIT_VALUE; +} + +void CRC_ProcessByte(unsigned short *crcvalue, byte data) +{ + *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; +} + +unsigned short CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} +//============================================================================= + +/* +============ +CreatePath +============ +*/ +void CreatePath (char *path) +{ + char *ofs, c; + + for (ofs = path+1 ; *ofs ; ofs++) + { + c = *ofs; + if (c == '/' || c == '\\') + { // create the directory + *ofs = 0; + Q_mkdir (path); + *ofs = c; + } + } +} + + +/* +============ +CopyFile + + Used to archive source files +============ +*/ +void CopyFile (char *from, char *to) +{ + void *buffer; + int length; + + length = LoadFile (from, &buffer); + CreatePath (to); + SaveFile (to, buffer, length); + free (buffer); +} diff --git a/Tools/Source/cmdlib.h b/Tools/Source/cmdlib.h new file mode 100644 index 0000000..b556288 --- /dev/null +++ b/Tools/Source/cmdlib.h @@ -0,0 +1,97 @@ +// cmdlib.h + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +typedef enum {false, true} qboolean; +typedef unsigned char byte; +#endif + +// the dec offsetof macro doesn't work very well... +#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) + + +// set these before calling CheckParm +extern int myargc; +extern char **myargv; + +char *strupr (char *in); +char *strlower (char *in); +int Q_strncasecmp (char *s1, char *s2, int n); +int Q_strcasecmp (char *s1, char *s2); +void Q_getwd (char *out); + +int filelength (FILE *f); +int FileTime (char *path); + +void Q_mkdir (char *path); + +extern char qdir[1024]; +extern char gamedir[1024]; +void SetQdirFromPath (char *path); +char *ExpandPath (char *path); +char *ExpandPathAndArchive (char *path); + + +double I_FloatTime (void); + +void Error (char *error, ...); +int CheckParm (char *check); + +FILE *SafeOpenWrite (char *filename); +FILE *SafeOpenRead (char *filename); +void SafeRead (FILE *f, void *buffer, int count); +void SafeWrite (FILE *f, void *buffer, int count); + +int LoadFile (char *filename, void **bufferptr); +void SaveFile (char *filename, void *buffer, int count); + +void DefaultExtension (char *path, char *extension); +void DefaultPath (char *path, char *basepath); +void StripFilename (char *path); +void StripExtension (char *path); + +void ExtractFilePath (char *path, char *dest); +void ExtractFileBase (char *path, char *dest); +void ExtractFileExtension (char *path, char *dest); + +int ParseNum (char *str); + +short BigShort (short l); +short LittleShort (short l); +int BigLong (int l); +int LittleLong (int l); +float BigFloat (float l); +float LittleFloat (float l); + + +char *COM_Parse (char *data); + +extern char com_token[1024]; +extern qboolean com_eof; + +char *copystring(char *s); + + +void CRC_Init(unsigned short *crcvalue); +void CRC_ProcessByte(unsigned short *crcvalue, byte data); +unsigned short CRC_Value(unsigned short crcvalue); + +void CreatePath (char *path); +void CopyFile (char *from, char *to); + +extern qboolean archive; +extern char archivedir[1024]; + + +#endif diff --git a/Tools/Source/pal2colormap.c b/Tools/Source/pal2colormap.c new file mode 100644 index 0000000..41219fa --- /dev/null +++ b/Tools/Source/pal2colormap.c @@ -0,0 +1,131 @@ +/* +PAL 2 COLORMAP SOURCECODE + +The MIT License (MIT) + +Copyright (c) 2016-2019 Marco "eukara" Hladik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include +#include + +#define TGAHEADER 18 +#define QPALSIZE 768 +#define QCOLORSIZE 16384 + +/* sample a 24-bit RGB value to one of the colours on the existing 8-bit palette */ +unsigned char +convert_24_to_8 (const unsigned char palette[768], const int rgb[3]) +{ + int i, j; + int best_index = -1; + int best_dist = 0; + + for (i = 0; i < 256; i++) { + int dist = 0; + + for (j = 0; j < 3; j++) { + /* note that we could use RGB luminosity bias for greater accuracy, but quake's colormap apparently didn't do this */ + int d = abs(rgb[j] - palette[i * 3 + j]); + dist += d * d; + } + + if (best_index == -1 || dist < best_dist) { + best_index = i; + best_dist = dist; + } + } + + return (unsigned char) best_index; +} + +void +process_pal2colormap(char *filename) +{ + FILE *fLMP; + unsigned char pal_buff[QPALSIZE]; + FILE *fTGA; + unsigned char colormap_buff[QCOLORSIZE]; + short pal_loop, tga_loop; + struct stat pal_st; + int num_fullbrights = 0; /* the last 32 colours will be full bright */ + int x, y, i; + + fLMP = fopen(filename, "rb"); + + if (!fLMP) { + fprintf(stderr, "couldn't find %s\n", filename); + return; + } + + /* There are no other types of palette lumps. Sorry */ + stat(filename, &pal_st); + if (pal_st.st_size != QPALSIZE) { + fprintf(stderr, "invalid palette lump %s, skipping\n", filename); + return; + } + + fread(pal_buff, 1, QPALSIZE, fLMP); + fclose(fLMP); + + for ( x = 0; x < 256; x++ ) { + for ( y = 0; y < 64; y++ ) { + if ( x < 256 - num_fullbrights ) { + int rgb[3]; + + for ( i = 0; i < 3; i++ ) { + /* divide by 32, rounding to nearest integer */ + rgb[i] = ( pal_buff[x * 3 + i] * ( 63 - y ) + 16 ) >> 5; + if ( rgb[i] > 255 ) + rgb[i] = 255; + } + + colormap_buff[y * 256 + x] = convert_24_to_8( pal_buff, rgb ); + } else { + /* this colour is a fullbright, just keep the original colour */ + colormap_buff[y * 256 + x] = x; + } + } + } + + + fTGA = fopen("colormap.lmp", "w+b"); + fwrite(colormap_buff, 1, QCOLORSIZE, fTGA); + fclose(fTGA); +} + +int +main(int argc, char *argv[]) +{ + int c; + + if (argc <= 1) { + fprintf(stderr, "usage: pal2colormap [file.lmp ...]\n"); + return 1; + } + + for (c = 1; c < argc; c++) + process_pal2colormap(argv[c]); + + return 0; +} diff --git a/Tools/Source/pal2pal.c b/Tools/Source/pal2pal.c new file mode 100644 index 0000000..4aef6a3 --- /dev/null +++ b/Tools/Source/pal2pal.c @@ -0,0 +1,91 @@ +/* +PAL 2 TGA SOURCECODE + +The MIT License (MIT) + +Copyright (c) 2016-2019 Marco "eukara" Hladik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include + +#define TGAHEADER 18 +#define QPALSIZE 768 +#define T2PVERSION "1.1" + +void process_pal2tga(char *filename) +{ + FILE *fLMP; + unsigned char pal_buff[QPALSIZE]; + FILE *fTGA; + unsigned char tga_buff[QPALSIZE + TGAHEADER]; + short pal_loop, tga_loop; + struct stat pal_st; + + fLMP = fopen(filename, "rb"); + + if (!fLMP) { + fprintf(stderr, "couldn't find %s\n", filename); + return; + } + + /* There are no other types of palette lumps. Sorry */ + stat(filename, &pal_st); + if (pal_st.st_size != QPALSIZE) { + fprintf(stderr, "invalid palette lump %s, skipping\n", filename); + return; + } + + fread(pal_buff, 1, QPALSIZE, fLMP); + fclose(fLMP); + + /* FIXME: We assume too much! + * Save the output to FILENAME.tga + * This is ugly when the input name has no .lmp extension. + * But who's going to try that. ...right? */ + filename[strlen(filename)-3] = 'p'; + filename[strlen(filename)-2] = 'a'; + filename[strlen(filename)-1] = 'l'; + fprintf(stdout, "writing %s\n", filename); + fTGA = fopen(filename, "w"); + fprintf(fTGA, "JASC-PAL\r\n"); + fprintf(fTGA, "0100\r\n"); + fprintf(fTGA, "256\r\n"); + for (int i = 0; i < QPALSIZE; i+=3) + fprintf(fTGA, "%d %d %d\r\n", pal_buff[i], pal_buff[i+1], pal_buff[i+2]); + fclose(fTGA); +} + +int main(int argc, char *argv[]) +{ + int c; + + if (argc <= 1) { + fprintf(stderr, "usage: pal2tga [file.lmp ...]\n"); + return 1; + } + + for (c = 1; c < argc; c++) + process_pal2tga(argv[c]); + + return 0; +} diff --git a/Tools/Source/qfiles.c b/Tools/Source/qfiles.c new file mode 100644 index 0000000..4c775e2 --- /dev/null +++ b/Tools/Source/qfiles.c @@ -0,0 +1,179 @@ + +#include "cmdlib.h" + +#define MAX_FILES 4096 + +#define MAX_DATA_PATH 512 + +char precache_files[MAX_FILES][MAX_DATA_PATH]; +int precache_files_block[MAX_FILES]; +int numfiles; + + +typedef struct +{ + char name[56]; + int filepos, filelen; +} packfile_t; + +typedef struct +{ + char id[4]; + int dirofs; + int dirlen; +} packheader_t; + +packfile_t pfiles[4096], *pf; +FILE *packhandle; +int packbytes; + + +/* +=========== +PackFile + +Copy a file into the pak file +=========== +*/ +void PackFile (char *src, char *name) +{ + FILE *in; + int remaining, count; + char buf[4096]; + + if ( (byte *)pf - (byte *)pfiles > sizeof(pfiles) ) + Error ("Too many files in pak file"); + + in = SafeOpenRead (src); + remaining = filelength (in); + + pf->filepos = LittleLong (ftell (packhandle)); + pf->filelen = LittleLong (remaining); + strcpy (pf->name, name); + printf ("%64s : %7i\n", pf->name, remaining); + + packbytes += remaining; + + while (remaining) + { + if (remaining < sizeof(buf)) + count = remaining; + else + count = sizeof(buf); + SafeRead (in, buf, count); + SafeWrite (packhandle, buf, count); + remaining -= count; + } + + fclose (in); + pf++; +} + + +/* +=========== +CopyQFiles +=========== +*/ +void CopyQFiles (int blocknum) +{ + int i, p; + char srcfile[1024]; + char destfile[1024]; + char name[1024]; + packheader_t header; + int dirlen; + unsigned short crc; + + // create a pak file + pf = pfiles; + + sprintf (destfile, "%spak%i.pak", gamedir, blocknum); + packhandle = SafeOpenWrite (destfile); + SafeWrite (packhandle, &header, sizeof(header)); + + blocknum++; + + for (i=0 ; i : build a .pak file\n"); + exit (1); + } + + SetQdirFromPath (""); + + ReadFiles (); + + if (!strcmp (argv[1], "-pak")) + { + CopyQFiles (atoi(argv[2])); + } + else + Error ("unknown command: %s", argv[1]); + + return 0; +} + diff --git a/Tools/Source/tga2lmp.c b/Tools/Source/tga2lmp.c new file mode 100644 index 0000000..b4c2d8e --- /dev/null +++ b/Tools/Source/tga2lmp.c @@ -0,0 +1,262 @@ +/* +TGA 2 LMP SOURCECODE + +The MIT License (MIT) + +Copyright (c) 2016-2019 Marco "eukara" Hladik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include +#include + +#define TGAHEADER 18 +#define QPALSIZE 768 +#define VERSION "1.1" + +typedef unsigned char byte; + +typedef union { + int rgba:32; + struct { + byte b:8; + byte g:8; + byte r:8; + byte a:8; + } u; +} pixel_t; + +byte cust_pal[QPALSIZE]; /* custom 256 color palette */ + +/* fallback palette from Quake, put into the Public Domain by John Carmack */ +pixel_t pal_fallb[256] = { + 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, + 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbb,0xcbcbcb,0xdbdbdb,0xebebeb, + 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, + 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, + 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, + 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383b,0x8b8bcb, + 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, + 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, + 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, + 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, + 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, + 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, + 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, + 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, + 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, + 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, + 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, + 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, + 0xb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, + 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, + 0xdbc3b,0xcb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, + 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, + 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, + 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, + 0xfff31b,0xefdf17,0xdbcb13,0xcb70f,0xba70f,0xab970b,0x9b8307,0x8b7307, + 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, + 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, + 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, + 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, + 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, + 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, + 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53 +}; + +byte c_last; /* last palette index we chose */ +pixel_t last_px; /* last RGB value we chose */ + +/* quickly translate 24 bit RGB value to our palette */ +byte pal24to8(byte r, byte g, byte b) +{ + pixel_t px; + byte c_red, c_green, c_blue, c_best, l; + int dist, best; + + /* compare the last with the current pixel color for speed */ + if ((last_px.u.r == r) && (last_px.u.g == g) && (last_px.u.b == b)) { + return c_last; + } + + px.u.r = last_px.u.r = r; + px.u.g = last_px.u.g = g; + px.u.b = last_px.u.b = b; + + best = 255 + 255 + 255; + c_last = c_best = c_red = c_green = c_blue = 255; + l = 0; + + while (1) { + if ((cust_pal[l * 3 + 0] == r) && (cust_pal[l * 3 + 0] == g) + && (cust_pal[l * 3 + 0] == b)) + { + last_px.u.r = cust_pal[l * 3 + 0]; + last_px.u.g = cust_pal[l * 3 + 1]; + last_px.u.b = cust_pal[l * 3 + 2]; + c_last = l; + return l; + } + + c_red = abs(cust_pal[l * 3 + 0] - px.u.r); + c_green = abs(cust_pal[l * 3 + 1] - px.u.g); + c_blue = abs(cust_pal[l * 3 + 2] - px.u.b); + dist = (c_red + c_green + c_blue); + + /* is it better than the last? */ + if (dist < best) { + best = dist; + c_best = l; + } + + if (l != 255) { + l++; + } else { + break; + } + } + + c_last = c_best; + return c_best; +} + +void process_tga2lmp(char *filename) +{ + FILE *fLMP; /* file Ident of the LUMP */ + byte *lmp_buff; /* buffer of the LUMP */ + FILE *fTGA; /* file Ident of the TARGA */ + byte tga_header[TGAHEADER]; /* TARGA Header (18 bytes usually) */ + byte *tga_buff; /* 24bit gR input buffer + TGA header */ + int col, row, done; /* used for the sorting loop */ + int img_w, img_h; /* dimensions */ + + /* load the TARGA */ + fTGA = fopen(filename, "rb"); + + /* check whether the file exists or not */ + if (!fTGA) { + fprintf(stderr, "couldn't find %s\n", filename); + return; + } + + /* put the TARGA header into the buffer for validation */ + fread(tga_header, 1, TGAHEADER, fTGA); + + /* only allow uncompressed, 24bit TARGAs */ + if (tga_header[2] != 2) { + fprintf(stderr, "%s should be an uncompressed, RGB image\n", filename); + return; + } + if (tga_header[16] != 24) { + fprintf(stderr, "%s is not 24 bit in depth\n", filename); + return; + } + + /* read the resolution into an int (TGA uses shorts for the dimensions) */ + img_w = (tga_header[12]) | (tga_header[13] << 8); + img_h = (tga_header[14]) | (tga_header[15] << 8); + tga_buff = malloc(img_w * img_h * 3); + + if (tga_buff == NULL) { + fprintf(stderr, "mem alloc failed at %d bytes\n", (img_w * img_h * 3)); + return; + } + + /* skip to after the TARGA HEADER... and then read the buffer */ + fseek(fTGA, TGAHEADER, SEEK_SET); + fread(tga_buff, 1, img_w * img_h * 3, fTGA); + fclose(fTGA); + + /* start generating the lump data */ + lmp_buff = malloc(img_w * img_h + 8); + + if (lmp_buff == NULL) { + fprintf(stderr, "mem alloc failed at %d bytes\n", (img_w * img_h + 8)); + return; + } + + /* split the integer dimensions into 4 bytes */ + lmp_buff[3] = (img_w >> 24) & 0xFF; + lmp_buff[2] = (img_w >> 16) & 0xFF; + lmp_buff[1] = (img_w >> 8) & 0xFF; + lmp_buff[0] = img_w & 0xFF; + lmp_buff[7] = (img_h >> 24) & 0xFF; + lmp_buff[6] = (img_h >> 16) & 0xFF; + lmp_buff[5] = (img_h >> 8) & 0xFF; + lmp_buff[4] = img_h & 0xFF; + + /* translate the rgb values into indexed entries and flip */ + done = 0; + for (row = img_h - 1; row >= 0; row--) { + for (col = 0; col < img_w; col++) { + lmp_buff[8 + done] = + pal24to8(tga_buff[((row * (img_w * 3)) + (col * 3 + 2))], + tga_buff[((row * (img_w * 3)) + (col * 3 + 1))], + tga_buff[((row * (img_w * 3)) + (col * 3 + 0))]); + done++; + } + } + + /* FIXME: We assume too much! + * Save the output to FILENAME.tga + * This is ugly when the input name has no .lmp extension. + * But who's going to try that. ...right? */ + filename[strlen(filename)-3] = 'l'; + filename[strlen(filename)-2] = 'm'; + filename[strlen(filename)-1] = 'p'; + fprintf(stdout, "writing %s\n", filename); + fLMP = fopen(filename, "w+b"); + fwrite(lmp_buff, 1, (img_w * img_h) + 8, fLMP); + fclose(fLMP); +} + +int main(int argc, char *argv[]) +{ + int c; + short p; + FILE *fPAL; + + if (argc <= 1) { + fprintf(stderr, "usage: tga2lmp [file.tga ...]\n"); + return 1; + } + + fPAL = fopen("palette.lmp", "rb"); + + if (!fPAL) { + fprintf(stdout, "no palette.lmp found, using builtin palette.\n"); + for (p = 0; p < 256; p++) { + cust_pal[p * 3 + 0] = pal_fallb[p].u.r; + cust_pal[p * 3 + 1] = pal_fallb[p].u.g; + cust_pal[p * 3 + 2] = pal_fallb[p].u.b; + } + } else { + fprintf(stdout, "custom palette.lmp found\n"); + fread(cust_pal, 1, QPALSIZE, fPAL); + fclose(fPAL); + } + + for (c = 1; c < argc; c++) + process_tga2lmp(argv[c]); + + return 0; +} diff --git a/Tools/Source/tga2pal.c b/Tools/Source/tga2pal.c new file mode 100644 index 0000000..362be23 --- /dev/null +++ b/Tools/Source/tga2pal.c @@ -0,0 +1,109 @@ +/* +TGA 2 PAL SOURCECODE + +The MIT License (MIT) + +Copyright (c) 2016-2019 Marco "eukara" Hladik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include + +#define TGAHEADER 18 +#define QPALSIZE 768 + +void process_tga2pal(char *filename) +{ + FILE *fTGA; + unsigned char tga_header[TGAHEADER]; + unsigned char tga_buff[QPALSIZE]; + FILE *fLMP; + unsigned char pal_buff[QPALSIZE]; + short loop_pal, loop_tga; + + fTGA = fopen(filename, "rb"); + if (!fTGA) { + fprintf(stderr, "couldn't find %s\n", filename); + return; + } + + /* Put the TARGA header into the buffer for validation */ + fread(tga_header, 1, TGAHEADER, fTGA); + + /* only allow uncompressed, 24bit TARGAs */ + if (tga_header[2] != 2) { + fprintf(stderr, "%s should be an uncompressed, RGB image\n", filename); + return; + } + if (tga_header[16] != 24) { + fprintf(stderr, "%s is not 24 bit in depth\n", filename); + return; + } + if (tga_header[12] != 16 || tga_header[14] != 16) { + fprintf(stderr, "%s is not a 16x16 image\n", filename); + return; + } + + /* Skip to after the TARGA HEADER... and then read the buffer */ + fseek(fTGA, 18, SEEK_SET); + fread(tga_buff, 1, QPALSIZE, fTGA); + fclose(fTGA); + + /* TARGAs are flipped in an odd way, + * so we gotta do the sorting dance */ + for(loop_tga = 15; loop_tga >= 0; loop_tga--) { + for(loop_pal = 0; loop_pal < 16; loop_pal++) { + pal_buff[((15 - loop_tga) * 48) + (loop_pal * 3) + 0] = + tga_buff[(loop_tga * 48) + (loop_pal * 3) + 2]; + pal_buff[((15 - loop_tga) * 48) + (loop_pal * 3) + 1] = + tga_buff[(loop_tga * 48) + (loop_pal * 3) + 1]; + pal_buff[((15 - loop_tga) * 48) + (loop_pal * 3) + 2] = + tga_buff[(loop_tga * 48) + (loop_pal * 3) + 0]; + } + } + + /* FIXME: We assume too much! + * Save the output to FILENAME.tga + * This is ugly when the input name has no .lmp extension. + * But who's going to try that. ...right? */ + filename[strlen(filename)-3] = 'l'; + filename[strlen(filename)-2] = 'm'; + filename[strlen(filename)-1] = 'p'; + fprintf(stdout, "writing %s\n", filename); + fLMP = fopen(filename, "w+b"); + fwrite(pal_buff, 1, QPALSIZE, fLMP); + fclose(fLMP); +} + +int main(int argc, char *argv[]) +{ + int c; + + if (argc <= 1) { + fprintf(stderr, "usage: tga2pal [file.tga ...]\n"); + return 1; + } + + for (c = 1; c < argc; c++) + process_tga2pal(argv[c]); + + return 0; +} diff --git a/Tools/build_filesdat.sh b/Tools/build_filesdat.sh new file mode 100755 index 0000000..9b9b9fb --- /dev/null +++ b/Tools/build_filesdat.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +# ensure we use the tools provided in ./bin +export PATH="$(pwd)/Tools:$PATH" + +CWD=$(pwd) + +# build src/files.dat +dump_filelist() +{ + + FILE_F="$CWD/$1/src/files_f.dat" + FILE="$CWD/$1/src/files.dat" + rm -v "$FILE" + + cd "$CWD/$1/src/" + grep -r Precache_Sound\(\ \" | cut -d '"' -f 2 | awk '{ print "sound/"$1; }' > "$FILE_F" + grep -r Precache_Model\(\ \" | cut -d '"' -f 2 >> "$FILE_F" + grep -r Precache_File\(\ \" | cut -d '"' -f 2 >> "$FILE_F" + + # print the counter for sounds + FIL_COUNT=$(cat "$FILE_F" | wc -l) + + if [ -z "$FIL_COUNT" ] + then + FIL_COUNT="0" + fi + + # misc files + echo "$FIL_COUNT" >> "$FILE" + cat "$FILE_F" | while read LINE + do + echo "1 $LINE" >> "$FILE" + done + + rm -v "$FILE_F" +} + +dump_filelist $1 diff --git a/Tools/build_gfx.sh b/Tools/build_gfx.sh new file mode 100644 index 0000000..58f4858 --- /dev/null +++ b/Tools/build_gfx.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# ensure we use the tools provided in ./bin +export PATH="$(pwd)/Tools:$PATH" + +CWD=$(pwd) + +# gfx lumps (built first) +build_gfx() +{ + echo "building gfx/*.lmp" + + cd "$CWD/$1/gfxsrc/" + ls | grep -v palette | grep "\.tga" | xargs -n 1 tga2lmp +} + +build_gfx $1 diff --git a/Tools/build_maps.sh b/Tools/build_maps.sh new file mode 100755 index 0000000..36f4be9 --- /dev/null +++ b/Tools/build_maps.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +if [ $# -lt 1 ] +then + exit 1 +fi + +GAMEDIR="$1" +CWD=$(pwd) + +if [ -d "$CWD/$GAMEDIR/mapsrc/" ] +then + OLDDIR="$(pwd)" + + find "$GAMEDIR/mapsrc/" -name "*.map" | while read QEDMAP + do + cd "$(dirname "$QEDMAP")" + MAPFILE="$(basename "$QEDMAP")" + PATH="$PATH:$OLDDIR" qbsp -wadpath "$CWD/$GAMEDIR" "$MAPFILE" + PATH="$PATH:$OLDDIR" qvis "$MAPFILE" + PATH="$PATH:$OLDDIR" qlight -extra4 "$MAPFILE" + cd "$OLDDIR" + done + + find "$GAMEDIR/mapsrc/" -name "*.bsp" | while read MAPFILE + do + NEWPATH="$(echo "$MAPFILE" | sed 's/\/mapsrc\//\/maps\//g')" + NEWPATHDIR=$(dirname "$NEWPATH") + mkdir -p "$NEWPATHDIR" + mv -v "$MAPFILE" "$NEWPATH" + done +fi diff --git a/Tools/build_models.sh b/Tools/build_models.sh new file mode 100755 index 0000000..c881d92 --- /dev/null +++ b/Tools/build_models.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# ensure we use the tools provided in ./bin +export PATH="$(pwd)/Tools:$PATH" + +CWD=$(pwd) + +build_models() +{ + cd "$CWD/$1/modelsrc/" + mkdir -p "$CWD/$1/progs/" + + find "$CWD/$1/modelsrc/" -name "*.qc" | while read LINE + do + DIR=$(dirname "$LINE") + cd "$DIR" + iqmtool $(basename "$LINE") + done +} + +build_models $1 diff --git a/Tools/build_wads.sh b/Tools/build_wads.sh new file mode 100755 index 0000000..6a153df --- /dev/null +++ b/Tools/build_wads.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# ensure we use the tools provided in root +export PATH="$(pwd):$PATH" +CWD=$(pwd) + +# texture wads +build_wads() +{ + echo "building texturesrc/*.wad" + + cd "$CWD/$1/texturesrc/" + + # will make a .wad out of every sub directory. + find ./ -type d -maxdepth 1 | while read WADDIR + do + DIRNAME=$(basename "$WADDIR") + + if [ ! "$DIRNAME" = "." ] + then + echo "$DIRNAME" + imgtool --palette ../gfx/palette.lmp --genwad2 ../"$DIRNAME".wad ./"$DIRNAME" + fi + done +} + +build_wads $1 diff --git a/Tools/make_map.sh b/Tools/make_map.sh new file mode 100755 index 0000000..4a27c3d --- /dev/null +++ b/Tools/make_map.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +if [ $# -lt 1 ] +then + exit 1 +fi + +CWD="$(pwd)" +GAMEDIR="$1" +TARGET="$2" +MAPFILE=$(echo "$TARGET" | sed 's/\.bsp/\.map/g' | sed 's/\/maps\//\/mapsrc\//g') +MAPOUT=$(echo "$TARGET" | sed 's/\/maps\//\/mapsrc\//g') +OLDDIR="$(pwd)" + +if [ -d "$CWD/$GAMEDIR/mapsrc/" ] +then + cd "$(dirname "$MAPFILE")" + MAPFILE="$(basename "$MAPFILE")" + PATH="$PATH:$OLDDIR" qbsp -wadpath "$CWD/$GAMEDIR" "$MAPFILE" + PATH="$PATH:$OLDDIR" qvis "$MAPFILE" + PATH="$PATH:$OLDDIR" qlight -extra4 "$MAPFILE" + cd "$OLDDIR" + mv -v "$MAPOUT" "$TARGET" +fi diff --git a/data1/src/files.qc b/data1/src/files.qc new file mode 100644 index 0000000..ff5bcba --- /dev/null +++ b/data1/src/files.qc @@ -0,0 +1,15 @@ +void main( void ) { + /* all the files that Hexen II needs to boot. */ + ncEngine::Precache_File( "maps/demo1.bsp" ); + ncEngine::Precache_File( "raven/menu1.wav" ); + ncEngine::Precache_File( "raven/menu2.wav" ); + ncEngine::Precache_File( "raven/menu3.wav" ); + ncEngine::Precache_File( "misc/barmovup.wav" ); + ncEngine::Precache_File( "misc/barmovdn.wav" ); + ncEngine::Precache_File( "misc/invmove.wav" ); + ncEngine::Precache_File( "misc/invuse.wav" ); + ncEngine::Precache_File( "progs.dat"); + ncEngine::Precache_File2( "gfx/pop.lmp" ); + ncEngine::Precache_File( "gfx.wad" ); + ncEngine::Precache_File( "default.cfg" ); +} diff --git a/data1/src/init.qc b/data1/src/init.qc index 4fb0df7..e408939 100644 --- a/data1/src/init.qc +++ b/data1/src/init.qc @@ -20,4 +20,4 @@ void Precaches(void) INIT_ITEM(H2Flight) INIT_ITEM(H2CubeOfForce) INIT_ITEM(H2Invincibility) -} \ No newline at end of file +} diff --git a/data1/src/progs.src b/data1/src/progs.src index 5597d73..982e452 100644 --- a/data1/src/progs.src +++ b/data1/src/progs.src @@ -24,4 +24,5 @@ items/torch.qc rules.qc init.qc -#endlist \ No newline at end of file +files.qc +#endlist diff --git a/data1/src/rules.qc b/data1/src/rules.qc new file mode 100644 index 0000000..692772a --- /dev/null +++ b/data1/src/rules.qc @@ -0,0 +1,16 @@ +class H2Rules:ncRules { + void H2Rules(void); + + virtual void PlayerFinishesJoining( ncPlayer ); +}; + +void H2Rules::H2Rules(void) { + +} + +void H2Rules::PlayerFinishesJoining( ncPlayer ourPlayer ) { + ourPlayer.hud.SetHealth(100); + + /* place them somewhere into the world */ + super::PlayerFinishesJoining( ourPlayer ); +} \ No newline at end of file diff --git a/id1/gfxsrc/palette.png b/id1/gfxsrc/palette.png new file mode 100644 index 0000000000000000000000000000000000000000..18dfc23af652814e637ab963728ed29bbd510930 GIT binary patch literal 1287 zcmZWnf0z_w9RFTFyw%7YBa;Rbqc+BQx7#vx6XV*urZe4bHOItYs;O3{Y}(6KgFS7W zOtm;{**GjNR;?VJS}YF5;^+`9uL;^_{-yY-uL~ypYQkkdA{HG`+e8V ztdoZi8#N37hSyB5n(bog$vtGCdyXq3RTl;YD{CtOcEpK4=M8rGXmk4PT7cDK0UFl= z{Nb7!4+E_50vw$SP_YSsUbbcTtSJBk4$hoWU+o5vB+0TY##oXhMNxEJw=B!HZO3tv z$z(d6&Staue7;a9^!4?T1fofTr3nv1VvZ7cTEesCdyp;|zg-p@8Q|fv032qY9iU@gdn4RVBxela|_Q8|_V2N3-3Pa(dblX(!bZ zO*Ds_oS+@mLlM=q6wQznP4FvN@O!Ypay-p&6h*u3Lns?=$yo7}5ld(hCtyVtBkWf# zQ89dy$_p~bi!6s}rqGeix3%U{$xOmYHQ5O})D$voQ?)eNP()So`F$RNXFV*@p)j2$Uxr_BtJ|zu`t>?!5Wtf`yBMH!Z%|f+b6D2`#&IdAMQ4 zZIP9$R@-p<9d||>?^+XEd-pv}_pZCof%O|U#_xaN!RCh^ek8H!q+#=;$t{mP-txqg zPoi*{2?|Qz6AAamTeB@}K^3%`1^#A(XvE#ee zN9O!KYTq=sXJZpEBDoVCs#};ukcOgz1|A1w|d*5 kN&gWn4J}x_Xx0A( 2.0f) ? (true) : (false); + bool isReverse = false; + + /* state switched */ + if (isWalking != wasWalking) { + if (isWalking) { + wasWalking = 1; + owner.frame = walk_firstFrame; + nextthink = time + walk_frameRate; + } else { + wasWalking = 0; + owner.frame = idle_firstFrame; + nextthink = time + idle_frameRate; + } + + return; + } + + /* at the end */ + if (isWalking) { + wasWalking = 1; + isReverse = ( walk_firstFrame > walk_lastFrame ) ? ( true ) : ( false ); + nextthink = time + walk_frameRate; + + if (owner.frame == walk_lastFrame) { + owner.frame = walk_firstFrame; + return; + } + } else { + wasWalking = 0; + isReverse = ( idle_firstFrame > idle_lastFrame ) ? ( true ) : ( false ); + nextthink = time + idle_frameRate; + + if (owner.frame == idle_lastFrame) { + owner.frame = idle_firstFrame; + return; + } + } + + /* is reverse */ + if (isReverse) { + owner.frame--; + } else { + owner.frame++; + } +} + +void ncAnimationManager::ProgressOverride( void ) +{ + bool isReverse = ( override_firstFrame > override_lastFrame ) ? ( true ) : ( false ); + + /* at the end, back to idle/walk cycle */ + if (owner.frame == override_lastFrame) { + nextthink = time + override_frameRate; + think = ProgressCycle; + wasWalking = 3; /* force state switch */ + return; + } + + /* is reverse */ + if (isReverse) { + owner.frame--; + } else { + owner.frame++; + } + + nextthink = time + health; +} + +void ncAnimationManager::ProgressWeaponIdle( void ) +{ + ncEntity carrier = ( ncEntity )owner; + float currentFrame = carrier.weaponframe; + + /* at the end */ + if (currentFrame == idle_lastFrame) { + carrier.hud.SetWeaponFrame(idle_firstFrame); + } else { + /* is reverse */ + if (skin) { + carrier.hud.SetWeaponFrame(currentFrame - 1); + } else { + carrier.hud.SetWeaponFrame(currentFrame + 1); + } + } + + nextthink = time + idle_frameRate; +} + +void ncAnimationManager::ProgressWeapon( void ) +{ + ncEntity carrier = ( ncEntity )owner; + float currentFrame = carrier.weaponframe; + + /* at the end */ + if (currentFrame == override_lastFrame) { + carrier.hud.SetWeaponFrame(idle_firstFrame); + nextthink = time + idle_frameRate; + think = ProgressWeaponIdle; + return; + } + + /* is reverse */ + if (skin) { + carrier.hud.SetWeaponFrame(currentFrame - 1); + } else { + carrier.hud.SetWeaponFrame(currentFrame + 1); + } + + nextthink = time + override_frameRate; +} + +float ncAnimationManager::FPSToRate(float framesPerSecond) +{ + if (framesPerSecond <= 0.0) { + return (0.1f); /* default is 10 fps. */ + } else { + return (1 / framesPerSecond); + } +} + +void ncAnimationManager::SetIdleCycle( float firstFrame, float lastFrame, float frameRate ) +{ + ncEntity targetEntity = ( ncEntity )owner; + idle_firstFrame = firstFrame; + idle_lastFrame = lastFrame; + idle_frameRate = FPSToRate( frameRate ); + + /* there may be a sequence active already, that's okay as its exit will go to cycle */ + if (!think) { + think = ncAnimationManager::ProgressCycle; + nextthink = ( time + idle_frameRate ); + targetEntity.SetFrame( firstFrame ); + } +} + +void ncAnimationManager::SetWalkCycle( float firstFrame, float lastFrame, float frameRate ) +{ + ncEntity targetEntity = ( ncEntity )owner; + walk_firstFrame = firstFrame; + walk_lastFrame = lastFrame; + walk_frameRate = FPSToRate( frameRate ); + + /* ditto, see SetIdleCycle */ + if (!think) { + think = ncAnimationManager::ProgressCycle; + nextthink = ( time + walk_frameRate ); + targetEntity.SetFrame( firstFrame ); + } +} + +void ncAnimationManager::PlayFrameSequence( float firstFrame, float lastFrame, float frameRate ) +{ + ncEntity targetEntity = ( ncEntity )owner; + override_firstFrame = firstFrame; + override_lastFrame = lastFrame; + override_frameRate = FPSToRate( frameRate ); + + nextthink = ( time + override_frameRate ); + think = ncAnimationManager::ProgressOverride; + targetEntity.SetFrame( firstFrame ); +} + +void ncAnimationManager::PlayWeaponFrameSequence( float firstFrame, float lastFrame, float frameRate ) +{ + ncEntity targetEntity = ( ncEntity )owner; + override_firstFrame = firstFrame; + override_lastFrame = lastFrame; + override_frameRate = FPSToRate( frameRate ); + + nextthink = ( time + override_frameRate ); + think = ncAnimationManager::ProgressWeapon; + targetEntity.hud.SetWeaponFrame( firstFrame ); +} diff --git a/src/system/ncAnimationManager.qh b/src/system/ncAnimationManager.qh new file mode 100644 index 0000000..79781a9 --- /dev/null +++ b/src/system/ncAnimationManager.qh @@ -0,0 +1,14 @@ +class ncAnimationManager { + + void ncAnimationManager( void ); + + nonvirtual void ProgressCycle( void ); + nonvirtual void ProgressOverride( void ); + nonvirtual void ProgressWeaponIdle( void ); + nonvirtual void ProgressWeapon( void ); + nonvirtual float FPSToRate( float ); + nonvirtual void SetWalkCycle( float firstFrame, float lastFrame, float frameRate ); + nonvirtual void SetIdleCycle( float firstFrame, float lastFrame, float frameRate ); + nonvirtual void PlayFrameSequence( float firstFrame, float lastFrame, float frameRate ); + nonvirtual void PlayWeaponFrameSequence( float firstFrame, float lastFrame, float frameRate ); +}; diff --git a/src/system/ncEntity.qc b/src/system/ncEntity.qc index ed6d206..ff5df09 100644 --- a/src/system/ncEntity.qc +++ b/src/system/ncEntity.qc @@ -372,6 +372,11 @@ dead_t ncEntity::GetDeadFlag( void ) { return (dead_t)deadflag; } +vector ncEntity::GetEyePos( void ) { + return origin + view_ofs; +} + + vector ncEntity::GetViewOffset( void ) { return view_ofs; } @@ -746,12 +751,53 @@ void ncEntity::PrimaryAttack( ncEntity carrier ) { currentweapon.PrimaryAttack(carrier); } -void ncEntity::SetNextPrimaryAttack(float attackDelay) +.ncAnimationManager animation_manager; +.ncAnimationManager weapon_animation_manager; + +void ncEntity::SetIdleCycle( float firstFrame, float lastFrame, float frameRate ) +{ + if (!animation_manager) { + animation_manager = spawn( ncAnimationManager ); + animation_manager.owner = this; + } + + animation_manager.SetIdleCycle( firstFrame, lastFrame, frameRate ); +} + +void ncEntity::SetWalkCycle( float firstFrame, float lastFrame, float frameRate ) +{ + if (!animation_manager) { + animation_manager = spawn( ncAnimationManager ); + animation_manager.owner = this; + } + + animation_manager.SetWalkCycle( firstFrame, lastFrame, frameRate ); +} + +void ncEntity::SetNextPrimaryAttack( float attackDelay ) { next_primary_attack = time + attackDelay; } +void ncEntity::PlayFrameSequence( float firstFrame, float lastFrame, float frameDelay ) +{ + if (!animation_manager) { + animation_manager = spawn( ncAnimationManager ); + animation_manager.owner = this; + } + animation_manager.PlayFrameSequence( firstFrame, lastFrame, frameDelay); +} + +void ncEntity::PlayWeaponFrameSequence( float firstFrame, float lastFrame, float frameDelay ) +{ + if (!weapon_animation_manager) { + weapon_animation_manager = spawn( ncAnimationManager ); + weapon_animation_manager.owner = this; + } + + weapon_animation_manager.PlayWeaponFrameSequence( firstFrame, lastFrame, frameDelay); +} /* wannabe thief */ void ncEntity::CalculateLightLevel( void ) @@ -770,4 +816,4 @@ void ncEntity::CalculateLightLevel( void ) light_level = lightValue; #endif -} \ No newline at end of file +} diff --git a/src/system/ncEntity.qh b/src/system/ncEntity.qh index ac5f10a..bb6a1ed 100644 --- a/src/system/ncEntity.qh +++ b/src/system/ncEntity.qh @@ -190,6 +190,8 @@ public: nonvirtual dead_t GetDeadFlag( void ); /** Returns the eye position of the entity relative to its origin. */ nonvirtual vector GetViewOffset( void ); + /** Returns the absolute eye position. */ + nonvirtual vector GetEyePos( void ); /** Returns either TRUE or FALSE if the entity has a given flag. */ nonvirtual bool HasFlag( flags_t ); /** Returns the colormap id of the entity. */ @@ -297,6 +299,12 @@ public: virtual void PrimaryAttack( ncEntity ); nonvirtual void SetNextPrimaryAttack( float ); + nonvirtual void PlayFrameSequence(float, float, float); + nonvirtual void PlayWeaponFrameSequence(float, float, float); + + nonvirtual void SetIdleCycle(float, float, float); + nonvirtual void SetWalkCycle(float, float, float); + /** Recalculate the light level of the entity, run automatically on players during PreThink. */ nonvirtual void CalculateLightLevel( void ); @@ -309,4 +317,4 @@ private: var bool g_world_initialized; .bool isIdEntity; -#define SPAWNDEFAULT(x,y) if (x == 0) { x = y; } \ No newline at end of file +#define SPAWNDEFAULT(x,y) if (x == 0) { x = y; } diff --git a/src/system/ncFX.qc b/src/system/ncFX.qc index 4d285e2..0694356 100644 --- a/src/system/ncFX.qc +++ b/src/system/ncFX.qc @@ -149,13 +149,13 @@ void ncFX::CastBlood( vector targetPos ) { #ifdef TARGET_QUAKEWORLD ncNet::WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY ); ncNet::WriteByte( FX_MSGTYPE, TE_BLOOD ); - ncNet::WriteByte( FX_MSGTYPE, 1 ); + ncNet::WriteByte( FX_MSGTYPE, 8 ); ncNet::WriteCoord( FX_MSGTYPE, targetPos[0] ); ncNet::WriteCoord( FX_MSGTYPE, targetPos[1] ); ncNet::WriteCoord( FX_MSGTYPE, targetPos[2] ); ncNet::MultiCast( targetPos, MULTICAST_PVS ); #else - Particle( targetPos, [0, 0, 0], 73, 1 ); + Particle( targetPos, [0, 0, 0], 73, 8 ); #endif } @@ -195,4 +195,4 @@ void ncFX::CastLightningBlood( ncEntity boltOwner, vector startPos, vector endPo #ifndef TARGET_QUAKEWORLD CastBlood(endPos); #endif -} \ No newline at end of file +} diff --git a/src/system/ncItem.qc b/src/system/ncItem.qc index 750f52c..7b02199 100644 --- a/src/system/ncItem.qc +++ b/src/system/ncItem.qc @@ -153,7 +153,7 @@ void ncItem::OnPickup(ncEntity carrier) { carrier.SPrint( PRINT_LOW, GetItemName() ); carrier.SPrint( PRINT_LOW, "!\n" ); - ncFX::CastScreenFlash( carrier ); + ncFX::CastScreenFlash( (ncPlayer)carrier ); if (nc_pckupsnd) Sound( CHAN_ITEM, nc_pckupsnd, 1.0f, ATTN_NORM ); @@ -167,6 +167,10 @@ void ncItem::OnInventoryUse( ncEntity carrier ) { carrier.SPrint(PRINT_LOW, " which is unimplemented\n" ); } +void ncItem::OnWorldImpact(vector endPos, vector planeNormal) { + +} + ncItem ncItem::ItemForID( float itemNum ) { ncEntity e; while ( ( e = ncEngine::NextEnt( e ) ) ) { @@ -184,4 +188,4 @@ void ncItem::_InternalPostPrecache( void ) { ncEngine::Precache_Sound( nc_pckupsnd ); if (nc_model) ncEngine::Precache_Model( nc_model ); -} \ No newline at end of file +} diff --git a/src/system/ncItem.qh b/src/system/ncItem.qh index 16443f3..a74cdc3 100644 --- a/src/system/ncItem.qh +++ b/src/system/ncItem.qh @@ -34,6 +34,8 @@ public: virtual void OnPickup(ncEntity); virtual void OnInventoryUse( ncEntity ); + virtual void OnWorldImpact(vector, vector); + nonvirtual ncItem ItemForID( float ); private: @@ -44,4 +46,4 @@ private: .float nc_wepnid; .string nc_model; .string nc_name; -.string nc_pckupsnd; \ No newline at end of file +.string nc_pckupsnd; diff --git a/src/system/ncMath.qc b/src/system/ncMath.qc index 4063500..4331d7d 100644 --- a/src/system/ncMath.qc +++ b/src/system/ncMath.qc @@ -3,6 +3,11 @@ void ncMath::MakeVectors( vector ang ) { return makevectors( ang ); } +vector ncMath::AnglesToForward( vector ang ) { + makevectors( ang ); + return v_forward; +} + float ncMath::Random( void ) { return random(); } @@ -170,4 +175,4 @@ float ncMath::Clamp( float min, float max, float x ) { } return x; -} \ No newline at end of file +} diff --git a/src/system/ncMath.qh b/src/system/ncMath.qh index b6e8b39..602e703 100644 --- a/src/system/ncMath.qh +++ b/src/system/ncMath.qh @@ -1,5 +1,6 @@ class ncMath { nonvirtual void MakeVectors( vector ang ); + nonvirtual vector AnglesToForward( vector ang ); nonvirtual float Random( void ); nonvirtual vector Normalize( vector v ); nonvirtual float VLen( vector v ); @@ -31,4 +32,4 @@ const float MATH_ACOS_CURVE_B = 0.872664625997165; /** If a is bigger than b, return b. */ #define min(a, b) ((a)>(b)?(b):(a)) /** If a is bigger than b, return a. */ -#define max(a, b) ((a)>(b)?(a):(b)) \ No newline at end of file +#define max(a, b) ((a)>(b)?(a):(b)) diff --git a/src/system/ncProjectile.qc b/src/system/ncProjectile.qc new file mode 100644 index 0000000..74dce35 --- /dev/null +++ b/src/system/ncProjectile.qc @@ -0,0 +1,19 @@ +void ncProjectile::ncProjectile( void ) +{ + +} + +bool ncProjectile::HitScan( ncEntity shootingEntity, vector spread2D, float shotCount, float damageValue, ncItem weaponItem ) +{ + vector startPos = shootingEntity.GetEyePos(); + vector endPos = startPos + ncMath::AnglesToForward( shootingEntity.v_angle ) * 4096.0f; + + ncEngine::TraceLine( startPos, endPos, false, shootingEntity ); + + if (trace_fraction >= 1.0f) { + return (false); + } + + weaponItem.OnWorldImpact(trace_endpos, trace_plane_normal); + return (true); +} diff --git a/src/system/ncProjectile.qh b/src/system/ncProjectile.qh new file mode 100644 index 0000000..b2a0b9a --- /dev/null +++ b/src/system/ncProjectile.qh @@ -0,0 +1,6 @@ +class ncProjectile:ncEntity +{ + void ncProjectile( void ); + + nonvirtual bool HitScan( ncEntity shootingEntity, vector spread2D, float shotCount, float damage, ncItem weaponItem ); +}; diff --git a/src/system/sources.src b/src/system/sources.src index 8243bf8..fdd53dd 100644 --- a/src/system/sources.src +++ b/src/system/sources.src @@ -11,5 +11,7 @@ ncCamera.qc ncMath.qc ncHud.qc ncNet.qc +ncAnimationManager.qc +ncProjectile.qc classes.qc #endlist